From: Date: December 22 2008 12:34pm Subject: bzr commit into mysql-6.0-runtime branch (alik:2780) Bug#39519 List-Archive: http://lists.mysql.com/commits/62206 X-Bug: 39519 Message-Id: <20081222113442.A0FF91D48F3@quad.opbmk> MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit #At file:///mnt/raid/alik/MySQL/bzr/bug39519/6.0-rt-bug39519/ 2780 Alexander Nozdrin 2008-12-22 Bug#39519: mysql_stmt_close() should flush all data associated with the statement. modified: include/mysql.h include/mysql.h.pp libmysql/libmysql.c libmysqld/lib_sql.cc sql-common/client.c tests/mysql_client_test.c per-file messages: include/mysql.h Add a new flag to MYSQL_METHODS::flush_use_result function pointer. This flag determines if all results should be flushed or only the first one: - if flush_all_results is TRUE, then cli_flush_use_result() will read/flush all pending results. I.e. it will read all packets while server status attribute indicates that there are more results. This is a new semantic, required to fix the bug. - if flush_all_results is FALSE, the old sematic is preserved -- i.e. cli_flush_use_result() reads data untile first EOF-packet. include/mysql.h.pp Update API. libmysql/libmysql.c Flush all results in mysql_stmt_close(). sql-common/client.c Implement new functionality in cli_flush_use_result(): if flush_all_delete is TRUE, then it should read/flush all pending results. In other words, it should read all packets while server status attribute indicates that there are more results. tests/mysql_client_test.c Add a test case for Bug#39519 / WL#4435. === modified file 'include/mysql.h' --- a/include/mysql.h 2008-12-05 01:05:05 +0000 +++ b/include/mysql.h 2008-12-22 11:34:26 +0000 @@ -654,7 +654,7 @@ typedef struct st_mysql_methods MYSQL_RES * (*use_result)(MYSQL *mysql); void (*fetch_lengths)(unsigned long *to, MYSQL_ROW column, unsigned int field_count); - void (*flush_use_result)(MYSQL *mysql); + void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); #if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY) MYSQL_FIELD * (*list_fields)(MYSQL *mysql); my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); === modified file 'include/mysql.h.pp' --- a/include/mysql.h.pp 2008-10-20 19:13:22 +0000 +++ b/include/mysql.h.pp 2008-12-22 11:34:26 +0000 @@ -557,7 +557,7 @@ typedef struct st_mysql_methods MYSQL_RES * (*use_result)(MYSQL *mysql); void (*fetch_lengths)(unsigned long *to, MYSQL_ROW column, unsigned int field_count); - void (*flush_use_result)(MYSQL *mysql); + void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results); MYSQL_FIELD * (*list_fields)(MYSQL *mysql); my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt); int (*stmt_execute)(MYSQL_STMT *stmt); === modified file 'libmysql/libmysql.c' --- a/libmysql/libmysql.c 2008-12-08 11:29:15 +0000 +++ b/libmysql/libmysql.c 2008-12-22 11:34:26 +0000 @@ -4608,7 +4608,7 @@ static my_bool reset_stmt_handle(MYSQL_S if (stmt->field_count && mysql->status != MYSQL_STATUS_READY) { /* There is a result set and it belongs to this statement */ - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, FALSE); if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; mysql->status= MYSQL_STATUS_READY; @@ -4692,7 +4692,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_S Flush result set of the connection. If it does not belong to this statement, set a warning. */ - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, TRUE); if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; mysql->status= MYSQL_STATUS_READY; === modified file 'libmysqld/lib_sql.cc' --- a/libmysqld/lib_sql.cc 2008-12-08 15:07:40 +0000 +++ b/libmysqld/lib_sql.cc 2008-12-22 11:34:26 +0000 @@ -139,7 +139,7 @@ emb_advanced_command(MYSQL *mysql, enum return result; } -static void emb_flush_use_result(MYSQL *mysql) +static void emb_flush_use_result(MYSQL *mysql, my_bool) { THD *thd= (THD*) mysql->thd; if (thd->cur_data) === modified file 'sql-common/client.c' --- a/sql-common/client.c 2008-10-22 11:51:28 +0000 +++ b/sql-common/client.c 2008-12-22 11:34:26 +0000 @@ -828,27 +828,165 @@ void free_old_query(MYSQL *mysql) Flush result set sent from server */ -static void cli_flush_use_result(MYSQL *mysql) +static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results) { /* Clear the current execution status */ DBUG_ENTER("cli_flush_use_result"); DBUG_PRINT("warning",("Not all packets read, clearing them")); - for (;;) + + /* + If flush_all_results is FALSE, then we just read through the first EOF + packet. + + MYSQL_STATUS_READY means, we're in front of a ResultSet packet, or an + Ok-packet. + + MYSQL_STATUS_GET_RESULT or MYSQL_STATUS_USE_RESULT means we're in the + beginning (or in the middle) of data-part of ResultSet-packet. + */ + + if (!flush_all_results || + mysql->status == MYSQL_STATUS_GET_RESULT || + mysql->status == MYSQL_STATUS_USE_RESULT) { - ulong pkt_len; - if ((pkt_len=cli_safe_read(mysql)) == packet_error) - break; - if (pkt_len <= 8 && mysql->net.read_pos[0] == 254) + my_bool more_results_exist; + + /* Waiting for EOF-packet (it will be PT_RS_DATA_EOF). */ + + while (1) + { + ulong packet_length= cli_safe_read(mysql); + + if (packet_length == packet_error) + DBUG_VOID_RETURN; + + if (packet_length <= 8 && mysql->net.read_pos[0] == 254) + break; + } + + /* Analyze received EOF-packet (PT_RS_DATA_EOF). */ + + if (protocol_41(mysql)) + { + char *pos= (char*) mysql->net.read_pos + 1; + mysql->warning_count=uint2korr(pos); pos+=2; + mysql->server_status=uint2korr(pos); pos+=2; + + more_results_exist= mysql->server_status & SERVER_MORE_RESULTS_EXISTS; + } + else + { + /* + This is the 4.0 protocol, which does not support multi result sets. + */ + more_results_exist= FALSE; + } + + if (!flush_all_results) + DBUG_VOID_RETURN; + + mysql->status = MYSQL_STATUS_READY; + + if (!more_results_exist) + DBUG_VOID_RETURN; + } + + DBUG_ASSERT(mysql->status == MYSQL_STATUS_READY); + DBUG_ASSERT(flush_all_results); + + /* + Here we can get two types of packets: + - OK-packet. + - ResultSet-packet. + */ + + while (1) + { + ulong packet_length= cli_safe_read(mysql); + + if (packet_length == packet_error) + DBUG_VOID_RETURN; + + if (packet_length > 0 && mysql->net.read_pos[0] == 0) { + /* Analyze OK-packet. */ + + uchar *pos= mysql->net.read_pos + 1; + + net_field_length_ll(&pos); /* affected rows */ + net_field_length_ll(&pos); /* insert id */ + + mysql->server_status=uint2korr(pos); + pos+=2; + if (protocol_41(mysql)) { - char *pos= (char*) mysql->net.read_pos + 1; - mysql->warning_count=uint2korr(pos); pos+=2; - mysql->server_status=uint2korr(pos); pos+=2; + mysql->warning_count=uint2korr(pos); + pos+=2; } - break; /* End of data */ + + /* message is ignored. */ + + if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS) + continue; + + DBUG_VOID_RETURN; } + + /* This is a ResultSet-packet. */ + + packet_length= cli_safe_read(mysql); /* Field count. */ + if (packet_length == packet_error) + DBUG_VOID_RETURN; + + /* Reading metadata (until EOF). */ + + while (1) + { + packet_length= cli_safe_read(mysql); /* Metadata row or EOF. */ + if (packet_length == packet_error) + DBUG_VOID_RETURN; + + if (packet_length > 0 && mysql->net.read_pos[0] == 0xfe) + break; + } + + /* Reading data (until EOF). */ + + while (1) + { + packet_length= cli_safe_read(mysql); /* Data row or EOF. */ + if (packet_length == packet_error) + DBUG_VOID_RETURN; + + if (packet_length > 0 && mysql->net.read_pos[0] == 0xfe) + break; + } + + /* Analyze received EOF-packet (PT_RS_DATA_EOF). */ + + my_bool more_results_exist; + + if (protocol_41(mysql)) + { + char *pos= (char*) mysql->net.read_pos + 1; + mysql->warning_count=uint2korr(pos); pos+=2; + mysql->server_status=uint2korr(pos); pos+=2; + + more_results_exist= mysql->server_status & SERVER_MORE_RESULTS_EXISTS; + } + else + { + /* + This is the 4.0 protocol, which does not support multi result sets. + */ + more_results_exist= FALSE; + } + + if (!more_results_exist) + DBUG_VOID_RETURN; } + DBUG_VOID_RETURN; } @@ -950,7 +1088,7 @@ mysql_free_result(MYSQL_RES *result) mysql->unbuffered_fetch_owner= 0; if (mysql->status == MYSQL_STATUS_USE_RESULT) { - (*mysql->methods->flush_use_result)(mysql); + (*mysql->methods->flush_use_result)(mysql, FALSE); mysql->status=MYSQL_STATUS_READY; if (mysql->unbuffered_fetch_owner) *mysql->unbuffered_fetch_owner= TRUE; === modified file 'tests/mysql_client_test.c' --- a/tests/mysql_client_test.c 2008-11-27 14:30:07 +0000 +++ b/tests/mysql_client_test.c 2008-12-22 11:34:26 +0000 @@ -2039,6 +2039,43 @@ static void test_wl4435() } } +static void test_wl4435_2() +{ + MYSQL_STMT *stmt; + int rc; + char query[MAX_TEST_QUERY_LENGTH]; + + myheader("test_wl4435_2"); + mct_start_logging("test_wl4435_2"); + + rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1"); + myquery(rc); + + rc= mysql_query(mysql, + "CREATE PROCEDURE p1()" + "BEGIN " + " SELECT 1; " + " SELECT 2, 3 UNION SELECT 4, 5; " + " SELECT 6, 7, 8; " + "END"); + myquery(rc); + + strmov(query, "CALL p1()"); + stmt= mysql_simple_prepare(mysql, query); + check_stmt(stmt); + + /* Execute! */ + + rc= mysql_stmt_execute(stmt); + check_execute(stmt, rc); + + mysql_stmt_close(stmt); + + rc= mysql_commit(mysql); + myquery(rc); +} + + /* Test simple prepare field results */ static void test_prepare_field_result() @@ -18847,6 +18884,7 @@ static struct my_tests_st my_tests[]= { { "test_bug36004", test_bug36004 }, { "test_wl4284_1", test_wl4284_1 }, { "test_wl4435", test_wl4435 }, + { "test_wl4435_2", test_wl4435_2 }, { "test_bug38486", test_bug38486 }, { "test_bug33831", test_bug33831 }, { "test_bug40365", test_bug40365 },