summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali <pali@cpan.org>2022-04-22 00:38:19 +0200
committerPali <pali@cpan.org>2022-04-22 00:38:19 +0200
commite4e1bd48ab33fc9b5eab99ac66debbde69be8338 (patch)
treec0c75e355ad911f7dc85037490eeb33def134174
parent9e9b5f9abf50080a17fd406425ae2f7bf35c4461 (diff)
parent9743bec52db5e8f1beb2b31e4a55d6ea1a4edcdd (diff)
Merge pull request https://github.com/perl5-dbi/DBD-MariaDB/pull/168
Fix compatibility with new MariaDB client and server versions
-rw-r--r--Makefile.PL8
-rw-r--r--dbdimp.c25
-rw-r--r--dbdimp.h26
-rw-r--r--t/40server_prepare.t9
-rw-r--r--t/45bind_no_backslash_escapes.t4
5 files changed, 60 insertions, 12 deletions
diff --git a/Makefile.PL b/Makefile.PL
index b9b046c..f28a8df 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -757,7 +757,13 @@ sub Configure {
# libmysqld.a from MySQL 8.x is broken too
$function .= "\n#if !defined(MARIADB_BASE_VERSION) && !defined(MARIADB_PACKAGE_VERSION)\nif (mysql_get_client_version() >= 80000) return 1;\n#endif\n";
}
- $function .= 'return (mysql_get_client_version() == MYSQL_VERSION_ID) ? 0 : 1;';
+ # MariaDB Connector/C 3.1.10+ has broken mysql_get_client_version() function, so use mariadb_get_infov(MARIADB_CLIENT_VERSION_ID) instead
+ $function .= "size_t version;\n";
+ $function .= "#if defined(MARIADB_PACKAGE_VERSION) && defined(MARIADB_PACKAGE_VERSION_ID) && MARIADB_PACKAGE_VERSION_ID >= 30000\n";
+ $function .= "if (mariadb_get_infov((void *)0, MARIADB_CLIENT_VERSION_ID, &version) != 0)\n";
+ $function .= "#endif\n";
+ $function .= "version = mysql_get_client_version();\n";
+ $function .= 'return (version == MYSQL_VERSION_ID) ? 0 : 1;';
# libmysqld is built using g++ rather than gcc and sometimes
# we have to use libstdc++ to resolve linking problems
foreach my $add_ldflags (undef, '-lstdc++') {
diff --git a/dbdimp.c b/dbdimp.c
index 73bd3f3..433a1c0 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -627,6 +627,25 @@ static char **fill_out_embedded_options(char *options,
return options_list;
}
+#if MYSQL_VERSION_ID < 50001
+/* MySQL client prior to version 5.0.1 does not implement mysql_real_escape_string() for SERVER_STATUS_NO_BACKSLASH_ESCAPES */
+static unsigned long string_escape_quotes(char *to, const char *from, unsigned long len)
+{
+ const char *to_start = to;
+ const char *end = from + len;
+
+ while (from < end)
+ {
+ if (*from == '\'')
+ *to++ = '\'';
+ *to++ = *from++;
+ }
+
+ *to = '\0';
+ return to - to_start;
+}
+#endif
+
/*
constructs an SQL statement previously prepared with
actual values replacing placeholders
@@ -838,9 +857,8 @@ static char *parse_params(
#if MYSQL_VERSION_ID < 50001
if (sock->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
{
- *ptr++ = 'X';
*ptr++ = '\'';
- ptr += mysql_hex_string(ptr, ph->value, ph->len);
+ ptr += string_escape_quotes(ptr, ph->value, ph->len);
*ptr++ = '\'';
}
else
@@ -6411,9 +6429,8 @@ SV* mariadb_db_quote(SV *dbh, SV *str, SV *type)
#if MYSQL_VERSION_ID < 50001
if (imp_dbh->pmysql->server_status & SERVER_STATUS_NO_BACKSLASH_ESCAPES)
{
- *sptr++ = 'X';
*sptr++ = '\'';
- sptr += mysql_hex_string(sptr, ptr, len);
+ sptr += string_escape_quotes(sptr, ptr, len);
*sptr++ = '\'';
}
else
diff --git a/dbdimp.h b/dbdimp.h
index 41cc72a..aee608f 100644
--- a/dbdimp.h
+++ b/dbdimp.h
@@ -322,6 +322,32 @@ PERL_STATIC_INLINE UV SvUV_nomg(pTHX_ SV *sv)
#define my_bool bool
#endif
+/*
+ * MariaDB Connector/C 3.1.10 changed API of mysql_get_client_version()
+ * function. Before that release it returned client version. With that release
+ * it started returning Connector/C package version.
+ *
+ * So when compiling with MariaDB Connector/C client library, redefine
+ * mysql_get_client_version() to always returns client version via function
+ * mariadb_get_infov(MARIADB_CLIENT_VERSION_ID) call.
+ *
+ * Driver code expects for a long time that mysql_get_client_version() call
+ * returns client version and not something different.
+ *
+ * Function mariadb_get_infov() is supported since MariaDB Connector/C 3.0+.
+ */
+#if defined(MARIADB_PACKAGE_VERSION) && defined(MARIADB_PACKAGE_VERSION_ID) && MARIADB_PACKAGE_VERSION_ID >= 30000
+PERL_STATIC_INLINE unsigned long mariadb_get_client_version(void)
+{
+ /* MARIADB_CLIENT_VERSION_ID really expects size_t type, documentation is wrong and says unsigned int. */
+ size_t version;
+ if (mariadb_get_infov(NULL, MARIADB_CLIENT_VERSION_ID, &version) != 0)
+ version = mysql_get_client_version(); /* On error fallback to mysql_get_client_version() */
+ return version;
+}
+#define mysql_get_client_version() mariadb_get_client_version()
+#endif
+
/* MYSQL_SECURE_AUTH became a no-op from MySQL 5.7.5 and is removed from MySQL 8.0.3 */
#if defined(MARIADB_BASE_VERSION) || MYSQL_VERSION_ID <= 50704
#define HAVE_SECURE_AUTH
diff --git a/t/40server_prepare.t b/t/40server_prepare.t
index 6eb2cf1..29aa6fd 100644
--- a/t/40server_prepare.t
+++ b/t/40server_prepare.t
@@ -72,16 +72,19 @@ ok($sth3->execute(1, 2), "insert t3");
is_deeply($dbh->selectall_arrayref('SELECT id, mydata FROM t3'), [[1, 2]]);
+# 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.
+my $non_preparable_statement = ($dbh->{mariadb_serverversion} >= 100602) ? q(PREPARE stmt FROM "SELECT 1") : ("USE " . $dbh->quote_identifier($test_db));
+
$dbh->{mariadb_server_prepare_disable_fallback} = 1;
my $error_handler_called = 0;
$dbh->{HandleError} = sub { $error_handler_called = 1; die $_[0]; };
-eval { $dbh->prepare("USE " . $dbh->quote_identifier($test_db)) };
+eval { $dbh->prepare($non_preparable_statement); };
$dbh->{HandleError} = undef;
-ok($error_handler_called, 'USE is not supported with mariadb_server_prepare_disable_fallback=1');
+ok($error_handler_called, "Non-preparable statement '$non_preparable_statement' is not supported with mariadb_server_prepare_disable_fallback=1");
$dbh->{mariadb_server_prepare_disable_fallback} = 0;
my $sth4;
-ok($sth4 = $dbh->prepare("USE " . $dbh->quote_identifier($test_db)), 'USE is supported with mariadb_server_prepare_disable_fallback=0');
+ok($sth4 = $dbh->prepare($non_preparable_statement), "Non-preparable statement '$non_preparable_statement' is supported with mariadb_server_prepare_disable_fallback=0");
ok($sth4->execute());
ok ($dbh->do(qq{DROP TABLE t3}), "cleaning up");
diff --git a/t/45bind_no_backslash_escapes.t b/t/45bind_no_backslash_escapes.t
index 13dce12..eaf011b 100644
--- a/t/45bind_no_backslash_escapes.t
+++ b/t/45bind_no_backslash_escapes.t
@@ -18,10 +18,6 @@ if ($dbh->{mariadb_serverversion} < 50001) {
plan skip_all => "Servers < 5.0.1 do not support sql_mode NO_BACKSLASH_ESCAPES";
}
-if ($dbh->{mariadb_clientversion} < 50001) {
- $id2_quoted_no_backslash = q(X'737472696E675C737472696E6722737472696E6727737472696E67');
-}
-
plan tests => 20;
ok $dbh->do('CREATE TEMPORARY TABLE t(id VARCHAR(255), value TEXT)');