Skip to content

Commit 849ad97

Browse files
committed
Merge pull request #125
Fix processing of multi result sets
2 parents 25aad51 + 3f9b67b commit 849ad97

File tree

2 files changed

+85
-29
lines changed

2 files changed

+85
-29
lines changed

dbdimp.c

Lines changed: 84 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2551,9 +2551,9 @@ IV mariadb_db_do6(SV *dbh, imp_dbh_t *imp_dbh, SV *statement_sv, SV *attribs, I3
25512551
bool disable_fallback_for_server_prepare = FALSE;
25522552
MYSQL_STMT *stmt = NULL;
25532553
MYSQL_BIND *bind = NULL;
2554-
MYSQL_RES *res;
25552554
STRLEN blen;
25562555
unsigned long int num_params;
2556+
unsigned int error;
25572557

25582558
ASYNC_CHECK_RETURN(dbh, -2);
25592559

@@ -2642,11 +2642,38 @@ IV mariadb_db_do6(SV *dbh, imp_dbh_t *imp_dbh, SV *statement_sv, SV *attribs, I3
26422642
imp_dbh->async_query_in_flight = imp_dbh;
26432643
}
26442644

2645-
while (mysql_next_result(imp_dbh->pmysql) == 0)
2645+
while ((next_result_rc = mysql_next_result(imp_dbh->pmysql)) == 0)
2646+
{
2647+
result = mysql_store_result(imp_dbh->pmysql);
2648+
if (!result)
2649+
{
2650+
if (mysql_errno(imp_dbh->pmysql))
2651+
{
2652+
mariadb_dr_do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql));
2653+
return -2;
2654+
}
2655+
}
2656+
if (result)
2657+
{
2658+
mysql_free_result(result);
2659+
result = NULL;
2660+
}
2661+
}
2662+
2663+
if (next_result_rc > 0)
26462664
{
2647-
res = mysql_use_result(imp_dbh->pmysql);
2648-
if (res)
2649-
mysql_free_result(res);
2665+
#if MYSQL_VERSION_ID < 50025
2666+
/* Cover a protocol design error: error packet does not contain the server status.
2667+
* Luckily, an error always aborts execution of a statement, so it is safe to turn off the flag. */
2668+
imp_dbh->pmysql->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
2669+
#endif
2670+
/* This is error for previous unfetched result ret. So do not report server errors to caller which is expecting new result set. */
2671+
error = mysql_errno(imp_dbh->pmysql);
2672+
if (error == CR_COMMANDS_OUT_OF_SYNC || error == CR_OUT_OF_MEMORY || error == CR_SERVER_GONE_ERROR || error == CR_SERVER_LOST || error == CR_UNKNOWN_ERROR)
2673+
{
2674+
mariadb_dr_do_error(dbh, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql));
2675+
return -2;
2676+
}
26502677
}
26512678

