Skip to content

Commit e4e1bd4

Browse files
committed
Merge pull request #168
Fix compatibility with new MariaDB client and server versions
2 parents 9e9b5f9 + 9743bec commit e4e1bd4

File tree

5 files changed

+60
-12
lines changed

5 files changed

+60
-12
lines changed

Makefile.PL

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,13 @@ sub Configure {
757757
# libmysqld.a from MySQL 8.x is broken too
758758
$function .= "\n#if !defined(MARIADB_BASE_VERSION) && !defined(MARIADB_PACKAGE_VERSION)\nif (mysql_get_client_version() >= 80000) return 1;\n#endif\n";
759759
}
760-
$function .= 'return (mysql_get_client_version() == MYSQL_VERSION_ID) ? 0 : 1;';
760+
# MariaDB Connector/C 3.1.10+ has broken mysql_get_client_version() function, so use mariadb_get_infov(MARIADB_CLIENT_VERSION_ID) instead
761+
$function .= "size_t version;\n";
762+
$function .= "#if defined(MARIADB_PACKAGE_VERSION) && defined(MARIADB_PACKAGE_VERSION_ID) && MARIADB_PACKAGE_VERSION_ID >= 30000\n";
763+
$function .= "if (mariadb_get_infov((void *)0, MARIADB_CLIENT_VERSION_ID, &version) != 0)\n";
764+
$function .= "#endif\n";
765+
$function .= "version = mysql_get_client_version();\n";
766+
$function .= 'return (version == MYSQL_VERSION_ID) ? 0 : 1;';
761767
# libmysqld is built using g++ rather than gcc and sometimes
762768
# we have to use libstdc++ to resolve linking problems
763769
foreach my $add_ldflags (undef, '-lstdc++') {

dbdimp.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,25 @@ static char **fill_out_embedded_options(char *options,
627627
return options_list;
628628
}
629629

630+
#if MYSQL_VERSION_ID < 50001
631+
/* MySQL client prior to version 5.0.1 does not implement mysql_real_escape_string() for SERVER_STATUS_NO_BACKSLASH_ESCAPES */
632+
static unsigned long string_escape_quotes(char *to, const char *from, unsigned long len)
633+
{
634+
const char *to_start = to;
635+
const char *end = from + len;
636+
637+
while (from < end)
638+
{
639+
if (*from == '\'')
640+
*to++ = '\'';
641+
*to++ = *from++;
642+
}
643+
644+
*to = '\0';
645+
return to - to_start;
646+
}
647+
#endif
648+
630649
/*
631650
constructs an SQL statement previously prepared with
632651
actual values replacing placeholders
@@ -838,9 +857,8 @@ static char *parse_params(
838857
#if MYSQL_VERSION_ID < 50001
839858
if (sock->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
840859
{
841-
*ptr++ = 'X';
842860
*ptr++ = '\'';
843-
ptr += mysql_hex_string(ptr, ph->value, ph->len);
861+
ptr += string_escape_quotes(ptr, ph->value, ph->len);
844862
*ptr++ = '\'';
845863
}
846864
else
@@ -6411,9 +6429,8 @@ SV* mariadb_db_quote(SV *dbh, SV *str, SV *type)
64116429
#if MYSQL_VERSION_ID < 50001
64126430
if (imp_dbh->pmysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
64136431
{
6414-
*sptr++ = 'X';
64156432
*sptr++ = '\'';
6416-
sptr += mysql_hex_string(sptr, ptr, len);
6433+
sptr += string_escape_quotes(sptr, ptr, len);
64176434
*sptr++ = '\'';
64186435
}
64196436
else

dbdimp.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,32 @@ PERL_STATIC_INLINE UV SvUV_nomg(pTHX_ SV *sv)
322322
#define my_bool bool
323323
#endif
324324

325+
/*
326+
* MariaDB Connector/C 3.1.10 changed API of mysql_get_client_version()
327+
* function. Before that release it returned client version. With that release
328+
* it started returning Connector/C package version.
329+
*
330+
* So when compiling with MariaDB Connector/C client library, redefine
331+
* mysql_get_client_version() to always returns client version via function
332+
* mariadb_get_infov(MARIADB_CLIENT_VERSION_ID) call.
333+
*
334+
* Driver code expects for a long time that mysql_get_client_version() call
335+
* returns client version and not something different.
336+
*
337+
* Function mariadb_get_infov() is supported since MariaDB Connector/C 3.0+.
338+
*/
339+
#if defined(MARIADB_PACKAGE_VERSION) && defined(MARIADB_PACKAGE_VERSION_ID) && MARIADB_PACKAGE_VERSION_ID >= 30000
340+
PERL_STATIC_INLINE unsigned long mariadb_get_client_version(void)
341+
{
342+
/* MARIADB_CLIENT_VERSION_ID really expects size_t type, documentation is wrong and says unsigned int. */
343+
size_t version;
344+
if (mariadb_get_infov(NULL, MARIADB_CLIENT_VERSION_ID, &version) != 0)
345+
version = mysql_get_client_version(); /* On error fallback to mysql_get_client_version() */
346+
return version;
347+
}
348+
#define mysql_get_client_version() mariadb_get_client_version()
349+
#endif
350+
325351
/* MYSQL_SECURE_AUTH became a no-op from MySQL 5.7.5 and is removed from MySQL 8.0.3 */
326352
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID <= 50704
327353
#define HAVE_SECURE_AUTH

t/40server_prepare.t

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,16 +72,19 @@ ok($sth3->execute(1, 2), "insert t3");
7272

7373
is_deeply($dbh->selectall_arrayref('SELECT id, mydata FROM t3'), [[1, 2]]);
7474

75+
# MariaDB server since version 10.6.2 can prepare all statements except PREPARE, EXECUTE, and DEALLOCATE / DROP PREPARE. Previous MariaDB and MySQL versions cannot prepare USE statement.
76+
my $non_preparable_statement = ($dbh->{mariadb_serverversion} >= 100602) ? q(PREPARE stmt FROM "SELECT 1") : ("USE " . $dbh->quote_identifier($test_db));
77+
7578
$dbh->{mariadb_server_prepare_disable_fallback} = 1;
7679
my $error_handler_called = 0;
7780
$dbh->{HandleError} = sub { $error_handler_called = 1; die $_[0]; };
78-
eval { $dbh->prepare("USE " . $dbh->quote_identifier($test_db)) };
81+
eval { $dbh->prepare($non_preparable_statement); };
7982
$dbh->{HandleError} = undef;
80-
ok($error_handler_called, 'USE is not supported with mariadb_server_prepare_disable_fallback=1');
83+
ok($error_handler_called, "Non-preparable statement '$non_preparable_statement' is not supported with mariadb_server_prepare_disable_fallback=1");
8184

8285
$dbh->{mariadb_server_prepare_disable_fallback} = 0;
8386
my $sth4;
84-
ok($sth4 = $dbh->prepare("USE " . $dbh->quote_identifier($test_db)), 'USE is supported with mariadb_server_prepare_disable_fallback=0');
87+
ok($sth4 = $dbh->prepare($non_preparable_statement), "Non-preparable statement '$non_preparable_statement' is supported with mariadb_server_prepare_disable_fallback=0");
8588
ok($sth4->execute());
8689

8790
ok ($dbh->do(qq{DROP TABLE t3}), "cleaning up");

t/45bind_no_backslash_escapes.t

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ if ($dbh->{mariadb_serverversion} < 50001) {
1818
plan skip_all => "Servers < 5.0.1 do not support sql_mode NO_BACKSLASH_ESCAPES";
1919
}
2020

21-
if ($dbh->{mariadb_clientversion} < 50001) {
22-
$id2_quoted_no_backslash = q(X'737472696E675C737472696E6722737472696E6727737472696E67');
23-
}
24-
2521
plan tests => 20;
2622

2723
ok $dbh->do('CREATE TEMPORARY TABLE t(id VARCHAR(255), value TEXT)');

0 commit comments

Comments
 (0)