Skip to content

Commit 4f47a06

Browse files
committed
Set $sth->{Active} to off after fetching all data
This is requirement by DBI API. Without it DBI throws following warning when $sth->finish() method is not called before issuing $dbh->disconnect(): DBI::db=HASH(...)->disconnect invalidates 1 active statement handle (either destroy statement handles or call finish on them before disconnecting) It happens also in the case when all data were fetched by some $sth->fetch* method call. DBI documentation about $sth->finish() method says: When all the data has been fetched from a SELECT statement, the driver will automatically call finish for you. So you should not call it explicitly except when you know that you've not fetched all the data from a statement handle and the handle won't be destroyed soon. With this change, $sth->{Active} attribute cannot be (mis)used for other DBD::MariaDB internal things. So error detection was modified to not depend on it. Method $sth->finish() was changed to not free last result set. So it is still possible to retrieve statement attributes even after calling method $sth->finish(). Methods $sth->fetch* were modified to return undef without throwing errors when trying to read data after all data were retrieved. Member fetch_done was removed as it is not used anymore. Member currow was changed from int to my_ulonglong as it contains current number of row up to the value from row_num which is my_ulonglong. Fixes: jhthorsen/mojo-mysql#47 (comment) Fixes: ea86854 See: https://metacpan.org/pod/DBI#Active See: https://metacpan.org/pod/DBI#finish
1 parent 0a9e6b4 commit 4f47a06

File tree

2 files changed

+74
-70
lines changed

2 files changed

+74
-70
lines changed

dbdimp.c

Lines changed: 73 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3712,7 +3712,7 @@ AV *mariadb_db_data_sources(SV *dbh, imp_dbh_t *imp_dbh, SV *attr)
37123712
return av;
37133713
}
37143714

3715-
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth);
3715+
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth, bool free_last);
37163716

37173717
/*
37183718
**************************************************************************
@@ -3774,7 +3774,6 @@ mariadb_st_prepare_sv(
37743774
imp_sth->use_server_side_prepare = imp_dbh->use_server_side_prepare;
37753775
imp_sth->disable_fallback_for_server_prepare = imp_dbh->disable_fallback_for_server_prepare;
37763776

3777-
imp_sth->fetch_done = FALSE;
37783777
imp_sth->done_desc = FALSE;
37793778
imp_sth->result = NULL;
37803779
imp_sth->currow = 0;
@@ -3845,7 +3844,7 @@ mariadb_st_prepare_sv(
38453844
Clean-up previous result set(s) for sth to prevent
38463845
'Commands out of sync' error
38473846
*/
3848-
if (!mariadb_st_free_result_sets(sth, imp_sth))
3847+
if (!mariadb_st_free_result_sets(sth, imp_sth, TRUE))
38493848
return 0;
38503849

38513850
if (imp_sth->use_server_side_prepare)
@@ -3989,11 +3988,12 @@ mariadb_st_prepare_sv(
39893988
*
39903989
* Inputs: sth - Statement handle
39913990
* imp_sth - driver's private statement handle
3991+
* free_last - free also the last result set
39923992
*
39933993
* Returns: TRUE ok
39943994
* FALSE error; mariadb_dr_do_error will be called
39953995
*************************************************************************/
3996-
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth)
3996+
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth, bool free_last)
39973997
{
39983998
dTHX;
39993999
D_imp_dbh_from_sth;
@@ -4031,7 +4031,7 @@ static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth)
40314031
imp_dbh->insertid = imp_sth->insertid = mysql_insert_id(imp_dbh->pmysql);
40324032
}
40334033
}
4034-
if (imp_sth->result)
4034+
if (imp_sth->result && (mysql_more_results(imp_dbh->pmysql) || free_last))
40354035
{
40364036
mysql_free_result(imp_sth->result);
40374037
imp_sth->result=NULL;
@@ -4102,6 +4102,8 @@ bool mariadb_st_more_results(SV* sth, imp_sth_t* imp_sth)
41024102
}
41034103
imp_dbh->async_query_in_flight = NULL;
41044104

