[CONC-683] Closing a statement while another statement has a cursor opened causes erratic behaviour (random errors, hangs, crashes, etc) Created: 2024-01-16  Updated: 2024-01-16

Status: Open
Project: MariaDB Connector/C
Component/s: None
Affects Version/s: 3.3.8
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Josep Yus Assignee: Georg Richter
Resolution: Unresolved Votes: 0
Labels: None

Attachments: File patch.diff    

 Description   

Closing a statement that has not started retrieval while there is a cursor opened in another statement causes the mariadb client to loose sync with the physical protocol state which in turn causes erratic behaviour in following interactions with the API: wrong return codes, crashes, hangs, etc

Correct behaviour should be for "mysql_stmt_close" to close the current cursor (regardless of what statement opened it). Then attempts to fetch data from a cursor while there is none active (regardless of the statement used in the mysql_stmt_fetch call) should return an error saying there is no cursor opened.

The interaction causing the issue to surface is:

create stmt1
prepare stmt1
create stmt2
prepare stmt2
fetch stmt1
close stmt2 -> from this point onwards the client is in an inconsistent state. Any interaction with the client from this point onwards will be subject to undeterministic behavior.

NOTE: The application I work on and where this causes wrong behaviour (crashes in my case) allows customers to choose how statements interact with connections. The customer can for instance use a connection per stmt or can decide to have concurrent statements using the same connection. This flexibility is in place since this application supports many other c connectors (oracle, postgres, etc) where retrieving data concurrently from different statements is supported by their respectives clients. In short, getting real useful errors indicating the interaction attempted is not supported by the underlying connector is ok but getting crashes or undeterministic behaviour is not.

For your convenience I have attached a patch.diff (generated from "master") with test cases that reproduce this issue. The patch adds this test cases in the "ps_bugs" executable that comes as part of the mariadb c connector test suite.

Also, I have done an initial investigation which shows that the culprit seems to be in line 379 (this is from "master" at the time of writing) in function mthd_stmt_flush_unbuffered() in file mariadb_stmt.c:

 369 void mthd_stmt_flush_unbuffered(MYSQL_STMT *stmt)
 370 {
 371   ulong packet_len;
 372   int in_resultset= stmt->state > MYSQL_STMT_EXECUTED &&
 373                     stmt->state < MYSQL_STMT_FETCH_DONE;
 374   while ((packet_len = ma_net_safe_read(stmt->mysql)) != packet_error)
 375   {
 376     unsigned int last_status= stmt->mysql->server_status;
 377     uchar *pos= stmt->mysql->net.read_pos;
 378 
 
"in_resultset" is 0 since "stmt" has not started fetching yet. However, there is 
a cursor open at this point on a different statement so "pos" is not pointing 
at an OK packet but at the header (also 0x00) of a row packet.
 
Fix: The mariadb client should not be using the state of the stmt struct to 
know if it is in the middle of a fetch but the state of the MYSQL struct.
 
 379     if (!in_resultset && *pos == 0) /* OK */
 380     {
 381       pos++;
 382       net_field_length(&pos);
 383       net_field_length(&pos);
 384       stmt->mysql->server_status= uint2korr(pos);
 385       ma_status_callback(stmt->mysql, last_status);
 386       goto end;
 387     }
 388     if (packet_len < 8 && *pos == 254) /* EOF */
 389     {
 390       if (mariadb_connection(stmt->mysql))
 391       {
 392         stmt->mysql->server_status= uint2korr(pos + 3);
 393         ma_status_callback(stmt->mysql, last_status);
 394         if (in_resultset)
 395           goto end;
 396         in_resultset= 1;
 397       }
 398       else
 399         goto end;
 400     }
 401   }
 402 end:
 403   stmt->state= MYSQL_STMT_FETCH_DONE;
 404 }
 

Please have a look.

Thanks



 Comments   
Comment by Georg Richter [ 2024-01-16 ]

Thanks for your bug report.

I was able to reproduce this issue:

The problem is that mysql_stmt_close(stmt2) doesn't return an error (commands out of sync).

Comment by Josep Yus [ 2024-01-16 ]

Thanks for acknowledging this bug so quickly. If you come up with a quick patch let me know and I will be happy to test it before you push it.

Generated at Thu Feb 08 03:07:05 UTC 2024 using Jira 8.20.16#820016-sha1:9d11dbea5f4be3d4cc21f03a88dd11d8c8687422.