diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 91c68950..edeca516 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -253,9 +253,15 @@ jobs: matrix: ${{ fromJson(needs.build-matrix.outputs.matrix) }} steps: - name: Checkout git commit ${{ github.sha }} + if: github.event_name != 'pull_request' uses: actions/checkout@v3 with: ref: ${{ github.sha }} + - name: Checkout git commit ${{ github.event.pull_request.head.sha }} (fixup for pull request) + if: github.event_name == 'pull_request' + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} - name: Setup cpan sources cache uses: actions/cache@v3 with: diff --git a/Makefile.PL b/Makefile.PL index 2772e060..6a7a9f63 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -24,9 +24,9 @@ push @mysql_headers, 'mysql.h'; our $opt = { "help" => \&Usage, }; -my ($test_host, $test_port, $test_socket); +my ($test_host, $test_port, $test_socket, $test_authplugin); { -local ($::test_host, $::test_port, $::test_user, $::test_socket, $::test_password, $::test_db, $::test_mysql_config, $::test_cflags, $::test_libs); +local ($::test_host, $::test_port, $::test_user, $::test_socket, $::test_authplugin, $::test_password, $::test_db, $::test_mysql_config, $::test_cflags, $::test_libs); eval { require "./t/MariaDB.mtest" } and do { $opt->{'testuser'} = $::test_user; $opt->{'testpassword'} = $::test_password; @@ -37,6 +37,7 @@ $opt->{'libs'} = $::test_libs; $test_host = $::test_host; $test_port = $::test_port; $test_socket = $::test_socket; +$test_authplugin = $::test_authplugin; } } @@ -49,6 +50,7 @@ Getopt::Long::GetOptions( "testuser:s", "testpassword:s", "testsocket:s", + "testauthplugin:s", "cflags:s", "libs:s", "mysql_config:s", @@ -104,7 +106,7 @@ MSG } } -for my $key (qw(testdb testhost testuser testpassword testsocket testport cflags libs)) +for my $key (qw(testdb testhost testuser testpassword testsocket testauthplugin testport cflags libs)) { Configure($opt, $source, $key); } @@ -136,7 +138,7 @@ if ( $opt->{testport} && !$opt->{testhost} ) { $source->{testhost} = 'guessed'; } -foreach (qw(testhost testport testsocket)) { +foreach (qw(testhost testport testsocket testauthplugin)) { next if defined $opt->{$_}; $opt->{$_} = ''; $source->{$_} = 'default'; @@ -419,12 +421,14 @@ my $fileName = File::Spec->catfile("t", "MariaDB.mtest"); "\$::test_port = \$opt->{'testport'};\n" . "\$::test_user = \$opt->{'testuser'};\n" . "\$::test_socket = \$opt->{'testsocket'};\n" . + "\$::test_authplugin = \$opt->{'testauthplugin'};\n" . "\$::test_password = \$opt->{'testpassword'};\n" . "\$::test_db = \$opt->{'testdb'};\n" . "\$::test_dsn = \"DBI:MariaDB:\$::test_db\";\n" . "\$::test_dsn .= \":\$::test_host\" if \$::test_host;\n" . "\$::test_dsn .= \":\$::test_port\" if \$::test_port;\n". "\$::test_dsn .= \";mariadb_socket=\$::test_socket\" if \$::test_socket;\n" . + "\$::test_dsn .= \";mariadb_auth_plugin=\$::test_authplugin\" if \$::test_authplugin;\n" . "\$::test_dsn .= \";mariadb_connect_timeout=120;mariadb_read_timeout=120;mariadb_write_timeout=120\";\n" . "\$::test_mysql_config = \$opt->{'mysql_config'} if \$source->{'mysql_config'} eq 'User\\'s choice';\n" . "\$::test_cflags = \$opt->{'cflags'} if \$source->{'cflags'} eq 'User\\'s choice';\n" . @@ -654,6 +658,9 @@ Possible options are: the database server; by default unix socket is chosen by mariadb/mysqlclient library; takes effect only when --testhost is set to "localhost" + --testauthplugin= Use auth plugin when doing user authentication + handshake with server; for older server versions it is + needed to pass "mysql_native_password" --mariadb_config Synonym for --mysql_config, override it --mysql_config= Specify for mariadb_config or mysql_config script --help Print this message and exit @@ -885,7 +892,7 @@ section "Linker flags" or type perl Makefile.PL --help MSG } - elsif ($param eq "testhost" || $param eq "testport" || $param eq "testsocket") { + elsif ($param eq "testhost" || $param eq "testport" || $param eq "testsocket" || $param eq "testauthplugin") { # known parameter, but do nothing } else { diff --git a/dbdimp.c b/dbdimp.c index 3b53c399..8c071e3f 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -1855,6 +1855,29 @@ static bool mariadb_dr_connect( } } + (void)hv_stores(processed, "mariadb_auth_plugin", &PL_sv_yes); + if ((svp = hv_fetchs(hv, "mariadb_auth_plugin", FALSE)) && *svp) + { + int error = 1; + STRLEN len; + char *auth_plugin = SvPVutf8(*svp, len); + if (strlen(auth_plugin) != len) + { + error_nul_character(dbh, "mariadb_auth_plugin"); + mariadb_db_disconnect(dbh, imp_dbh); + return FALSE; + } +#if MYSQL_VERSION_ID >= 50507 && MYSQL_VERSION_ID != 60000 + error = mysql_options(sock, MYSQL_DEFAULT_AUTH, auth_plugin); +#endif + if (error) + { + mariadb_dr_do_error(dbh, CR_CONNECTION_ERROR, "Connection error: mariadb_auth_plugin is not supported", "HY000"); + mariadb_db_disconnect(dbh, imp_dbh); + return FALSE; + } + } + (void)hv_stores(processed, "mariadb_read_default_file", &PL_sv_yes); if ((svp = hv_fetchs(hv, "mariadb_read_default_file", FALSE)) && *svp && SvTRUE(*svp)) { @@ -1924,13 +1947,8 @@ static bool mariadb_dr_connect( } (void)hv_stores(processed, "mariadb_client_found_rows", &PL_sv_yes); - if ((svp = hv_fetchs(hv, "mariadb_client_found_rows", FALSE)) && *svp) - { - if (SvTRUE(*svp)) - client_flag |= CLIENT_FOUND_ROWS; - else - client_flag &= ~CLIENT_FOUND_ROWS; - } + if ((svp = hv_fetchs(hv, "mariadb_client_found_rows", FALSE)) && *svp && !SvTRUE(*svp)) + client_flag &= ~CLIENT_FOUND_ROWS; (void)hv_stores(processed, "mariadb_auto_reconnect", &PL_sv_yes); if ((svp = hv_fetchs(hv, "mariadb_auto_reconnect", FALSE)) && *svp) @@ -1999,13 +2017,11 @@ static bool mariadb_dr_connect( } (void)hv_stores(processed, "mariadb_multi_statements", &PL_sv_yes); - if ((svp = hv_fetchs(hv, "mariadb_multi_statements", FALSE)) && *svp) + if ((svp = hv_fetchs(hv, "mariadb_multi_statements", FALSE)) && *svp && SvTRUE(*svp)) { - if (SvTRUE(*svp)) - client_flag |= CLIENT_MULTI_STATEMENTS; - else - client_flag &= ~CLIENT_MULTI_STATEMENTS; - } + imp_dbh->use_multi_statements = TRUE; + client_flag |= CLIENT_MULTI_STATEMENTS; + } (void)hv_stores(processed, "mariadb_server_prepare", &PL_sv_yes); if ((svp = hv_fetchs(hv, "mariadb_server_prepare", FALSE)) && *svp) @@ -3471,6 +3487,17 @@ mariadb_db_STORE_attrib( return 0; #endif } + else if (memEQs(key, kl, "mariadb_multi_statements")) + { + if (!imp_dbh->connected) /* When not connected, it is handled in mariadb_dr_connect() */ + return 0; + if (mysql_set_server_option(imp_dbh->pmysql, bool_value ? MYSQL_OPTION_MULTI_STATEMENTS_ON : MYSQL_OPTION_MULTI_STATEMENTS_OFF) != 0) + { + mariadb_dr_do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql)); + return 0; + } + imp_dbh->use_multi_statements = bool_value; + } else { if (imp_dbh->connected) /* Ignore unknown attributes passed by DBI->connect, they are handled in mariadb_dr_connect() */ @@ -3732,6 +3759,8 @@ SV* mariadb_db_FETCH_attrib(SV *dbh, imp_dbh_t *imp_dbh, SV *keysv) result = imp_dbh->pmysql ? sv_2mortal(newSVuv(mysql_warning_count(imp_dbh->pmysql))) : &PL_sv_undef; else if (memEQs(key, kl, "mariadb_use_result")) result = boolSV(imp_dbh->use_mysql_use_result); + else if (memEQs(key, kl, "mariadb_multi_statements")) + result = boolSV(imp_dbh->use_multi_statements); else { error_unknown_attribute(dbh, key); diff --git a/dbdimp.h b/dbdimp.h index f5b15bb3..e66f7b42 100644 --- a/dbdimp.h +++ b/dbdimp.h @@ -537,6 +537,7 @@ struct imp_dbh_st { */ bool use_server_side_prepare; bool disable_fallback_for_server_prepare; + bool use_multi_statements; void* async_query_in_flight; my_ulonglong insertid; struct { diff --git a/lib/DBD/MariaDB.pod b/lib/DBD/MariaDB.pod index 9434e929..c5cf8d04 100644 --- a/lib/DBD/MariaDB.pod +++ b/lib/DBD/MariaDB.pod @@ -133,6 +133,10 @@ This is similar to the behavior of the C or C command line client. Also, C