4105+
DBIc_ACTIVE_off(imp_sth);
4106+
41054107
if (!imp_dbh->pmysql && !mariadb_db_reconnect(sth, NULL))
41064108
{
41074109
mariadb_dr_do_error(sth, CR_SERVER_GONE_ERROR, "MySQL server has gone away", "HY000");
@@ -4163,9 +4165,6 @@ bool mariadb_st_more_results(SV* sth, imp_sth_t* imp_sth)
41634165
(void)hv_deletes((HV*)SvRV(sth), "mariadb_type_name", G_DISCARD);
41644166
(void)hv_deletes((HV*)SvRV(sth), "mariadb_warning_count", G_DISCARD);
41654167

4166-
if (DBIc_ACTIVE(imp_sth))
4167-
DBIc_ACTIVE_off(imp_sth);
4168-
41694168
next_result_return_code= mysql_next_result(imp_dbh->pmysql);
41704169

41714170
imp_sth->warning_count = mysql_warning_count(imp_dbh->pmysql);
@@ -4210,6 +4209,9 @@ bool mariadb_st_more_results(SV* sth, imp_sth_t* imp_sth)
42104209
imp_sth->row_num = mysql_affected_rows(imp_dbh->pmysql);
42114210

42124211
imp_dbh->insertid = imp_sth->insertid = mysql_insert_id(imp_dbh->pmysql);
4212+
4213+
if (mysql_more_results(imp_dbh->pmysql))
4214+
DBIc_ACTIVE_on(imp_sth);
42134215
}
42144216
else
42154217
{
@@ -4221,7 +4223,8 @@ bool mariadb_st_more_results(SV* sth, imp_sth_t* imp_sth)
42214223
sv_2mortal(newSVuv(mysql_num_fields(imp_sth->result)))
42224224
);
42234225

4224-
DBIc_ACTIVE_on(imp_sth);
4226+
if (imp_sth->row_num)
4227+
DBIc_ACTIVE_on(imp_sth);
42254228
}
42264229

42274230
if (imp_sth->is_async && mysql_more_results(imp_dbh->pmysql))
@@ -4619,7 +4622,7 @@ IV mariadb_st_execute_iv(SV* sth, imp_sth_t* imp_sth)
46194622
Clean-up previous result set(s) for sth to prevent
46204623
'Commands out of sync' error
46214624
*/
4622-
if (!mariadb_st_free_result_sets(sth, imp_sth))
4625+
if (!mariadb_st_free_result_sets(sth, imp_sth, TRUE))
46234626
return -2;
46244627

46254628
if (use_server_side_prepare)
@@ -4694,10 +4697,10 @@ IV mariadb_st_execute_iv(SV* sth, imp_sth_t* imp_sth)
46944697
/** Store the result in the current statement handle */
46954698
num_fields = mysql_num_fields(imp_sth->result);
46964699
DBIc_NUM_FIELDS(imp_sth) = (num_fields <= INT_MAX) ? num_fields : INT_MAX;
4697-
DBIc_ACTIVE_on(imp_sth);
4700+
if (imp_sth->row_num)
4701+
DBIc_ACTIVE_on(imp_sth);
46984702
if (!use_server_side_prepare)
46994703
imp_sth->done_desc = FALSE;
4700-
imp_sth->fetch_done = FALSE;
47014704
}
47024705
}
47034706

@@ -4740,6 +4743,9 @@ static int mariadb_st_describe(SV* sth, imp_sth_t* imp_sth)
47404743
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
47414744
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t--> mariadb_st_describe\n");
47424745

4746+
if (imp_sth->done_desc)
4747+
return 1;
4748+
47434749
if (imp_sth->use_server_side_prepare)
47444750
{
47454751
int i;
@@ -4752,9 +4758,6 @@ static int mariadb_st_describe(SV* sth, imp_sth_t* imp_sth)
47524758
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmariadb_st_describe() num_fields %d\n",
47534759
num_fields);
47544760

4755-
if (imp_sth->done_desc)
4756-
return 1;
4757-
47584761
if (num_fields <= 0 || !imp_sth->result)
47594762
{
47604763
/* no metadata */
@@ -4916,30 +4919,22 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
49164919
return Nullav;
49174920
}
49184921

4919-
if(imp_dbh->async_query_in_flight) {
4920-
if (mariadb_db_async_result(sth, &imp_sth->result) == (my_ulonglong)-1)
4921-
return Nullav;
4922+
if (imp_dbh->async_query_in_flight)
4923+
{
4924+
if (!DBIc_ACTIVE(imp_sth))
4925+
return Nullav;
4926+
if (mariadb_db_async_result(sth, &imp_sth->result) == (my_ulonglong)-1)
4927+
return Nullav;
49224928
}
4923-
4924-
if (imp_sth->use_server_side_prepare)
4929+
else
49254930
{
4926-
if (!DBIc_ACTIVE(imp_sth) )
4931+
if (!imp_sth->result)
49274932
{
4928-
mariadb_dr_do_error(sth, CR_UNKNOWN_ERROR, "no statement executing", "HY000");
4933+
mariadb_dr_do_error(sth, CR_UNKNOWN_ERROR, "fetch() without execute()", "HY000");
49294934
return Nullav;
49304935
}
4931-
4932-
if (imp_sth->fetch_done)
4933-
{
4934-
mariadb_dr_do_error(sth, CR_UNKNOWN_ERROR, "fetch() but fetch already done", "HY000");
4936+
if (!DBIc_ACTIVE(imp_sth))
49354937
return Nullav;
4936-
}
4937-
4938-
if (!imp_sth->done_desc)
4939-
{
4940-
if (!mariadb_st_describe(sth, imp_sth))
4941-
return Nullav;
4942-
}
49434938
}
49444939

49454940
ChopBlanks = DBIc_is(imp_sth, DBIcf_ChopBlanks) ? TRUE : FALSE;
@@ -4949,19 +4944,16 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
49494944
"\t\tmariadb_st_fetch for %p, chopblanks %d\n",
49504945
sth, ChopBlanks ? 1 : 0);
49514946