26522679
if (use_server_side_prepare)
@@ -2804,7 +2831,12 @@ IV mariadb_db_do6(SV *dbh, imp_dbh_t *imp_dbh, SV *statement_sv, SV *attribs, I3
28042831
/* more results? -1 = no, >0 = error, 0 = yes (keep looping) */
28052832
while ((next_result_rc = mysql_next_result(imp_dbh->pmysql)) == 0)
28062833
{
2807-
result = mysql_use_result(imp_dbh->pmysql);
2834+
result = mysql_store_result(imp_dbh->pmysql);
2835+
if (mysql_errno(imp_dbh->pmysql))
2836+
{
2837+
next_result_rc = 1;
2838+
break;
2839+
}
28082840
if (!result) /* Next statement without result set, new insert id */
28092841
imp_dbh->insertid = mysql_insert_id(imp_dbh->pmysql);
28102842
if (result)
@@ -2814,6 +2846,12 @@ IV mariadb_db_do6(SV *dbh, imp_dbh_t *imp_dbh, SV *statement_sv, SV *attribs, I3
28142846

28152847
if (next_result_rc > 0)
28162848
{
2849+
#if MYSQL_VERSION_ID < 50025
2850+
/* Cover a protocol design error: error packet does not contain the server status.
2851+
* Luckily, an error always aborts execution of a statement, so it is safe to turn off the flag. */
2852+
imp_dbh->pmysql->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
2853+
#endif
2854+
28172855
if (DBIc_DBISTATE(imp_dbh)->debug >= 2)
28182856
PerlIO_printf(DBIc_LOGPIO(imp_dbh), "\t<- do() ERROR: %s\n", mysql_error(imp_dbh->pmysql));
28192857

@@ -3668,7 +3706,7 @@ AV *mariadb_db_data_sources(SV *dbh, imp_dbh_t *imp_dbh, SV *attr)
36683706
return av;
36693707
}
36703708

3671-
static int mariadb_st_free_result_sets (SV * sth, imp_sth_t * imp_sth);
3709+
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth);
36723710

36733711
/*
36743712
**************************************************************************
@@ -3800,7 +3838,8 @@ mariadb_st_prepare_sv(
38003838
Clean-up previous result set(s) for sth to prevent
38013839
'Commands out of sync' error
38023840
*/
3803-
mariadb_st_free_result_sets(sth, imp_sth);
3841+
if (!mariadb_st_free_result_sets(sth, imp_sth))
3842+
return 0;
38043843

38053844
if (imp_sth->use_server_side_prepare)
38063845
{
@@ -3944,18 +3983,20 @@ mariadb_st_prepare_sv(
39443983
* Inputs: sth - Statement handle
39453984
* imp_sth - driver's private statement handle
39463985
*
3947-
* Returns: 1 ok
3948-
* 0 error
3986+
* Returns: TRUE ok
3987+
* FALSE error; mariadb_dr_do_error will be called
39493988
*************************************************************************/
3950-
static int mariadb_st_free_result_sets (SV * sth, imp_sth_t * imp_sth)
3989+
static bool mariadb_st_free_result_sets(SV *sth, imp_sth_t *imp_sth)
39513990
{
39523991
dTHX;
39533992
D_imp_dbh_from_sth;
39543993
D_imp_xxh(sth);
39553994
int next_result_rc= -1;
3995+
unsigned int error;
39563996

3997+
/* No connection, nothing to clean, no error */
39573998
if (!imp_dbh->pmysql)
3958-
return 0;
3999+
return TRUE;
39594000

39604001
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
39614002
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t>- mariadb_st_free_result_sets\n");
@@ -3967,18 +4008,18 @@ static int mariadb_st_free_result_sets (SV * sth, imp_sth_t * imp_sth)
39674008

39684009
if (next_result_rc == 0)
39694010
{
3970-
if (!(imp_sth->result = mysql_use_result(imp_dbh->pmysql)))
4011+
if (!(imp_sth->result = mysql_store_result(imp_dbh->pmysql)))
39714012
{
39724013
/* Check for possible error */
3973-
if (mysql_field_count(imp_dbh->pmysql))
4014+
if (mysql_errno(imp_dbh->pmysql))
39744015
{
39754016
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
39764017
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- mariadb_st_free_result_sets ERROR: %s\n",
39774018
mysql_error(imp_dbh->pmysql));
39784019

39794020
mariadb_dr_do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql),
39804021
mysql_sqlstate(imp_dbh->pmysql));
3981-
return 0;
4022+
return FALSE;
39824023
}
39834024
}
39844025
}
@@ -3991,18 +4032,29 @@ static int mariadb_st_free_result_sets (SV * sth, imp_sth_t * imp_sth)
39914032

39924033
if (next_result_rc > 0)
39934034
{
4035+
#if MYSQL_VERSION_ID < 50025
4036+
/* Cover a protocol design error: error packet does not contain the server status.
4037+
* Luckily, an error always aborts execution of a statement, so it is safe to turn off the flag. */
4038+
imp_dbh->pmysql->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
4039+
#endif
4040+
39944041
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
39954042
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- mariadb_st_free_result_sets: Error while processing multi-result set: %s\n",
39964043
mysql_error(imp_dbh->pmysql));
39974044

3998-
mariadb_dr_do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql),
3999-
mysql_sqlstate(imp_dbh->pmysql));
4045+
/* This is error for previous unfetched result ret. So do not report server errors to caller which is expecting new result set. */
4046+
error = mysql_errno(imp_dbh->pmysql);
4047+
if (error == CR_COMMANDS_OUT_OF_SYNC || error == CR_OUT_OF_MEMORY || error == CR_SERVER_GONE_ERROR || error == CR_SERVER_LOST || error == CR_UNKNOWN_ERROR)
4048+
{
4049+
mariadb_dr_do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql), mysql_sqlstate(imp_dbh->pmysql));
4050+
return FALSE;
4051+
}
40004052
}
40014053

