Bug #24390 Com_stmt_close doesn't increment when prepare failed, but Com_stmt_prepare does
Submitted: 17 Nov 2006 11:44 Modified: 30 Nov 2006 20:43
Reporter: Shane Bester (Platinum Quality Contributor) Email Updates:
Status: Not a Bug Impact on me:
None 
Category:MySQL Server: C API (client library) Severity:S3 (Non-critical)
Version:5.0.30,5.1.13,4.1.23 OS:Any (*)
Assigned to: CPU Architecture:Any
Tags: Com_stmt_close, Com_stmt_prepare

[17 Nov 2006 11:44] Shane Bester
Description:
I normally use the com_%stmt% status variables to check whether
my code is cleanly closing all prepared statements, and not leaking any.

But, I found that sometimes mysql_stmt_prepare() fails, 
due to whatever reason.  In this case, Com_stmt_prepare is
incremented.   My code correctly calls mysql_stmt_close()
which doesn't give an error, but Com_stmt_close is not incremented.

So, we have a situation like this:

mysql> show global status like '%stmt%';
+-------------------------+-------+
| Variable_name           | Value |
+-------------------------+-------+
| Com_stmt_close          | 3     |
| Com_stmt_execute        | 1     |
| Com_stmt_fetch          | 1     |
| Com_stmt_prepare        | 1219  |
| Com_stmt_reset          | 0     |
| Com_stmt_send_long_data | 0     |
+-------------------------+-------+

Yet, my code always called mysql_stmt_close() as it should.
Since I like to identify prepared statement 'leaks' using these
variables, it's quite inconvenient to have the numbers
not match up to the actual api calls made.  Monitoring apps
might be affected too.

How to repeat:
See top of 'prepare.c' attached to see how to compile and run.
First, run with:

char *query="select dfgdfg";  //invalid will fail prepare

Then, make it valid and recompile and run:

char *query="select now()";

not the output of show global status like '%stmt%'; each time.

Suggested fix:
I guess this piece of code fails in mysql_stmt_close()

if (!(stmt= find_prepared_statement(thd, stmt_id, "mysql_stmt_close")))
  DBUG_VOID_RETURN;

which never allows it to reach stmt->deallocate(), which contains the much
needed "statistic_increment(thd->status_var.com_stmt_close, &LOCK_status);"
[17 Nov 2006 11:44] MySQL Verification Team
testcase

Attachment: prepare.c (text/x-csrc), 1.41 KiB.

[30 Nov 2006 20:43] Konstantin Osipov
Shane, mysql_stmt_close is not necessarily a server call. 
Imagine the following C API sequnce:

stmt= mysql_stmt_init(mysql);
mysql_stmt_close(stmt);

In this case, all operations are local and Com_stmt_close is not incremented (the server doesn't even know you allocated a handle and then closed it).

In this case:
stmt= mysql_stmt_init(mysql);
mysql_stmt_prepare(stmt); // there is an error, so there is no statement
// handle on the server side, and the client library notices that
mysql_stmt_close(stmt); 

There is also no server call.