List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:December 22 2008 12:34pm
Subject:bzr commit into mysql-6.0-runtime branch (alik:2780) Bug#39519
View as plain text  
#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 },

Thread
bzr commit into mysql-6.0-runtime branch (alik:2780) Bug#39519Alexander Nozdrin22 Dec
  • Re: bzr commit into mysql-6.0-runtime branch (alik:2780) Bug#39519Konstantin Osipov20 Feb 2009