40024054
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)
40034055
PerlIO_printf(DBIc_LOGPIO(imp_xxh), "\t<- mariadb_st_free_result_sets\n");
40044056

4005-
return 1;
4057+
return TRUE;
40064058
}
40074059

40084060

@@ -4083,6 +4135,11 @@ bool mariadb_st_more_results(SV* sth, imp_sth_t* imp_sth)
40834135
*/
40844136
if (next_result_return_code > 0)
40854137
{
4138+
#if MYSQL_VERSION_ID < 50025
4139+
/* Cover a protocol design error: error packet does not contain the server status.
4140+
* Luckily, an error always aborts execution of a statement, so it is safe to turn off the flag. */
4141+
imp_dbh->pmysql->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
4142+
#endif
40864143
mariadb_dr_do_error(sth, mysql_errno(imp_dbh->pmysql), mysql_error(imp_dbh->pmysql),
40874144
mysql_sqlstate(imp_dbh->pmysql));
40884145

@@ -4270,6 +4327,11 @@ static my_ulonglong mariadb_st_internal_execute(
42704327
(!mariadb_db_reconnect(h, NULL) ||
42714328
(mysql_real_query(*svsock, sbuf, slen))))
42724329
{
4330+
#if MYSQL_VERSION_ID < 50025
4331+
/* Cover a protocol design error: error packet does not contain the server status.
4332+
* Luckily, an error always aborts execution of a statement, so it is safe to turn off the flag. */
4333+
(*svsock)->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
4334+
#endif
42734335
rows = -1;
42744336
} else {
42754337
/** Store the result from the Query */
@@ -4538,7 +4600,8 @@ IV mariadb_st_execute_iv(SV* sth, imp_sth_t* imp_sth)
45384600
Clean-up previous result set(s) for sth to prevent
45394601
'Commands out of sync' error
45404602
*/
4541-
mariadb_st_free_result_sets (sth, imp_sth);
4603+
if (!mariadb_st_free_result_sets(sth, imp_sth))
4604+
return -2;
45424605

45434606
if (use_server_side_prepare)
45444607
{
@@ -5332,7 +5395,8 @@ int mariadb_st_finish(SV* sth, imp_sth_t* imp_sth) {
53325395
Clean-up previous result set(s) for sth to prevent
53335396
'Commands out of sync' error
53345397
*/
5335-
mariadb_st_free_result_sets(sth, imp_sth);
5398+
if (!mariadb_st_free_result_sets(sth, imp_sth))
5399+
return 0;
53365400
}
53375401
DBIc_ACTIVE_off(imp_sth);
53385402
if (DBIc_TRACE_LEVEL(imp_xxh) >= 2)

t/76multi_statement.t

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ my $dbh = DbiTestConnect($test_dsn, $test_user, $test_password,
1313
{ RaiseError => 1, PrintError => 0, AutoCommit => 0,
1414
mariadb_multi_statements => 1 });
1515

16-
if ($dbh->{mariadb_serverversion} < 50025 or ($dbh->{mariadb_serverversion} >= 50100 and $dbh->{mariadb_serverversion} < 50112)) {
17-
plan skip_all => "Server has deadlock bug 16581";
18-
}
19-
20-
if ($dbh->{mariadb_clientversion} < 50025 or ($dbh->{mariadb_clientversion} >= 50100 and $dbh->{mariadb_clientversion} < 50112)) {
21-
plan skip_all => "Client has multiple-result-set detection deadlock bug 15752";
22-
}
23-
2416
plan tests => 36;
2517

2618
ok (defined $dbh, "Connected to database with multi statement support");
@@ -40,7 +32,7 @@ $dbh->{mariadb_server_prepare}= 0;
4032

4133
# Check that more_results works for non-SELECT results too
4234
my $sth;
43-
ok($sth = $dbh->prepare("UPDATE dbd_mysql_t76multi SET a=5 WHERE a=1; UPDATE dbd_mysql_t76multi SET a='6-' WHERE a<4"));
35+
ok($sth = $dbh->prepare("UPDATE dbd_mysql_t76multi SET a=5 WHERE a=1; UPDATE dbd_mysql_t76multi SET a='6suffix' WHERE a<4"));
4436
ok($sth->execute(), "Execute updates");
4537
is($sth->rows, 1, "First update affected 1 row");
4638
is($sth->{mariadb_warning_count}, 0, "First update had no warnings");

0 commit comments

Comments
 (0)