4952-
if (!imp_sth->result)
4953-
{
4954-
mariadb_dr_do_error(sth, CR_UNKNOWN_ERROR, "fetch() without execute()", "HY000");
4955-
return Nullav;
4956-
}
4957-
49584947
/* fix from 2.9008 */
49594948
imp_dbh->pmysql->net.last_errno = 0;
49604949

49614950
if (imp_sth->use_server_side_prepare)
49624951
{
4952+
if (!mariadb_st_describe(sth, imp_sth))
4953+
return Nullav;
4954+
49634955
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
4964-
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmariadb_st_fetch calling mysql_fetch\n");
4956+
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmariadb_st_fetch calling mysql_stmt_fetch\n");
49654957

49664958
if ((rc= mysql_stmt_fetch(imp_sth->stmt)))
49674959
{
@@ -4975,7 +4967,6 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
49754967

49764968
if (rc == MYSQL_NO_DATA)
49774969
{
4978-
imp_sth->fetch_done = TRUE;
49794970
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
49804971
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t\tmariadb_st_fetch no data\n");
49814972
}
@@ -4986,12 +4977,17 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
49864977
mysql_stmt_sqlstate(imp_sth->stmt));
49874978
}
49884979

4980+
DBIc_ACTIVE_off(imp_sth);
4981+
49894982
return Nullav;
49904983
}
49914984

49924985
process:
49934986
imp_sth->currow++;
49944987

4988+
if (imp_sth->currow >= imp_sth->row_num)
4989+
DBIc_ACTIVE_off(imp_sth);
4990+
49954991
av= DBIc_DBISTATE(imp_sth)->get_fbav(imp_sth);
49964992
num_fields=mysql_stmt_field_count(imp_sth->stmt);
49974993
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
@@ -5245,8 +5241,8 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
52455241
SVfARG(sv_2mortal(my_ulonglong2sv(mysql_num_rows(imp_sth->result)))));
52465242
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmysql_affected_rows=%" SVf "\n",
52475243
SVfARG(sv_2mortal(my_ulonglong2sv(mysql_affected_rows(imp_dbh->pmysql)))));
5248-
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmariadb_st_fetch for %p, currow= %d\n",
5249-
sth,imp_sth->currow);
5244+
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\tmariadb_st_fetch for %p, currow=%" SVf "\n",
5245+
sth, SVfARG(sv_2mortal(my_ulonglong2sv(imp_sth->currow))));
52505246
}
52515247

52525248
if (!(cols= mysql_fetch_row(imp_sth->result)))
@@ -5259,9 +5255,14 @@ mariadb_st_fetch(SV *sth, imp_sth_t* imp_sth)
52595255
mariadb_dr_do_error(sth, mysql_errno(imp_dbh->pmysql),
52605256
mysql_error(imp_dbh->pmysql),
52615257
mysql_sqlstate(imp_dbh->pmysql));
5258+
if (!mysql_more_results(imp_dbh->pmysql))
5259+
DBIc_ACTIVE_off(imp_sth);
52625260
return Nullav;
52635261
}
52645262

5263+
if (imp_sth->currow >= imp_sth->row_num && !mysql_more_results(imp_dbh->pmysql))
5264+
DBIc_ACTIVE_off(imp_sth);
5265+
52655266
num_fields= mysql_num_fields(imp_sth->result);
52665267
fields= mysql_fetch_fields(imp_sth->result);
52675268
lengths= mysql_fetch_lengths(imp_sth->result);
@@ -5393,32 +5394,22 @@ int mariadb_st_finish(SV* sth, imp_sth_t* imp_sth) {
53935394
}
53945395

