From b1f4834c9f5acdd6aa1ffd4088a53ac3a1b6501b Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Mon, 28 Nov 2022 16:26:29 +0100 Subject: [PATCH 1/2] Don't call mysql_close() when InactiveDestroy is set Without this change, the END block of DBI would call disconnect_all() and that would call mysql_close() on all open connections. This is bad because forks would destroy their parent's context on exit as shown in the example for AutoInactiveDestroy in the DBI docs. This example fails with "DBD::MariaDB::db do failed: Lost connection to MySQL server during query" without this fix: $dbh->{'AutoInactiveDestroy'} = 1; if (my $pid = fork()) { waitpid($pid, 0); $dbh->do('SELECT 1'); } else { exit(0); } --- dbdimp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/dbdimp.c b/dbdimp.c index 0f440892..f86364a5 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -2956,11 +2956,12 @@ mariadb_db_rollback(SV* dbh, imp_dbh_t* imp_dbh) { return 1; } -static void mariadb_dr_close_mysql(pTHX_ imp_drh_t *imp_drh, MYSQL *pmysql) +static void mariadb_dr_close_mysql(pTHX_ imp_drh_t *imp_drh, MYSQL *pmysql, bool actually_close) { if (pmysql) { - mysql_close(pmysql); + if (actually_close) + mysql_close(pmysql); imp_drh->instances--; } if (imp_drh->instances == 0) @@ -3021,7 +3022,7 @@ static void mariadb_db_close_mysql(pTHX_ imp_drh_t *imp_drh, imp_dbh_t *imp_dbh) if (imp_dbh->pmysql) { - mariadb_dr_close_mysql(aTHX_ imp_drh, imp_dbh->pmysql); + mariadb_dr_close_mysql(aTHX_ imp_drh, imp_dbh->pmysql, DBIc_ACTIVE(imp_dbh)); imp_dbh->pmysql = NULL; svp = hv_fetchs((HV*)DBIc_MY_H(imp_dbh), "ChildHandles", FALSE); if (svp && *svp) @@ -3059,6 +3060,8 @@ static void mariadb_db_close_mysql(pTHX_ imp_drh_t *imp_drh, imp_dbh_t *imp_dbh) } } } + + DBIc_ACTIVE_off(imp_dbh); } /* @@ -3112,7 +3115,7 @@ int mariadb_dr_discon_all (SV *drh, imp_drh_t *imp_drh) { while ((entry = imp_drh->taken_pmysqls)) { - mariadb_dr_close_mysql(aTHX_ imp_drh, (MYSQL *)entry->data); + mariadb_dr_close_mysql(aTHX_ imp_drh, (MYSQL *)entry->data, TRUE); mariadb_list_remove(imp_drh->taken_pmysqls, entry); } From ff8dc1d5de3353902aebcfe072dfda4f91feae3b Mon Sep 17 00:00:00 2001 From: Robert Vollmer Date: Tue, 29 Nov 2022 16:11:48 +0100 Subject: [PATCH 2/2] Don't call $sth->finish() when InactiveDestroy is set DBI already calls dbd_st_finish() before dbd_st_destroy() if the handle is active. For inactive handles no finish() should be necessary. With InactiveDestroy set, the handle is always considered inactive. Skipping finish() could lead to "out of sync" situations and inconsistencies, however, the attribute is explicitly meant to skip all DB-related destroy actions, especially for the case when the connection will be closed anyway. The only thing which is not cleaned up by finish() is the last result. This commit fixes the following example: $dbh->{'AutoInactiveDestroy'} = 1; my $sth = $dbh->prepare('SELECT id FROM bcwb_maint_types; SELECT SLEEP(1); SELECT text FROM bcwb_maint_types;'); $sth->execute(); my $i = 0; do { printf "Result %d\n", ++$i; while (my $id = $sth->fetchrow_array()) { print $id, "\n"; } print "---\n"; if ($i == 1) { if (my $pid = fork()) { waitpid($pid, 0); } else { exit(0); } } } while ($sth->more_results); --- dbdimp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/dbdimp.c b/dbdimp.c index f86364a5..41b20a39 100644 --- a/dbdimp.c +++ b/dbdimp.c @@ -5441,13 +5441,8 @@ void mariadb_st_destroy(SV *sth, imp_sth_t *imp_sth) { int num_params; int num_fields; - if (!PL_dirty) - { - /* During global destruction, DBI objects are destroyed in random order - * and therefore imp_dbh may be already freed. So do not access it. */ - mariadb_st_finish(sth, imp_sth); - mariadb_st_free_result_sets(sth, imp_sth, TRUE); - } + if (imp_sth->result) + mysql_free_result(imp_sth->result); DBIc_ACTIVE_off(imp_sth);