53955396
if (imp_sth->use_server_side_prepare && imp_sth->stmt)
5396-
{
5397-
/*
5398-
We have to fetch all data from stmt
5399-
There is may be useful for 2 cases:
5400-
1. st_finish when we have undef statement
5401-
2. call st_execute again when we have some unfetched data in stmt
5402-
*/
5403-
if (DBIc_ACTIVE(imp_sth) && mariadb_st_describe(sth, imp_sth) && !imp_sth->fetch_done)
5404-
mysql_stmt_free_result(imp_sth->stmt);
5405-
}
5397+
mysql_stmt_free_result(imp_sth->stmt);
5398+
5399+
/*
5400+
Clean-up previous result set(s) for sth to prevent
5401+
'Commands out of sync' error
5402+
*/
5403+
if (!mariadb_st_free_result_sets(sth, imp_sth, FALSE))
5404+
return 0;
54065405

54075406
/*
54085407
Cancel further fetches from this cursor.
54095408
We don't close the cursor till DESTROY.
54105409
The application may re execute it.
54115410
*/
5412-
if (imp_sth && DBIc_ACTIVE(imp_sth))
5413-
{
5414-
/*
5415-
Clean-up previous result set(s) for sth to prevent
5416-
'Commands out of sync' error
5417-
*/
5418-
if (!mariadb_st_free_result_sets(sth, imp_sth))
5419-
return 0;
5420-
}
54215411
DBIc_ACTIVE_off(imp_sth);
5412+
54225413
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
54235414
{
54245415
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\n<-- mariadb_st_finish\n");
@@ -5450,6 +5441,16 @@ void mariadb_st_destroy(SV *sth, imp_sth_t *imp_sth) {
54505441
int num_params;
54515442
int num_fields;
54525443

5444+
if (!PL_dirty)
5445+
{
5446+
/* During global destruction, DBI objects are destroyed in random order
5447+
* and therefore imp_dbh may be already freed. So do not access it. */
5448+
mariadb_st_finish(sth, imp_sth);
5449+
mariadb_st_free_result_sets(sth, imp_sth, TRUE);
5450+
}
5451+
5452+
DBIc_ACTIVE_off(imp_sth);
5453+
54535454
if (imp_sth->statement)
54545455
Safefree(imp_sth->statement);
54555456

@@ -5486,8 +5487,6 @@ void mariadb_st_destroy(SV *sth, imp_sth_t *imp_sth) {
54865487
imp_sth->stmt= NULL;
54875488
}
54885489

5489-
/* mariadb_st_finish has already been called by .xs code if needed. */
5490-
54915490
/* Free values allocated by mariadb_st_bind_ph */
54925491
if (imp_sth->params)
54935492
{
@@ -6520,6 +6519,12 @@ my_ulonglong mariadb_db_async_result(SV* h, MYSQL_RES** resp)
65206519
}
65216520
dbh->async_query_in_flight = NULL;
65226521

6522+
if (htype == DBIt_ST)
6523+
{
6524+
D_imp_sth(h);
6525+
DBIc_ACTIVE_off(imp_sth);
6526+
}
6527+
65236528
svsock= dbh->pmysql;
65246529
if (!svsock)
65256530
{
@@ -6561,13 +6566,13 @@ my_ulonglong mariadb_db_async_result(SV* h, MYSQL_RES** resp)
65616566

65626567
if(! *resp) {
65636568
imp_sth->insertid = dbh->insertid;
6564-
if(! mysql_more_results(svsock))
6565-
DBIc_ACTIVE_off(imp_sth);
6569+
if (mysql_more_results(svsock))
6570+
DBIc_ACTIVE_on(imp_sth);
65666571
} else {
65676572
num_fields = mysql_num_fields(imp_sth->result);
65686573
DBIc_NUM_FIELDS(imp_sth) = (num_fields <= INT_MAX) ? num_fields : INT_MAX;
6569-
imp_sth->done_desc = FALSE;
6570-
imp_sth->fetch_done = FALSE;
6574+
if (imp_sth->row_num)
6575+
DBIc_ACTIVE_on(imp_sth);
65716576
}
65726577
imp_sth->warning_count = mysql_warning_count(imp_dbh->pmysql);
65736578
}

dbdimp.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,8 +553,7 @@ struct imp_sth_st {
553553
bool disable_fallback_for_server_prepare;
554554

555555
MYSQL_RES* result; /* result */
556-
int currow; /* number of current row */
557-
bool fetch_done; /* mark that fetch done */
556+
my_ulonglong currow; /* number of current row */
558557
my_ulonglong row_num; /* total number of rows */
559558

560559
bool done_desc; /* have we described this sth yet ? */

0 commit comments

Comments
 (0)