List:Commits« Previous MessageNext Message »
From:Konstantin Osipov Date:October 21 2009 12:52pm
Subject:bzr commit into mysql-5.5.0-next-mr-runtime branch (kostja:2932)
WL#4264
View as plain text  
#At file:///opt/local/work/next-mr-kostja/ based on revid:kostja@stripped

 2932 Konstantin Osipov	2009-10-21
      Backport of:
      ----------------------------------------------------------
      revno: 2630.28.10
      committer: Konstantin Osipov <kostja@stripped>
      branch nick: mysql-6.0-runtime
      timestamp: Tue 2008-11-18 23:25:51 +0300
      message:
        WL#4264 "Backup: Stabilize Service Interface", a prerequisite
        patch: use Protocol::end_statement() instead of net_end_statement().
        This allows to intercept OK, EOF and ERROR packets in the protocol,
        and thus redirect all interactaion between the client and the server
        through the abstraction of this class.
        Committing on behalf of Alexander Nozdrin.
      
      ******
      Backport of:
      ----------------------------------------------------------
      revno: 2630.28.11
      committer: Konstantin Osipov <kostja@stripped>
      branch nick: mysql-6.0-runtime
      timestamp: Wed 2008-11-19 01:30:59 +0300
      message:
        Wl#4264 "Backup: Stabilize Service Interface", a prerequisite patch:
        move Warning_info class declaration and implementation from
        sql_class.{h,cc} to sql_error.{h,cc} and make sql_error.h a
        self-sufficient header.
      
      Except that thanks to SINGAL/RESIGNAL, there is a new
      dependency in sql_error.h on sql_string.h, which is not
      self-sufficient, so this change was effectively reverted by 
      SIGNAL/RESIGNAL (which got in first).
      ******
      Backport of:
      ------------------------------------------------------------
      revno: 2630.23.1
      committer: Alexander Nozdrin <alik@stripped>
      branch nick: 6.0-rt-wl4435
      timestamp: Thu 2008-08-07 21:52:43 +0400
      message:
        Patch for WL#4435: Support OUT-parameters in prepared statements.
      
        After execution of a prepared statement, send OUT parameters of the invoked
        stored procedure, if any, to the client.
      
        When using the binary protocol, send the parameters in an additional result
        set over the wire.  When using the text protocol, assign out parameters to
        the user variables from the CALL(@var1, @var2, ...) specification.
      
        The following refactoring has been made:
          - Protocol::send_fields() was renamed to Protocol::send_result_set_metadata();
          - A new Protocol::send_result_set_row() was introduced to incapsulate
            common functionality for sending row data.
          - Signature of Protocol::prepare_for_send() was changed: this operation
            does not need a list of items, the number of items is fully sufficient.
      
        The following backward incompatible changes have been made:
          - CLIENT_MULTI_RESULTS is now enabled by default in the client;
          - CLIENT_PS_MULTI_RESUTLS is now enabled by default in the client.
      
      ******
      Backport of:
      ------------------------------------------------------------
      revno: 2617.23.14
      committer: Alexander Nozdrin <alik@stripped>
      branch nick: 6.0-rt-bug39519
      timestamp: Wed 2009-02-25 13:03:18 +0300
      message:
        Bug#39519: mysql_stmt_close() should flush all data
        associated with the statement.

    modified:
      include/mysql.h
      include/mysql.h.pp
      include/mysql_com.h
      libmysql/client_settings.h
      libmysql/libmysql.c
      libmysqld/lib_sql.cc
      mysql-test/r/ps.result
      mysql-test/t/ps.test
      sql-common/client.c
      sql/events.cc
      sql/handler.cc
      sql/item.cc
      sql/item.h
      sql/procedure.h
      sql/protocol.cc
      sql/protocol.h
      sql/repl_failsafe.cc
      sql/slave.cc
      sql/sp_head.cc
      sql/sql_acl.cc
      sql/sql_class.cc
      sql/sql_class.h
      sql/sql_connect.cc
      sql/sql_cursor.cc
      sql/sql_error.cc
      sql/sql_handler.cc
      sql/sql_help.cc
      sql/sql_list.cc
      sql/sql_list.h
      sql/sql_parse.cc
      sql/sql_prepare.cc
      sql/sql_profile.cc
      sql/sql_repl.cc
      sql/sql_select.cc
      sql/sql_show.cc
      sql/sql_table.cc
      tests/mysql_client_test.c
=== modified file 'include/mysql.h'
--- a/include/mysql.h	2009-10-15 20:56:17 +0000
+++ b/include/mysql.h	2009-10-21 12:52:25 +0000
@@ -656,7 +656,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);
@@ -716,6 +716,7 @@ my_bool STDCALL mysql_rollback(MYSQL * m
 my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
 my_bool STDCALL mysql_more_results(MYSQL *mysql);
 int STDCALL mysql_next_result(MYSQL *mysql);
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt);
 void STDCALL mysql_close(MYSQL *sock);
 
 

=== modified file 'include/mysql.h.pp'
--- a/include/mysql.h.pp	2009-10-15 20:56:17 +0000
+++ b/include/mysql.h.pp	2009-10-21 12:52:25 +0000
@@ -560,7 +560,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);
@@ -615,4 +615,5 @@ my_bool mysql_rollback(MYSQL * mysql);
 my_bool mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
 my_bool mysql_more_results(MYSQL *mysql);
 int mysql_next_result(MYSQL *mysql);
+int mysql_stmt_next_result(MYSQL_STMT *stmt);
 void mysql_close(MYSQL *sock);

=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h	2009-10-15 21:11:42 +0000
+++ b/include/mysql_com.h	2009-10-21 12:52:25 +0000
@@ -144,6 +144,7 @@ enum enum_server_command
 #define CLIENT_SECURE_CONNECTION 32768  /* New 4.1 authentication */
 #define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */
 #define CLIENT_MULTI_RESULTS    (1UL << 17) /* Enable/disable multi-results */
+#define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
 
 #define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
 #define CLIENT_REMEMBER_OPTIONS (1UL << 31)
@@ -167,6 +168,7 @@ enum enum_server_command
                            CLIENT_SECURE_CONNECTION | \
                            CLIENT_MULTI_STATEMENTS | \
                            CLIENT_MULTI_RESULTS | \
+                           CLIENT_PS_MULTI_RESULTS | \
                            CLIENT_SSL_VERIFY_SERVER_CERT | \
                            CLIENT_REMEMBER_OPTIONS)
 
@@ -205,6 +207,11 @@ enum enum_server_command
 #define SERVER_STATUS_METADATA_CHANGED 1024
 
 /**
+  To mark ResultSet containing output parameter values.
+*/
+#define SERVER_PS_OUT_PARAMS            4096
+
+/**
   Server status flags that must be cleared when starting
   execution of a new SQL statement.
   Flags from this set are only added to the

=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h	2007-09-29 19:31:08 +0000
+++ b/libmysql/client_settings.h	2009-10-21 12:52:25 +0000
@@ -16,9 +16,13 @@
 extern uint		mysql_port;
 extern char *	mysql_unix_port;
 
-#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG |	  \
-                             CLIENT_TRANSACTIONS | \
-			     CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
+                             CLIENT_LONG_FLAG |     \
+                             CLIENT_TRANSACTIONS |  \
+                             CLIENT_PROTOCOL_41 | \
+                             CLIENT_SECURE_CONNECTION | \
+                             CLIENT_MULTI_RESULTS | \
+                             CLIENT_PS_MULTI_RESULTS)
 
 sig_handler my_pipe_sig_handler(int sig);
 void read_user_name(char *name);

=== modified file 'libmysql/libmysql.c'
--- a/libmysql/libmysql.c	2009-10-15 20:56:17 +0000
+++ b/libmysql/libmysql.c	2009-10-21 12:52:25 +0000
@@ -1722,6 +1722,8 @@ static void alloc_stmt_fields(MYSQL_STMT
   MEM_ROOT *alloc= &stmt->mem_root;
   MYSQL *mysql= stmt->mysql;
 
+  DBUG_ASSERT(mysql->field_count);
+
   stmt->field_count= mysql->field_count;
 
   /*
@@ -1743,18 +1745,21 @@ static void alloc_stmt_fields(MYSQL_STMT
 	 field= stmt->fields;
        field && fields < end; fields++, field++)
   {
-    field->db       = strdup_root(alloc,fields->db);
-    field->table    = strdup_root(alloc,fields->table);
-    field->org_table= strdup_root(alloc,fields->org_table);
-    field->name     = strdup_root(alloc,fields->name);
-    field->org_name = strdup_root(alloc,fields->org_name);
-    field->charsetnr= fields->charsetnr;
-    field->length   = fields->length;
-    field->type     = fields->type;
-    field->flags    = fields->flags;
-    field->decimals = fields->decimals;
-    field->def      = fields->def ? strdup_root(alloc,fields->def): 0;
-    field->max_length= 0;
+    *field= *fields; /* To copy all numeric parts. */
+    field->catalog=   strmake_root(alloc, fields->catalog,
+                                   fields->catalog_length);
+    field->db=        strmake_root(alloc, fields->db, fields->db_length);
+    field->table=     strmake_root(alloc, fields->table, fields->table_length);
+    field->org_table= strmake_root(alloc, fields->org_table,
+                                   fields->org_table_length);
+    field->name=      strmake_root(alloc, fields->name, fields->name_length);
+    field->org_name=  strmake_root(alloc, fields->org_name,
+                                   fields->org_name_length);
+    field->def=       fields->def ? strmake_root(alloc, fields->def,
+                                                 fields->def_length) : 0;
+    field->def_length= field->def ? fields->def_length : 0;
+    field->extension= 0; /* Avoid dangling links. */
+    field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */
   }
 }
 
@@ -2481,6 +2486,33 @@ static void reinit_result_set_metadata(M
 }
 
 
+static void prepare_to_fetch_result(MYSQL_STMT *stmt)
+{
+  if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
+  {
+    stmt->mysql->status= MYSQL_STATUS_READY;
+    stmt->read_row_func= stmt_read_row_from_cursor;
+  }
+  else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
+  {
+    /*
+      This is a single-row result set, a result set with no rows, EXPLAIN,
+      SHOW VARIABLES, or some other command which either a) bypasses the
+      cursors framework in the server and writes rows directly to the
+      network or b) is more efficient if all (few) result set rows are
+      precached on client and server's resources are freed.
+    */
+    mysql_stmt_store_result(stmt);
+  }
+  else
+  {
+    stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+    stmt->unbuffered_fetch_cancelled= FALSE;
+    stmt->read_row_func= stmt_read_row_unbuffered;
+  }
+}
+
+
 /*
   Send placeholders data to server (if there are placeholders)
   and execute prepared statement.
@@ -2548,28 +2580,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STM
   if (mysql->field_count)
   {
     reinit_result_set_metadata(stmt);
-    if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
-    {
-      mysql->status= MYSQL_STATUS_READY;
-      stmt->read_row_func= stmt_read_row_from_cursor;
-    }
-    else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
-    {
-      /*
-        This is a single-row result set, a result set with no rows, EXPLAIN,
-        SHOW VARIABLES, or some other command which either a) bypasses the
-        cursors framework in the server and writes rows directly to the
-        network or b) is more efficient if all (few) result set rows are
-        precached on client and server's resources are freed.
-      */
-      mysql_stmt_store_result(stmt);
-    }
-    else
-    {
-      stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
-      stmt->unbuffered_fetch_cancelled= FALSE;
-      stmt->read_row_func= stmt_read_row_unbuffered;
-    }
+    prepare_to_fetch_result(stmt);
   }
   DBUG_RETURN(test(stmt->last_errno));
 }
@@ -4034,7 +4045,6 @@ static my_bool setup_one_fetch_function(
     field->max_length= 10;                    /* 2003-11-11 */
     param->skip_result= skip_result_with_length;
     break;
-    break;
   case MYSQL_TYPE_DATETIME:
   case MYSQL_TYPE_TIMESTAMP:
     param->skip_result= skip_result_with_length;
@@ -4614,7 +4624,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;
@@ -4698,7 +4708,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;
@@ -4846,6 +4856,49 @@ int STDCALL mysql_next_result(MYSQL *mys
 }
 
 
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
+{
+  MYSQL *mysql= stmt->mysql;
+  int rc;
+  DBUG_ENTER("mysql_stmt_next_result");
+
+  if (!mysql)
+    DBUG_RETURN(1);
+
+  if (stmt->last_errno)
+    DBUG_RETURN(stmt->last_errno);
+
+  if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
+  {
+    if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
+      DBUG_RETURN(1);
+  }
+
+  rc= mysql_next_result(mysql);
+
+  if (rc)
+  {
+    set_stmt_errmsg(stmt, &mysql->net);
+    DBUG_RETURN(rc);
+  }
+
+  stmt->state= MYSQL_STMT_EXECUTE_DONE;
+  stmt->bind_result_done= FALSE;
+
+  if (mysql->field_count)
+  {
+    alloc_stmt_fields(stmt);
+    prepare_to_fetch_result(stmt);
+  }
+  else
+  {
+    stmt->field_count= mysql->field_count;
+  }
+
+  DBUG_RETURN(0);
+}
+
+
 MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql)
 {
   return (*mysql->methods->use_result)(mysql);

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2009-10-09 13:59:25 +0000
+++ b/libmysqld/lib_sql.cc	2009-10-21 12:52:25 +0000
@@ -148,7 +148,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)
@@ -653,7 +653,7 @@ int check_embedded_connection(MYSQL *mys
   strmake(sctx->priv_host, (char*) my_localhost,  MAX_HOSTNAME-1);
   sctx->priv_user= sctx->user= my_strdup(mysql->user, MYF(0));
   result= check_user(thd, COM_CONNECT, NULL, 0, db, true);
-  net_end_statement(thd);
+  thd->protocol->end_statement();
   emb_read_query_result(mysql);
   return result;
 }
@@ -879,7 +879,7 @@ void Protocol_text::remove_last_row()
 }
 
 
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
 {
   List_iterator_fast<Item> it(*list);
   Item                     *item;
@@ -888,7 +888,7 @@ bool Protocol::send_fields(List<Item> *l
   CHARSET_INFO             *thd_cs= thd->variables.character_set_results;
   CHARSET_INFO             *cs= system_charset_info;
   MYSQL_DATA               *data;
-  DBUG_ENTER("send_fields");
+  DBUG_ENTER("send_result_set_metadata");
 
   if (!thd->mysql)            // bootstrap file handling
     DBUG_RETURN(0);
@@ -982,7 +982,7 @@ bool Protocol::send_fields(List<Item> *l
     write_eof_packet(thd, thd->server_status,
                      thd->warning_info->statement_warn_count());
 
-  DBUG_RETURN(prepare_for_send(list));
+  DBUG_RETURN(prepare_for_send(list->elements));
  err:
   my_error(ER_OUT_OF_RESOURCES, MYF(0));        /* purecov: inspected */
   DBUG_RETURN(1);				/* purecov: inspected */

=== modified file 'mysql-test/r/ps.result'
--- a/mysql-test/r/ps.result	2009-10-09 20:01:10 +0000
+++ b/mysql-test/r/ps.result	2009-10-21 12:52:25 +0000
@@ -2926,4 +2926,165 @@ execute stmt;
 Db	Name	Definer	Time zone	Type	Execute at	Interval value	Interval field	Starts	Ends	Status	Originator	character_set_client	collation_connection	Database Collation
 drop table t1;
 deallocate prepare stmt;
+
 End of 5.1 tests.
+
+#
+# WL#4435: Support OUT-parameters in prepared statements.
+#
+
+DROP PROCEDURE IF EXISTS p_string;
+DROP PROCEDURE IF EXISTS p_double;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_decimal;
+
+CREATE PROCEDURE p_string(
+IN v0 INT,
+OUT v1 CHAR(32),
+IN v2 CHAR(32),
+INOUT v3 CHAR(32))
+BEGIN
+SET v0 = -1;
+SET v1 = 'test_v1';
+SET v2 = 'n/a';
+SET v3 = 'test_v3';
+END|
+
+CREATE PROCEDURE p_double(
+IN v0 INT,
+OUT v1 DOUBLE(4, 2),
+IN v2 DOUBLE(4, 2),
+INOUT v3 DOUBLE(4, 2))
+BEGIN
+SET v0 = -1;
+SET v1 = 12.34;
+SET v2 = 98.67;
+SET v3 = 56.78;
+END|
+
+CREATE PROCEDURE p_int(
+IN v0 CHAR(10),
+OUT v1 INT,
+IN v2 INT,
+INOUT v3 INT)
+BEGIN
+SET v0 = 'n/a';
+SET v1 = 1234;
+SET v2 = 9876;
+SET v3 = 5678;
+END|
+
+CREATE PROCEDURE p_decimal(
+IN v0 INT,
+OUT v1 DECIMAL(4, 2),
+IN v2 DECIMAL(4, 2),
+INOUT v3 DECIMAL(4, 2))
+BEGIN
+SET v0 = -1;
+SET v1 = 12.34;
+SET v2 = 98.67;
+SET v3 = 56.78;
+END|
+
+PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)';
+
+SET @x_str_1 = NULL;
+SET @x_str_2 = NULL;
+SET @x_str_3 = NULL;
+SET @x_dbl_1 = NULL;
+SET @x_dbl_2 = NULL;
+SET @x_dbl_3 = NULL;
+SET @x_int_1 = NULL;
+SET @x_int_2 = NULL;
+SET @x_int_3 = NULL;
+SET @x_dec_1 = NULL;
+SET @x_dec_2 = NULL;
+SET @x_dec_3 = NULL;
+
+-- Testing strings...
+
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+@x_int_1	@x_str_1	@x_str_2	@x_str_3
+NULL	test_v1	NULL	test_v3
+
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+@x_int_1	@x_str_1	@x_str_2	@x_str_3
+NULL	test_v1	NULL	test_v3
+
+-- Testing doubles...
+
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+@x_int_1	@x_dbl_1	@x_dbl_2	@x_dbl_3
+NULL	12.34	NULL	56.78
+
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+@x_int_1	@x_dbl_1	@x_dbl_2	@x_dbl_3
+NULL	12.34	NULL	56.78
+
+-- Testing ints...
+
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+@x_str_1	@x_int_1	@x_int_2	@x_int_3
+test_v1	1234	NULL	5678
+
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+@x_str_1	@x_int_1	@x_int_2	@x_int_3
+test_v1	1234	NULL	5678
+
+-- Testing decs...
+
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+@x_int_1	@x_dec_1	@x_dec_2	@x_dec_3
+1234	12.34	NULL	56.78
+
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+@x_int_1	@x_dec_1	@x_dec_2	@x_dec_3
+1234	12.34	NULL	56.78
+
+DEALLOCATE PREPARE stmt_str;
+DEALLOCATE PREPARE stmt_dbl;
+DEALLOCATE PREPARE stmt_int;
+DEALLOCATE PREPARE stmt_dec;
+
+DROP PROCEDURE p_string;
+DROP PROCEDURE p_double;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_decimal;
+
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+
+CREATE PROCEDURE p1(OUT v1 CHAR(10))
+SET v1 = 'test1';
+
+CREATE PROCEDURE p2(OUT v2 CHAR(10))
+BEGIN
+SET @query = 'CALL p1(?)';
+PREPARE stmt1 FROM @query;
+EXECUTE stmt1 USING @u1;
+DEALLOCATE PREPARE stmt1;
+SET v2 = @u1;
+END|
+
+CALL p2(@a);
+SELECT @a;
+@a
+test1
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+# End of WL#4435.
+
+End of 6.0 tests.

=== modified file 'mysql-test/t/ps.test'
--- a/mysql-test/t/ps.test	2009-05-27 15:19:44 +0000
+++ b/mysql-test/t/ps.test	2009-10-21 12:52:25 +0000
@@ -3009,5 +3009,209 @@ execute stmt;
 drop table t1;
 deallocate prepare stmt;
 
+###########################################################################
 
+--echo 
 --echo End of 5.1 tests.
+
+###########################################################################
+
+--echo
+--echo #
+--echo # WL#4435: Support OUT-parameters in prepared statements.
+--echo #
+--echo
+
+# The idea of this test case is to check that
+#   - OUT-parameters of four allowed types (string, double, int, decimal) work
+#     properly;
+#   - INOUT and OUT parameters work properly;
+#   - A mix of IN and OUT parameters work properly;
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p_string;
+DROP PROCEDURE IF EXISTS p_double;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_decimal;
+--enable_warnings
+
+delimiter |;
+
+--echo
+CREATE PROCEDURE p_string(
+  IN v0 INT,
+  OUT v1 CHAR(32),
+  IN v2 CHAR(32),
+  INOUT v3 CHAR(32))
+BEGIN
+  SET v0 = -1;
+  SET v1 = 'test_v1';
+  SET v2 = 'n/a';
+  SET v3 = 'test_v3';
+END|
+
+--echo
+CREATE PROCEDURE p_double(
+  IN v0 INT,
+  OUT v1 DOUBLE(4, 2),
+  IN v2 DOUBLE(4, 2),
+  INOUT v3 DOUBLE(4, 2))
+BEGIN
+  SET v0 = -1;
+  SET v1 = 12.34;
+  SET v2 = 98.67;
+  SET v3 = 56.78;
+END|
+
+--echo
+CREATE PROCEDURE p_int(
+  IN v0 CHAR(10),
+  OUT v1 INT,
+  IN v2 INT,
+  INOUT v3 INT)
+BEGIN
+  SET v0 = 'n/a';
+  SET v1 = 1234;
+  SET v2 = 9876;
+  SET v3 = 5678;
+END|
+
+--echo
+CREATE PROCEDURE p_decimal(
+  IN v0 INT,
+  OUT v1 DECIMAL(4, 2),
+  IN v2 DECIMAL(4, 2),
+  INOUT v3 DECIMAL(4, 2))
+BEGIN
+  SET v0 = -1;
+  SET v1 = 12.34;
+  SET v2 = 98.67;
+  SET v3 = 56.78;
+END|
+
+delimiter ;|
+
+--echo
+PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)';
+
+--echo
+SET @x_str_1 = NULL;
+SET @x_str_2 = NULL;
+SET @x_str_3 = NULL;
+SET @x_dbl_1 = NULL;
+SET @x_dbl_2 = NULL;
+SET @x_dbl_3 = NULL;
+SET @x_int_1 = NULL;
+SET @x_int_2 = NULL;
+SET @x_int_3 = NULL;
+SET @x_dec_1 = NULL;
+SET @x_dec_2 = NULL;
+SET @x_dec_3 = NULL;
+
+--echo
+--echo -- Testing strings...
+
+--echo
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+
+--echo
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+
+--echo
+--echo -- Testing doubles...
+
+--echo
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+
+--echo
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+
+--echo
+--echo -- Testing ints...
+
+--echo
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+
+--echo
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+
+--echo
+--echo -- Testing decs...
+
+--echo
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+
+--echo
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+
+--echo
+DEALLOCATE PREPARE stmt_str;
+DEALLOCATE PREPARE stmt_dbl;
+DEALLOCATE PREPARE stmt_int;
+DEALLOCATE PREPARE stmt_dec;
+
+--echo
+DROP PROCEDURE p_string;
+DROP PROCEDURE p_double;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_decimal;
+
+#
+# Another test case for WL#4435: check out parameters in Dynamic SQL.
+#
+
+--echo
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+--enable_warnings
+
+--echo
+
+CREATE PROCEDURE p1(OUT v1 CHAR(10))
+  SET v1 = 'test1';
+
+--echo
+
+delimiter |;
+CREATE PROCEDURE p2(OUT v2 CHAR(10))
+BEGIN
+  SET @query = 'CALL p1(?)';
+  PREPARE stmt1 FROM @query;
+  EXECUTE stmt1 USING @u1;
+  DEALLOCATE PREPARE stmt1;
+
+  SET v2 = @u1;
+END|
+delimiter ;|
+
+--echo
+
+CALL p2(@a);
+SELECT @a;
+
+--echo
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+--echo
+--echo # End of WL#4435.
+
+###########################################################################
+
+--echo
+--echo End of 6.0 tests.
+
+###########################################################################

=== modified file 'sql-common/client.c'
--- a/sql-common/client.c	2009-10-15 21:11:42 +0000
+++ b/sql-common/client.c	2009-10-21 12:52:25 +0000
@@ -710,10 +710,14 @@ err:
 }
 #endif
 
-/*****************************************************************************
+/**
   Read a packet from server. Give error message if socket was down
   or packet is an error message
-*****************************************************************************/
+
+  @retval  packet_error    An error occurred during reading.
+                           Error message is set.
+  @retval  
+*/
 
 ulong
 cli_safe_read(MYSQL *mysql)
@@ -879,31 +883,132 @@ void free_old_query(MYSQL *mysql)
   DBUG_VOID_RETURN;
 }
 
+
+/**
+  Finish reading of a partial result set from the server.
+  Get the EOF packet, and update mysql->status
+  and mysql->warning_count.
+
+  @return  TRUE if a communication or protocol error, an error
+           is set in this case, FALSE otherwise.
+*/
+
+my_bool flush_one_result(MYSQL *mysql)
+{
+  ulong packet_length;
+
+  DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY);
+
+  do
+  {
+    packet_length= cli_safe_read(mysql);
+    /*
+      There is an error reading from the connection,
+      or (sic!) there were no error and no
+      data in the stream, i.e. no more data from the server.
+      Since we know our position in the stream (somewhere in
+      the middle of a result set), this latter case is an error too
+      -- each result set must end with a EOF packet.
+      cli_safe_read() has set an error for us, just return.
+    */
+    if (packet_length == packet_error)
+      return TRUE;
+  }
+  while (packet_length > 8 || mysql->net.read_pos[0] != 254);
+
+  /* Analyze EOF packet of the result set. */
+
+  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;
+  }
+  return FALSE;
+}
+
+
+/**
+  Read a packet from network. If it's an OK packet, flush it.
+
+  @return  TRUE if error, FALSE otherwise. In case of 
+           success, is_ok_packet is set to TRUE or FALSE,
+           based on what we got from network.
+*/
+
+my_bool opt_flush_ok_packet(MYSQL *mysql, my_bool *is_ok_packet)
+{
+  ulong packet_length= cli_safe_read(mysql);
+
+  if (packet_length == packet_error)
+    return TRUE;
+
+  /* cli_safe_read always reads a non-empty packet. */
+  DBUG_ASSERT(packet_length);
+
+  *is_ok_packet= mysql->net.read_pos[0] == 0;
+  if (*is_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))
+    {
+      mysql->warning_count=uint2korr(pos);
+      pos+=2;
+    }
+  }
+  return FALSE;
+}
+
+
 /*
   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_one_result(mysql))
+    DBUG_VOID_RETURN;                           /* An error occurred */
+
+  if (! flush_all_results)
+    DBUG_VOID_RETURN;
+
+  while (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
   {
-    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 is_ok_packet;
+    if (opt_flush_ok_packet(mysql, &is_ok_packet))
+      DBUG_VOID_RETURN;                         /* An error occurred. */
+    if (is_ok_packet)
     {
-      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;
-      }
-      break;                            /* End of data */
+      /*
+        Indeed what we got from network was an OK packet, and we
+        know that OK is the last one in a multi-result-set, so
+        just return.
+      */
+      DBUG_VOID_RETURN;
     }
+    /*
+      It's a result set, not an OK packet. A result set contains
+      of two result set subsequences: field metadata, terminated
+      with EOF packet, and result set data, again terminated with
+      EOF packet. Read and flush them.
+    */
+    if (flush_one_result(mysql) || flush_one_result(mysql))
+      DBUG_VOID_RETURN;                         /* An error occurred. */
   }
+
   DBUG_VOID_RETURN;
 }
 
@@ -1009,7 +1114,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 'sql/events.cc'
--- a/sql/events.cc	2009-08-29 08:52:22 +0000
+++ b/sql/events.cc	2009-10-21 12:52:25 +0000
@@ -757,7 +757,7 @@ send_show_create_event(THD *thd, Event_t
   field_list.push_back(
     new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/handler.cc'
--- a/sql/handler.cc	2009-10-14 16:37:38 +0000
+++ b/sql/handler.cc	2009-10-21 12:52:25 +0000
@@ -1644,7 +1644,7 @@ bool mysql_xa_recover(THD *thd)
   field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS));
   field_list.push_back(new Item_empty_string("data",XIDDATASIZE));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(1);
 
@@ -4434,7 +4434,7 @@ bool ha_show_status(THD *thd, handlerton
   field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
   field_list.push_back(new Item_empty_string("Status",10));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     return TRUE;
 

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2009-09-17 09:20:11 +0000
+++ b/sql/item.cc	2009-10-21 12:52:25 +0000
@@ -2583,7 +2583,8 @@ Item_param::Item_param(uint pos_in_query
   param_type(MYSQL_TYPE_VARCHAR),
   pos_in_query(pos_in_query_arg),
   set_param_func(default_set_param_func),
-  limit_clause_param(FALSE)
+  limit_clause_param(FALSE),
+  m_out_param_info(NULL)
 {
   name= (char*) "?";
   /* 
@@ -2665,6 +2666,17 @@ void Item_param::set_decimal(const char 
   DBUG_VOID_RETURN;
 }
 
+void Item_param::set_decimal(const my_decimal *dv)
+{
+  state= DECIMAL_VALUE;
+
+  my_decimal2decimal(dv, &decimal_value);
+
+  decimals= (uint8) decimal_value.frac;
+  unsigned_flag= !decimal_value.sign();
+  max_length= my_decimal_precision_to_length(decimal_value.intg + decimals,
+                                             decimals, unsigned_flag);
+}
 
 /**
   Set parameter value from MYSQL_TIME value.
@@ -3287,6 +3299,158 @@ Item_param::set_param_type_and_swap_valu
   str_value_ptr.swap(src->str_value_ptr);
 }
 
+
+/**
+  This operation is intended to store some item value in Item_param to be
+  used later.
+
+  @param thd    thread context
+  @param ctx    stored procedure runtime context
+  @param it     a pointer to an item in the tree
+
+  @return Error status
+    @retval TRUE on error
+    @retval FALSE on success
+*/
+
+bool
+Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+  Item *value= *it;
+
+  if (value->is_null())
+  {
+    set_null();
+    return FALSE;
+  }
+
+  null_value= FALSE;
+
+  switch (value->result_type()) {
+  case STRING_RESULT:
+  {
+    char str_buffer[STRING_BUFFER_USUAL_SIZE];
+    String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin);
+    String *sv= value->val_str(&sv_buffer);
+
+    if (!sv)
+      return TRUE;
+
+    set_str(sv->c_ptr_safe(), sv->length());
+    str_value_ptr.set(str_value.ptr(),
+                      str_value.length(),
+                      str_value.charset());
+    collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+    decimals= 0;
+    param_type= MYSQL_TYPE_STRING;
+
+    break;
+  }
+
+  case REAL_RESULT:
+    set_double(value->val_real());
+      param_type= MYSQL_TYPE_DOUBLE;
+    break;
+
+  case INT_RESULT:
+    set_int(value->val_int(), value->max_length);
+    param_type= MYSQL_TYPE_LONG;
+    break;
+
+  case DECIMAL_RESULT:
+  {
+    my_decimal dv_buf;
+    my_decimal *dv= value->val_decimal(&dv_buf);
+
+    if (!dv)
+      return TRUE;
+
+    set_decimal(dv);
+    param_type= MYSQL_TYPE_NEWDECIMAL;
+
+    break;
+  }
+
+  default:
+    /* That can not happen. */
+
+    DBUG_ASSERT(TRUE);  // Abort in debug mode.
+
+    set_null();         // Set to NULL in release mode.
+    return FALSE;
+  }
+
+  item_result_type= value->result_type();
+  item_type= value->type();
+  return FALSE;
+}
+
+
+/**
+  Setter of Item_param::m_out_param_info.
+
+  m_out_param_info is used to store information about store routine
+  OUT-parameters, such as stored routine name, database, stored routine
+  variable name. It is supposed to be set in sp_head::execute() after
+  Item_param::set_value() is called.
+*/
+
+void
+Item_param::set_out_param_info(Send_field *info)
+{
+  m_out_param_info= info;
+}
+
+
+/**
+  Getter of Item_param::m_out_param_info.
+
+  m_out_param_info is used to store information about store routine
+  OUT-parameters, such as stored routine name, database, stored routine
+  variable name. It is supposed to be retrieved in
+  Protocol_binary::send_out_parameters() during creation of OUT-parameter
+  result set.
+*/
+
+const Send_field *
+Item_param::get_out_param_info() const
+{
+  return m_out_param_info;
+}
+
+
+/**
+  Fill meta-data information for the corresponding column in a result set.
+  If this is an OUT-parameter of a stored procedure, preserve meta-data of
+  stored-routine variable.
+
+  @param field container for meta-data to be filled
+*/
+
+void Item_param::make_field(Send_field *field)
+{
+  Item::make_field(field);
+
+  if (!m_out_param_info)
+    return;
+
+  /*
+    This is an OUT-parameter of stored procedure. We should use
+    OUT-parameter info to fill out the names.
+  */
+
+  field->db_name= m_out_param_info->db_name;
+  field->table_name= m_out_param_info->table_name;
+  field->org_table_name= m_out_param_info->org_table_name;
+  field->col_name= m_out_param_info->col_name;
+  field->org_col_name= m_out_param_info->org_col_name;
+  field->length= m_out_param_info->length;
+  field->charsetnr= m_out_param_info->charsetnr;
+  field->flags= m_out_param_info->flags;
+  field->decimals= m_out_param_info->decimals;
+  field->type= m_out_param_info->type;
+}
+
 /****************************************************************************
   Item_copy
 ****************************************************************************/
@@ -3518,7 +3682,7 @@ void Item_copy_decimal::copy()
 
 
 /*
-  Functions to convert item to field (for send_fields)
+  Functions to convert item to field (for send_result_set_metadata)
 */
 
 /* ARGSUSED */

=== modified file 'sql/item.h'
--- a/sql/item.h	2009-08-28 10:55:59 +0000
+++ b/sql/item.h	2009-10-21 12:52:25 +0000
@@ -450,6 +450,11 @@ public:
       TRUE if error has occured.
   */
   virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+
+  virtual void set_out_param_info(Send_field *info) {}
+
+  virtual const Send_field *get_out_param_info() const
+  { return NULL; }
 };
 
 
@@ -1560,7 +1565,8 @@ public:
 
 /* Item represents one placeholder ('?') of prepared statement */
 
-class Item_param :public Item
+class Item_param :public Item,
+                  private Settable_routine_parameter
 {
   char cnvbuf[MAX_FIELD_WIDTH];
   String cnvstr;
@@ -1648,6 +1654,7 @@ public:
   void set_int(longlong i, uint32 max_length_arg);
   void set_double(double i);
   void set_decimal(const char *str, ulong length);
+  void set_decimal(const my_decimal *dv);
   bool set_str(const char *str, ulong length);
   bool set_longdata(const char *str, ulong length);
   void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
@@ -1697,6 +1704,25 @@ public:
   /** Item is a argument to a limit clause. */
   bool limit_clause_param;
   void set_param_type_and_swap_value(Item_param *from);
+
+private:
+  virtual inline Settable_routine_parameter *
+    get_settable_routine_parameter()
+  {
+    return this;
+  }
+
+  virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+
+  virtual void set_out_param_info(Send_field *info);
+
+public:
+  virtual const Send_field *get_out_param_info() const;
+
+  virtual void make_field(Send_field *field);
+
+private:
+  Send_field *m_out_param_info;
 };
 
 
@@ -2054,7 +2080,7 @@ public:
 
 /**
   Item_empty_string -- is a utility class to put an item into List<Item>
-  which is then used in protocol.send_fields() when sending SHOW output to
+  which is then used in protocol.send_result_set_metadata() when sending SHOW output to
   the client.
 */
 

=== modified file 'sql/procedure.h'
--- a/sql/procedure.h	2008-03-21 15:48:28 +0000
+++ b/sql/procedure.h	2009-10-21 12:52:25 +0000
@@ -23,7 +23,7 @@
 #define PROC_NO_SORT 1				/**< Bits in flags */
 #define PROC_GROUP   2				/**< proc must have group */
 
-/* Procedure items used by procedures to store values for send_fields */
+/* Procedure items used by procedures to store values for send_result_set_metadata */
 
 class Item_proc :public Item
 {

=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc	2009-10-09 09:06:41 +0000
+++ b/sql/protocol.cc	2009-10-21 12:52:25 +0000
@@ -28,6 +28,7 @@
 #include <stdarg.h>
 
 static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
+/* Declared non-static only because of the embedded library. */
 bool net_send_error_packet(THD *, uint, const char *, const char *);
 /* Declared non-static only because of the embedded library. */
 bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *);
@@ -141,6 +142,7 @@ bool Protocol::net_store_data(const ucha
 bool net_send_error(THD *thd, uint sql_errno, const char *err,
                     const char* sqlstate)
 {
+  bool error;
   DBUG_ENTER("net_send_error");
 
   DBUG_ASSERT(!thd->spcont);
@@ -148,7 +150,6 @@ bool net_send_error(THD *thd, uint sql_e
   DBUG_ASSERT(err);
 
   DBUG_PRINT("enter",("sql_errno: %d  err: %s", sql_errno, err));
-  bool error;
 
   if (sqlstate == NULL)
     sqlstate= mysql_errno_to_sqlstate(sql_errno);
@@ -470,6 +471,12 @@ static uchar *net_store_length_fast(ucha
   packet is "buffered" in the diagnostics area and sent to the client
   in the end of statement.
 
+  @note This method defines a template, but delegates actual 
+  sending of data to virtual Protocol::send_{ok,eof,error}. This
+  allows for implementation of protocols that "intercept" ok/eof/error
+  messages, and store them in memory, etc, instead of sending to
+  the client.
+
   @pre  The diagnostics area is assigned or disabled. It can not be empty
         -- we assume that every SQL statement or COM_* command
         generates OK, ERROR, or EOF status.
@@ -484,47 +491,94 @@ static uchar *net_store_length_fast(ucha
           Diagnostics_area::is_sent is set for debugging purposes only.
 */
 
-void net_end_statement(THD *thd)
+void Protocol::end_statement()
 {
+  DBUG_ENTER("Protocol::end_statement");
   DBUG_ASSERT(! thd->stmt_da->is_sent);
+  bool error= FALSE;
 
   /* Can not be true, but do not take chances in production. */
   if (thd->stmt_da->is_sent)
-    return;
-
-  bool error= FALSE;
+    DBUG_VOID_RETURN;
 
   switch (thd->stmt_da->status()) {
   case Diagnostics_area::DA_ERROR:
     /* The query failed, send error to log and abort bootstrap. */
-    error= net_send_error(thd,
-                          thd->stmt_da->sql_errno(),
-                          thd->stmt_da->message(),
-                          thd->stmt_da->get_sqlstate());
+    error= send_error(thd->stmt_da->sql_errno(),
+                      thd->stmt_da->message(),
+                      thd->stmt_da->get_sqlstate());
     break;
   case Diagnostics_area::DA_EOF:
-    error= net_send_eof(thd,
-                        thd->stmt_da->server_status(),
-                        thd->stmt_da->statement_warn_count());
+    error= send_eof(thd->stmt_da->server_status(),
+                    thd->stmt_da->statement_warn_count());
     break;
   case Diagnostics_area::DA_OK:
-    error= net_send_ok(thd,
-                       thd->stmt_da->server_status(),
-                       thd->stmt_da->statement_warn_count(),
-                       thd->stmt_da->affected_rows(),
-                       thd->stmt_da->last_insert_id(),
-                       thd->stmt_da->message());
+    error= send_ok(thd->stmt_da->server_status(),
+                   thd->stmt_da->statement_warn_count(),
+                   thd->stmt_da->affected_rows(),
+                   thd->stmt_da->last_insert_id(),
+                   thd->stmt_da->message());
     break;
   case Diagnostics_area::DA_DISABLED:
     break;
   case Diagnostics_area::DA_EMPTY:
   default:
     DBUG_ASSERT(0);
-    error= net_send_ok(thd, thd->server_status, 0, 0, 0, NULL);
+    error= send_ok(thd->server_status, 0, 0, 0, NULL);
     break;
   }
   if (!error)
     thd->stmt_da->is_sent= TRUE;
+  DBUG_VOID_RETURN;
+}
+
+
+/**
+  A default implementation of "OK" packet response to the client.
+
+  Currently this implementation is re-used by both network-oriented
+  protocols -- the binary and text one. They do not differ
+  in their OK packet format, which allows for a significant simplification
+  on client side.
+*/
+
+bool Protocol::send_ok(uint server_status, uint statement_warn_count,
+                       ulonglong affected_rows, ulonglong last_insert_id,
+                       const char *message)
+{
+  DBUG_ENTER("Protocol::send_ok");
+
+  DBUG_RETURN(net_send_ok(thd, server_status, statement_warn_count,
+                          affected_rows, last_insert_id, message));
+}
+
+
+/**
+  A default implementation of "EOF" packet response to the client.
+
+  Binary and text protocol do not differ in their EOF packet format.
+*/
+
+bool Protocol::send_eof(uint server_status, uint statement_warn_count)
+{
+  DBUG_ENTER("Protocol::send_eof");
+
+  DBUG_RETURN(net_send_eof(thd, server_status, statement_warn_count));
+}
+
+
+/**
+  A default implementation of "ERROR" packet response to the client.
+
+  Binary and text protocol do not differ in ERROR packet format.
+*/
+
+bool Protocol::send_error(uint sql_errno, const char *err_msg,
+                          const char *sql_state)
+{
+  DBUG_ENTER("Protocol::send_error");
+
+  DBUG_RETURN(net_send_error_packet(thd, sql_errno, err_msg, sql_state));
 }
 
 
@@ -581,9 +635,10 @@ void Protocol::init(THD *thd_arg)
   for the error.
 */
 
-void Protocol::end_partial_result_set(THD *thd)
+void Protocol::end_partial_result_set(THD *thd_arg)
 {
-  net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */);
+  net_send_eof(thd_arg, thd_arg->server_status,
+               0 /* no warnings, we're inside SP */);
 }
 
 
@@ -616,16 +671,16 @@ bool Protocol::flush()
     1	Error  (Note that in this case the error is not sent to the
     client)
 */
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
 {
   List_iterator_fast<Item> it(*list);
   Item *item;
-  uchar buff[80];
+  uchar buff[MAX_FIELD_WIDTH];
   String tmp((char*) buff,sizeof(buff),&my_charset_bin);
   Protocol_text prot(thd);
   String *local_packet= prot.storage_packet();
   CHARSET_INFO *thd_charset= thd->variables.character_set_results;
-  DBUG_ENTER("send_fields");
+  DBUG_ENTER("send_result_set_metadata");
 
   if (flags & SEND_NUM_ROWS)
   {				// Packet with number of elements
@@ -770,7 +825,7 @@ bool Protocol::send_fields(List<Item> *l
     write_eof_packet(thd, &thd->net, thd->server_status,
                      thd->warning_info->statement_warn_count());
   }
-  DBUG_RETURN(prepare_for_send(list));
+  DBUG_RETURN(prepare_for_send(list->elements));
 
 err:
   my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES),
@@ -789,6 +844,47 @@ bool Protocol::write()
 
 
 /**
+  Send one result set row.
+
+  @param row_items a collection of column values for that row
+
+  @return Error status.
+    @retval TRUE  Error.
+    @retval FALSE Success.
+*/
+
+bool Protocol::send_result_set_row(List<Item> *row_items)
+{
+  char buffer[MAX_FIELD_WIDTH];
+  String str_buffer(buffer, sizeof (buffer), &my_charset_bin);
+  List_iterator_fast<Item> it(*row_items);
+
+  DBUG_ENTER("Protocol::send_result_set_row");
+
+  for (Item *item= it++; item; item= it++)
+  {
+    if (item->send(this, &str_buffer))
+    {
+      // If we're out of memory, reclaim some, to help us recover.
+      this->free();
+      DBUG_RETURN(TRUE);
+    }
+    /* Item::send() may generate an error. If so, abort the loop. */
+    if (thd->is_error())
+      DBUG_RETURN(TRUE);
+
+    /*
+      Reset str_buffer to its original state, as it may have been altered in
+      Item::send().
+    */
+    str_buffer.set(buffer, sizeof(buffer), &my_charset_bin);
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
+/**
   Send \\0 end terminated string.
 
   @param from	NullS or \\0 terminated string
@@ -834,7 +930,6 @@ bool Protocol::store(I_List<i_string>* s
   return store((char*) tmp.ptr(), len,  tmp.charset());
 }
 
-
 /****************************************************************************
   Functions to handle the simple (default) protocol where everything is
   This protocol is the one that is used by default between the MySQL server
@@ -1113,6 +1208,53 @@ bool Protocol_text::store_time(MYSQL_TIM
   return net_store_data((uchar*) buff, length);
 }
 
+/**
+  Assign OUT-parameters to user variables.
+
+  @param sp_params  List of PS/SP parameters (both input and output).
+
+  @return Error status.
+    @retval FALSE Success.
+    @retval TRUE  Error.
+*/
+
+bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
+{
+  DBUG_ASSERT(sp_params->elements ==
+              thd->lex->prepared_stmt_params.elements);
+
+  List_iterator_fast<Item_param> item_param_it(*sp_params);
+  List_iterator_fast<LEX_STRING> user_var_name_it(thd->lex->prepared_stmt_params);
+
+  while (true)
+  {
+    Item_param *item_param= item_param_it++;
+    LEX_STRING *user_var_name= user_var_name_it++;
+
+    if (!item_param || !user_var_name)
+      break;
+
+    if (!item_param->get_out_param_info())
+      continue; // It's an IN-parameter.
+
+    Item_func_set_user_var *suv=
+      new Item_func_set_user_var(*user_var_name, item_param);
+    /*
+      Item_func_set_user_var is not fixed after construction, call
+      fix_fields().
+    */
+    if (suv->fix_fields(thd, NULL))
+      return TRUE;
+
+    if (suv->check(FALSE))
+      return TRUE;
+
+    if (suv->update())
+      return TRUE;
+  }
+
+  return FALSE;
+}
 
 /****************************************************************************
   Functions to handle the binary protocol used with prepared statements
@@ -1133,14 +1275,13 @@ bool Protocol_text::store_time(MYSQL_TIM
    [..]..[[length]data]              data
 ****************************************************************************/
 
-bool Protocol_binary::prepare_for_send(List<Item> *item_list)
+bool Protocol_binary::prepare_for_send(uint num_columns)
 {
-  Protocol::prepare_for_send(item_list);
+  Protocol::prepare_for_send(num_columns);
   bit_fields= (field_count+9)/8;
-  if (packet->alloc(bit_fields+1))
-    return 1;
+  return packet->alloc(bit_fields+1);
+
   /* prepare_for_resend will be called after this one */
-  return 0;
 }
 
 
@@ -1328,3 +1469,80 @@ bool Protocol_binary::store_time(MYSQL_T
   buff[0]=(char) length;			// Length is stored first
   return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
 }
+
+/**
+  Send a result set with OUT-parameter values by means of PS-protocol.
+
+  @param sp_params  List of PS/SP parameters (both input and output).
+
+  @return Error status.
+    @retval FALSE Success.
+    @retval TRUE  Error.
+*/
+
+bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
+{
+  if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
+  {
+    /* The client does not support OUT-parameters. */
+    return FALSE;
+  }
+
+  List<Item> out_param_lst;
+
+  {
+    List_iterator_fast<Item_param> item_param_it(*sp_params);
+
+    while (true)
+    {
+      Item_param *item_param= item_param_it++;
+
+      if (!item_param)
+        break;
+
+      if (!item_param->get_out_param_info())
+        continue; // It's an IN-parameter.
+
+      if (out_param_lst.push_back(item_param))
+        return TRUE;
+    }
+  }
+
+  if (!out_param_lst.elements)
+    return FALSE;
+
+  /*
+    We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it
+    is used in send_result_set_metadata().
+  */
+
+  thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
+
+  /* Send meta-data. */
+  if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+    return TRUE;
+
+  /* Send data. */
+
+  prepare_for_resend();
+
+  if (send_result_set_row(&out_param_lst))
+    return TRUE;
+
+  if (write())
+    return TRUE;
+
+  /* Restore THD::server_status. */
+  thd->server_status&= ~SERVER_PS_OUT_PARAMS;
+
+  /*
+    Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
+    for sure.
+  */
+  thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+
+  /* Send EOF-packet. */
+  net_send_eof(thd, thd->server_status, 0);
+
+  return FALSE;
+}

=== modified file 'sql/protocol.h'
--- a/sql/protocol.h	2009-09-30 05:09:28 +0000
+++ b/sql/protocol.h	2009-10-21 12:52:25 +0000
@@ -21,6 +21,7 @@
 
 class i_string;
 class THD;
+class Item_param;
 typedef struct st_mysql_field MYSQL_FIELD;
 typedef struct st_mysql_rows MYSQL_ROWS;
 
@@ -47,6 +48,16 @@ protected:
                       CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
   bool store_string_aux(const char *from, size_t length,
                         CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
+
+  virtual bool send_ok(uint server_status, uint statement_warn_count,
+                       ulonglong affected_rows, ulonglong last_insert_id,
+                       const char *message);
+
+  virtual bool send_eof(uint server_status, uint statement_warn_count);
+
+  virtual bool send_error(uint sql_errno, const char *err_msg,
+                          const char *sql_state);
+
 public:
   Protocol() {}
   Protocol(THD *thd_arg) { init(thd_arg); }
@@ -54,7 +65,8 @@ public:
   void init(THD* thd_arg);
 
   enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
-  virtual bool send_fields(List<Item> *list, uint flags);
+  virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+  bool send_result_set_row(List<Item> *row_items);
 
   bool store(I_List<i_string> *str_list);
   bool store(const char *from, CHARSET_INFO *cs);
@@ -72,9 +84,9 @@ public:
   inline bool store(String *str)
   { return store((char*) str->ptr(), str->length(), str->charset()); }
 
-  virtual bool prepare_for_send(List<Item> *item_list) 
+  virtual bool prepare_for_send(uint num_columns)
   {
-    field_count=item_list->elements;
+    field_count= num_columns;
     return 0;
   }
   virtual bool flush();
@@ -96,6 +108,8 @@ public:
   virtual bool store_date(MYSQL_TIME *time)=0;
   virtual bool store_time(MYSQL_TIME *time)=0;
   virtual bool store(Field *field)=0;
+
+  virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
 #ifdef EMBEDDED_LIBRARY
   int begin_dataset();
   virtual void remove_last_row() {}
@@ -111,6 +125,8 @@ public:
     */
   };
   virtual enum enum_protocol_type type()= 0;
+
+  void end_statement();
 };
 
 
@@ -137,6 +153,8 @@ public:
   virtual bool store(float nr, uint32 decimals, String *buffer);
   virtual bool store(double from, uint32 decimals, String *buffer);
   virtual bool store(Field *field);
+
+  virtual bool send_out_parameters(List<Item_param> *sp_params);
 #ifdef EMBEDDED_LIBRARY
   void remove_last_row();
 #endif
@@ -151,7 +169,7 @@ private:
 public:
   Protocol_binary() {}
   Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
-  virtual bool prepare_for_send(List<Item> *item_list);
+  virtual bool prepare_for_send(uint num_columns);
   virtual void prepare_for_resend();
 #ifdef EMBEDDED_LIBRARY
   virtual bool write();
@@ -172,13 +190,15 @@ public:
   virtual bool store(float nr, uint32 decimals, String *buffer);
   virtual bool store(double from, uint32 decimals, String *buffer);
   virtual bool store(Field *field);
+
+  virtual bool send_out_parameters(List<Item_param> *sp_params);
+
   virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
 };
 
 void send_warning(THD *thd, uint sql_errno, const char *err=0);
 bool net_send_error(THD *thd, uint sql_errno, const char *err,
                     const char* sqlstate);
-void net_end_statement(THD *thd);
 bool send_old_password_request(THD *thd);
 uchar *net_store_data(uchar *to,const uchar *from, size_t length);
 uchar *net_store_data(uchar *to,int32 from);

=== modified file 'sql/repl_failsafe.cc'
--- a/sql/repl_failsafe.cc	2009-10-14 16:37:38 +0000
+++ b/sql/repl_failsafe.cc	2009-10-21 12:52:25 +0000
@@ -471,7 +471,7 @@ bool show_new_master(THD* thd)
     field_list.push_back(new Item_empty_string("Log_name", 20));
     field_list.push_back(new Item_return_int("Log_pos", 10,
 					     MYSQL_TYPE_LONGLONG));
-    if (protocol->send_fields(&field_list,
+    if (protocol->send_result_set_metadata(&field_list,
                               Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
       DBUG_RETURN(TRUE);
     protocol->prepare_for_resend();
@@ -675,7 +675,7 @@ bool show_slave_hosts(THD* thd)
   field_list.push_back(new Item_return_int("Master_id", 10,
 					   MYSQL_TYPE_LONG));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/slave.cc'
--- a/sql/slave.cc	2009-09-10 09:18:29 +0000
+++ b/sql/slave.cc	2009-10-21 12:52:25 +0000
@@ -1605,7 +1605,7 @@ bool show_master_info(THD* thd, Master_i
   field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG));
   field_list.push_back(new Item_empty_string("Last_SQL_Error", 20));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2009-10-16 14:37:43 +0000
+++ b/sql/sp_head.cc	2009-10-21 12:52:25 +0000
@@ -2043,6 +2043,16 @@ sp_head::execute_procedure(THD *thd, Lis
         err_status= TRUE;
         break;
       }
+
+      Send_field *out_param_info= new Send_field();
+      nctx->get_item(i)->make_field(out_param_info);
+      out_param_info->db_name= m_db.str;
+      out_param_info->table_name= m_name.str;
+      out_param_info->org_table_name= m_name.str;
+      out_param_info->col_name= spvar->name.str;
+      out_param_info->org_col_name= spvar->name.str;
+
+      srp->set_out_param_info(out_param_info);
     }
   }
 
@@ -2450,7 +2460,7 @@ sp_head::show_create_routine(THD *thd, i
   fields.push_back(new Item_empty_string("Database Collation",
                                          MY_CS_NAME_SIZE));
 
-  if (protocol->send_fields(&fields,
+  if (protocol->send_result_set_metadata(&fields,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
   {
     DBUG_RETURN(TRUE);
@@ -2636,8 +2646,8 @@ sp_head::show_routine_code(THD *thd)
   field_list.push_back(new Item_uint("Pos", 9));
   // 1024 is for not to confuse old clients
   field_list.push_back(new Item_empty_string("Instruction",
-					     max(buffer.length(), 1024)));
-  if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+                                             max(buffer.length(), 1024)));
+  if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
                                          Protocol::SEND_EOF))
     DBUG_RETURN(1);
 
@@ -2869,7 +2879,7 @@ sp_instr_stmt::execute(THD *thd, uint *n
       res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
 
       if (thd->stmt_da->is_eof())
-        net_end_statement(thd);
+        thd->protocol->end_statement();
 
       query_cache_end_of_result(thd);
 

=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc	2009-10-14 16:37:38 +0000
+++ b/sql/sql_acl.cc	2009-10-21 12:52:25 +0000
@@ -4532,7 +4532,7 @@ bool mysql_show_grants(THD *thd,LEX_USER
   strxmov(buff,"Grants for ",lex_user->user.str,"@",
 	  lex_user->host.str,NullS);
   field_list.push_back(field);
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
   {
     VOID(pthread_mutex_unlock(&acl_cache->lock));

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2009-10-14 16:37:38 +0000
+++ b/sql/sql_class.cc	2009-10-21 12:52:25 +0000
@@ -1493,8 +1493,8 @@ int THD::send_explain_fields(select_resu
   }
   item->maybe_null= 1;
   field_list.push_back(new Item_empty_string("Extra", 255, cs));
-  return (result->send_fields(field_list,
-                              Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
+  return (result->send_result_set_metadata(field_list,
+                                           Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
 }
 
 #ifdef SIGNAL_WITH_VIO_CLOSE
@@ -1618,10 +1618,10 @@ bool sql_exchange::escaped_given(void)
 }
 
 
-bool select_send::send_fields(List<Item> &list, uint flags)
+bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
 {
   bool res;
-  if (!(res= thd->protocol->send_fields(&list, flags)))
+  if (!(res= thd->protocol->send_result_set_metadata(&list, flags)))
     is_result_set_started= 1;
   return res;
 }
@@ -1662,10 +1662,13 @@ void select_send::cleanup()
 
 bool select_send::send_data(List<Item> &items)
 {
+  Protocol *protocol= thd->protocol;
+  DBUG_ENTER("select_send::send_data");
+
   if (unit->offset_limit_cnt)
   {						// using limit offset,count
     unit->offset_limit_cnt--;
-    return 0;
+    DBUG_RETURN(FALSE);
   }
 
   /*
@@ -1675,36 +1678,18 @@ bool select_send::send_data(List<Item> &
   */
   ha_release_temporary_latches(thd);
 
-  List_iterator_fast<Item> li(items);
-  Protocol *protocol= thd->protocol;
-  char buff[MAX_FIELD_WIDTH];
-  String buffer(buff, sizeof(buff), &my_charset_bin);
-  DBUG_ENTER("select_send::send_data");
-
   protocol->prepare_for_resend();
-  Item *item;
-  while ((item=li++))
-  {
-    if (item->send(protocol, &buffer))
-    {
-      protocol->free();				// Free used buffer
-      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
-      break;
-    }
-    /*
-      Reset buffer to its original state, as it may have been altered in
-      Item::send().
-    */
-    buffer.set(buff, sizeof(buff), &my_charset_bin);
-  }
-  thd->sent_row_count++;
-  if (thd->is_error())
+  if (protocol->send_result_set_row(&items))
   {
     protocol->remove_last_row();
-    DBUG_RETURN(1);
+    DBUG_RETURN(TRUE);
   }
+
+  thd->sent_row_count++;
+
   if (thd->vio_ok())
     DBUG_RETURN(protocol->write());
+
   DBUG_RETURN(0);
 }
 

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2009-10-16 11:39:57 +0000
+++ b/sql/sql_class.h	2009-10-21 12:52:25 +0000
@@ -2441,7 +2441,7 @@ public:
   */
   virtual uint field_count(List<Item> &fields) const
   { return fields.elements; }
-  virtual bool send_fields(List<Item> &list, uint flags)=0;
+  virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
   virtual bool send_data(List<Item> &items)=0;
   virtual bool initialize_tables (JOIN *join=0) { return 0; }
   virtual void send_error(uint errcode,const char *err);
@@ -2486,7 +2486,7 @@ class select_result_interceptor: public 
 public:
   select_result_interceptor() {}              /* Remove gcc warning */
   uint field_count(List<Item> &fields) const { return 0; }
-  bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
+  bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE; }
 };
 
 
@@ -2499,7 +2499,7 @@ class select_send :public select_result 
   bool is_result_set_started;
 public:
   select_send() :is_result_set_started(FALSE) {}
-  bool send_fields(List<Item> &list, uint flags);
+  bool send_result_set_metadata(List<Item> &list, uint flags);
   bool send_data(List<Item> &items);
   bool send_eof();
   virtual bool check_simple_select() const { return FALSE; }

=== modified file 'sql/sql_connect.cc'
--- a/sql/sql_connect.cc	2009-10-14 16:37:38 +0000
+++ b/sql/sql_connect.cc	2009-10-21 12:52:25 +0000
@@ -957,7 +957,7 @@ static bool login_connection(THD *thd)
   my_net_set_write_timeout(net, connect_timeout);
 
   error= check_connection(thd);
-  net_end_statement(thd);
+  thd->protocol->end_statement();
 
   if (error)
   {						// Wrong permissions

=== modified file 'sql/sql_cursor.cc'
--- a/sql/sql_cursor.cc	2009-02-17 12:24:09 +0000
+++ b/sql/sql_cursor.cc	2009-10-21 12:52:25 +0000
@@ -90,7 +90,7 @@ class Materialized_cursor: public Server
 public:
   Materialized_cursor(select_result *result, TABLE *table);
 
-  int fill_item_list(THD *thd, List<Item> &send_fields);
+  int fill_item_list(THD *thd, List<Item> &send_result_set_metadata);
   virtual bool is_open() const { return table != 0; }
   virtual int open(JOIN *join __attribute__((unused)));
   virtual void fetch(ulong num_rows);
@@ -115,7 +115,7 @@ public:
   Materialized_cursor *materialized_cursor;
   Select_materialize(select_result *result_arg)
     :result(result_arg), materialized_cursor(0) {}
-  virtual bool send_fields(List<Item> &list, uint flags);
+  virtual bool send_result_set_metadata(List<Item> &list, uint flags);
 };
 
 
@@ -376,12 +376,12 @@ Sensitive_cursor::open(JOIN *join_arg)
   join->change_result(result);
   /*
     Send fields description to the client; server_status is sent
-    in 'EOF' packet, which follows send_fields().
-    We don't simply use SEND_EOF flag of send_fields because we also
+    in 'EOF' packet, which follows send_result_set_metadata().
+    We don't simply use SEND_EOF flag of send_result_set_metadata because we also
     want to flush the network buffer, which is done only in a standalone
     send_eof().
   */
-  result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+  result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS);
   thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
   result->send_eof();
   thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -566,14 +566,14 @@ Materialized_cursor::Materialized_cursor
   Preserve the original metadata that would be sent to the client.
 
   @param thd Thread identifier.
-  @param send_fields List of fields that would be sent.
+  @param send_result_set_metadata List of fields that would be sent.
 */
 
-int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
+int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_result_set_metadata)
 {
   Query_arena backup_arena;
   int rc;
-  List_iterator_fast<Item> it_org(send_fields);
+  List_iterator_fast<Item> it_org(send_result_set_metadata);
   List_iterator_fast<Item> it_dst(item_list);
   Item *item_org;
   Item *item_dst;
@@ -583,7 +583,7 @@ int Materialized_cursor::fill_item_list(
   if ((rc= table->fill_item_list(&item_list)))
     goto end;
 
-  DBUG_ASSERT(send_fields.elements == item_list.elements);
+  DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements);
 
   /*
     Unless we preserve the original metadata, it will be lost,
@@ -623,17 +623,17 @@ int Materialized_cursor::open(JOIN *join
   {
     /*
       Now send the result set metadata to the client. We need to
-      do it here, as in Select_materialize::send_fields the items
-      for column types are not yet created (send_fields requires
+      do it here, as in Select_materialize::send_result_set_metadata the items
+      for column types are not yet created (send_result_set_metadata requires
       a list of items). The new types may differ from the original
       ones sent at prepare if some of them were altered by MySQL
       HEAP tables mechanism -- used when create_tmp_field_from_item
       may alter the original column type.
 
-      We can't simply supply SEND_EOF flag to send_fields, because
-      send_fields doesn't flush the network buffer.
+      We can't simply supply SEND_EOF flag to send_result_set_metadata, because
+      send_result_set_metadata doesn't flush the network buffer.
     */
-    rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS);
+    rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
     thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
     result->send_eof();
     thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -717,7 +717,7 @@ Materialized_cursor::~Materialized_curso
  Select_materialize
 ****************************************************************************/
 
-bool Select_materialize::send_fields(List<Item> &list, uint flags)
+bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
 {
   DBUG_ASSERT(table == 0);
   if (create_result_table(unit->thd, unit->get_unit_column_types(),

=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc	2009-09-10 09:18:29 +0000
+++ b/sql/sql_error.cc	2009-10-21 12:52:25 +0000
@@ -672,7 +672,7 @@ bool mysqld_show_warnings(THD *thd, ulon
   field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
   field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
 
-  if (thd->protocol->send_fields(&field_list,
+  if (thd->protocol->send_result_set_metadata(&field_list,
                                  Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc	2009-10-14 16:37:38 +0000
+++ b/sql/sql_handler.cc	2009-10-21 12:52:25 +0000
@@ -548,7 +548,7 @@ retry:
                     tables->db, tables->alias, &it, 0))
     goto err;
 
-  protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+  protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
 
   /*
     In ::external_lock InnoDB resets the fields which tell it that
@@ -670,18 +670,11 @@ retry:
       continue;
     if (num_rows >= offset_limit_cnt)
     {
-      Item *item;
       protocol->prepare_for_resend();
-      it.rewind();
-      while ((item=it++))
-      {
-	if (item->send(thd->protocol, &buffer))
-	{
-	  protocol->free();                             // Free used
-	  my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
-	  goto err;
-	}
-      }
+
+      if (protocol->send_result_set_row(&list))
+        goto err;
+
       protocol->write();
     }
     num_rows++;

=== modified file 'sql/sql_help.cc'
--- a/sql/sql_help.cc	2009-06-17 14:56:44 +0000
+++ b/sql/sql_help.cc	2009-10-21 12:52:25 +0000
@@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, St
   field_list.push_back(new Item_empty_string("description",1000));
   field_list.push_back(new Item_empty_string("example",1000));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(1);
 
@@ -463,7 +463,7 @@ int send_answer_1(Protocol *protocol, St
    +-                    -+
 
   RETURN VALUES
-    result of protocol->send_fields
+    result of protocol->send_result_set_metadata
 */
 
 int send_header_2(Protocol *protocol, bool for_category)
@@ -474,7 +474,7 @@ int send_header_2(Protocol *protocol, bo
     field_list.push_back(new Item_empty_string("source_category_name",64));
   field_list.push_back(new Item_empty_string("name",64));
   field_list.push_back(new Item_empty_string("is_it_category",1));
-  DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+  DBUG_RETURN(protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
                                                  Protocol::SEND_EOF));
 }
 

=== modified file 'sql/sql_list.cc'
--- a/sql/sql_list.cc	2007-05-28 11:30:01 +0000
+++ b/sql/sql_list.cc	2009-10-21 12:52:25 +0000
@@ -18,7 +18,7 @@
 #pragma implementation				// gcc: Class implementation
 #endif
 
-#include "mysql_priv.h"
+#include "sql_list.h"
 
 list_node end_of_list;
 

=== modified file 'sql/sql_list.h'
--- a/sql/sql_list.h	2009-06-10 08:59:49 +0000
+++ b/sql/sql_list.h	2009-10-21 12:52:25 +0000
@@ -15,11 +15,16 @@
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 
+#include "my_global.h"
+#include "my_sys.h"
+
 
 #ifdef USE_PRAGMA_INTERFACE
 #pragma interface			/* gcc class implementation */
 #endif
 
+void *sql_alloc(size_t);
+
 /* mysql standard class memory allocator */
 
 class Sql_alloc

=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc	2009-10-13 18:18:48 +0000
+++ b/sql/sql_parse.cc	2009-10-21 12:52:25 +0000
@@ -453,7 +453,7 @@ static void handle_bootstrap_impl(THD *t
       /* purecov: begin tested */
       if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
       {
-        net_end_statement(thd);
+        thd->protocol->end_statement();
         bootstrap_error= 1;
         break;
       }
@@ -494,7 +494,7 @@ static void handle_bootstrap_impl(THD *t
     close_thread_tables(thd);			// Free tables
 
     bootstrap_error= thd->is_error();
-    net_end_statement(thd);
+    thd->protocol->end_statement();
 
 #if defined(ENABLED_PROFILING)
     thd->profiling.finish_current_query();
@@ -819,7 +819,7 @@ bool do_command(THD *thd)
 
     /* The error must be set. */
     DBUG_ASSERT(thd->is_error());
-    net_end_statement(thd);
+    thd->protocol->end_statement();
 
     if (net->error != 3)
     {
@@ -1236,7 +1236,7 @@ bool dispatch_command(enum enum_server_c
     {
       char *beginning_of_next_stmt= (char*) end_of_stmt;
 
-      net_end_statement(thd);
+      thd->protocol->end_statement();
       query_cache_end_of_result(thd);
       /*
         Multiple queries exits, execute them individually
@@ -1609,7 +1609,7 @@ bool dispatch_command(enum enum_server_c
 
   thd->transaction.stmt.reset();
 
-  net_end_statement(thd);
+  thd->protocol->end_statement();
   query_cache_end_of_result(thd);
 
   thd->proc_info= "closing tables";

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2009-10-14 16:37:38 +0000
+++ b/sql/sql_prepare.cc	2009-10-21 12:52:25 +0000
@@ -106,7 +106,7 @@ class Select_fetch_protocol_binary: publ
   Protocol_binary protocol;
 public:
   Select_fetch_protocol_binary(THD *thd);
-  virtual bool send_fields(List<Item> &list, uint flags);
+  virtual bool send_result_set_metadata(List<Item> &list, uint flags);
   virtual bool send_data(List<Item> &items);
   virtual bool send_eof();
 #ifdef EMBEDDED_LIBRARY
@@ -260,7 +260,7 @@ static bool send_prep_stmt(Prepared_stat
   error= my_net_write(net, buff, sizeof(buff));
   if (stmt->param_count && ! error)
   {
-    error= thd->protocol_text.send_fields((List<Item> *)
+    error= thd->protocol_text.send_result_set_metadata((List<Item> *)
                                           &stmt->lex->param_list,
                                           Protocol::SEND_EOF);
   }
@@ -1374,7 +1374,7 @@ static int mysql_test_select(Prepared_st
       unit->prepare call above.
     */
     if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
-        lex->result->send_fields(fields, Protocol::SEND_EOF) ||
+        lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) ||
         thd->protocol->flush())
       goto error;
     DBUG_RETURN(2);
@@ -2787,19 +2787,19 @@ Select_fetch_protocol_binary::Select_fet
   :protocol(thd_arg)
 {}
 
-bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
+bool Select_fetch_protocol_binary::send_result_set_metadata(List<Item> &list, uint flags)
 {
   bool rc;
   Protocol *save_protocol= thd->protocol;
 
   /*
-    Protocol::send_fields caches the information about column types:
+    Protocol::send_result_set_metadata caches the information about column types:
     this information is later used to send data. Therefore, the same
     dedicated Protocol object must be used for all operations with
     a cursor.
   */
   thd->protocol= &protocol;
-  rc= select_send::send_fields(list, flags);
+  rc= select_send::send_result_set_metadata(list, flags);
   thd->protocol= save_protocol;
 
   return rc;
@@ -3637,6 +3637,14 @@ bool Prepared_statement::execute(String 
   if (state == Query_arena::PREPARED)
     state= Query_arena::EXECUTED;
 
+  if (this->lex->sql_command == SQLCOM_CALL)
+  {
+    if (is_sql_prepare())
+      thd->protocol_text.send_out_parameters(&this->lex->param_list);
+    else
+      thd->protocol->send_out_parameters(&this->lex->param_list);
+  }
+
   /*
     Log COM_EXECUTE to the general log. Note, that in case of SQL
     prepared statements this causes two records to be output:

=== modified file 'sql/sql_profile.cc'
--- a/sql/sql_profile.cc	2009-10-09 13:59:25 +0000
+++ b/sql/sql_profile.cc	2009-10-21 12:52:25 +0000
@@ -415,7 +415,7 @@ bool PROFILING::show_profiles()
                                            MYSQL_TYPE_DOUBLE));
   field_list.push_back(new Item_empty_string("Query", 40));
 
-  if (thd->protocol->send_fields(&field_list,
+  if (thd->protocol->send_result_set_metadata(&field_list,
                                  Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'sql/sql_repl.cc'
--- a/sql/sql_repl.cc	2009-09-10 09:18:29 +0000
+++ b/sql/sql_repl.cc	2009-10-21 12:52:25 +0000
@@ -1404,7 +1404,7 @@ bool mysql_show_binlog_events(THD* thd)
   DBUG_ENTER("mysql_show_binlog_events");
 
   Log_event::init_show_field_list(&field_list);
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -1563,7 +1563,7 @@ bool show_binlog_info(THD* thd)
   field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
   field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
   protocol->prepare_for_resend();
@@ -1615,7 +1615,7 @@ bool show_binlogs(THD* thd)
   field_list.push_back(new Item_empty_string("Log_name", 255));
   field_list.push_back(new Item_return_int("File_size", 20,
                                            MYSQL_TYPE_LONGLONG));
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
   

=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc	2009-10-16 15:58:26 +0000
+++ b/sql/sql_select.cc	2009-10-21 12:52:25 +0000
@@ -1680,8 +1680,8 @@ JOIN::exec()
 		      (zero_result_cause?zero_result_cause:"No tables used"));
     else
     {
-      if (result->send_fields(*columns_list,
-                              Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+      if (result->send_result_set_metadata(*columns_list,
+                                           Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
       {
         DBUG_VOID_RETURN;
       }
@@ -2081,7 +2081,7 @@ JOIN::exec()
   }
   if (curr_join->group_list || curr_join->order)
   {
-    DBUG_PRINT("info",("Sorting for send_fields"));
+    DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
     thd_proc_info(thd, "Sorting result");
     /* If we have already done the group, add HAVING to sorted table */
     if (curr_join->tmp_having && ! curr_join->group_list && 
@@ -2221,7 +2221,7 @@ JOIN::exec()
   {
     thd_proc_info(thd, "Sending data");
     DBUG_PRINT("info", ("%s", thd->proc_info));
-    result->send_fields((procedure ? curr_join->procedure_fields_list :
+    result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list :
                          *curr_fields_list),
                         Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
     error= do_select(curr_join, curr_fields_list, NULL, procedure);
@@ -7214,7 +7214,7 @@ return_zero_rows(JOIN *join, select_resu
     if (having && having->val_int() == 0)
       send_row=0;
   }
-  if (!(result->send_fields(fields,
+  if (!(result->send_result_set_metadata(fields,
                               Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
   {
     bool send_error= FALSE;
@@ -9736,7 +9736,7 @@ void setup_tmp_table_column_bitmaps(TABL
   Create a temp table according to a field list.
 
   Given field pointers are changed to point at tmp_table for
-  send_fields. The table object is self contained: it's
+  send_result_set_metadata. The table object is self contained: it's
   allocated in its own memory root, as well as Field objects
   created for table columns.
   This function will replace Item_sum items in 'fields' list with
@@ -14988,7 +14988,7 @@ test_if_group_changed(List<Cached_item> 
 
   Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
   Change old item_field to use a new field with points at saved fieldvalue
-  This function is only called before use of send_fields.
+  This function is only called before use of send_result_set_metadata.
 
   @param thd                   THD pointer
   @param param                 temporary table parameters
@@ -15228,7 +15228,7 @@ bool JOIN::alloc_func_list()
   Initialize 'sum_funcs' array with all Item_sum objects.
 
   @param field_list        All items
-  @param send_fields       Items in select list
+  @param send_result_set_metadata       Items in select list
   @param before_group_by   Set to 1 if this is called before GROUP BY handling
   @param recompute         Set to TRUE if sum_funcs must be recomputed
 
@@ -15238,7 +15238,7 @@ bool JOIN::alloc_func_list()
     1  error
 */
 
-bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
+bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_result_set_metadata,
 			      bool before_group_by, bool recompute)
 {
   List_iterator_fast<Item> it(field_list);
@@ -15260,7 +15260,7 @@ bool JOIN::make_sum_func_list(List<Item>
   if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
   {
     rollup.state= ROLLUP::STATE_READY;
-    if (rollup_make_fields(field_list, send_fields, &func))
+    if (rollup_make_fields(field_list, send_result_set_metadata, &func))
       DBUG_RETURN(TRUE);			// Should never happen
   }
   else if (rollup.state == ROLLUP::STATE_NONE)

=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc	2009-10-14 11:14:58 +0000
+++ b/sql/sql_show.cc	2009-10-21 12:52:25 +0000
@@ -216,7 +216,7 @@ bool mysqld_show_authors(THD *thd)
   field_list.push_back(new Item_empty_string("Location",40));
   field_list.push_back(new Item_empty_string("Comment",80));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -250,7 +250,7 @@ bool mysqld_show_contributors(THD *thd)
   field_list.push_back(new Item_empty_string("Location",40));
   field_list.push_back(new Item_empty_string("Comment",80));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -326,7 +326,7 @@ bool mysqld_show_privileges(THD *thd)
   field_list.push_back(new Item_empty_string("Context",15));
   field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -403,8 +403,8 @@ bool mysqld_show_column_types(THD *thd)
   field_list.push_back(new Item_empty_string("Default",NAME_CHAR_LEN));
   field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
 
-  if (protocol->send_fields(&field_list,
-                            Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+  if (protocol->send_result_set_metadata(&field_list,
+                                         Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
   /* TODO: Change the loop to not use 'i' */
@@ -655,7 +655,7 @@ mysqld_show_create(THD *thd, TABLE_LIST 
                                                max(buffer.length(),1024)));
   }
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
   protocol->prepare_for_resend();
@@ -740,7 +740,7 @@ bool mysqld_show_create_db(THD *thd, cha
   field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
   field_list.push_back(new Item_empty_string("Create Database",1024));
 
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -808,7 +808,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST 
   }
   restore_record(table, s->default_values);              // Get empty record
   table->use_all_columns();
-  if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
+  if (thd->protocol->send_result_set_metadata(&field_list, Protocol::SEND_DEFAULTS))
     DBUG_VOID_RETURN;
   my_eof(thd);
   DBUG_VOID_RETURN;
@@ -1698,7 +1698,7 @@ void mysqld_list_processes(THD *thd,cons
   field->maybe_null=1;
   field_list.push_back(field=new Item_empty_string("Info",max_query_length));
   field->maybe_null=1;
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_VOID_RETURN;
 
@@ -6905,7 +6905,7 @@ static bool show_create_trigger_impl(THD
   fields.push_back(new Item_empty_string("Database Collation",
                                          MY_CS_NAME_SIZE));
 
-  if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+  if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     return TRUE;
 
   /* Send data. */

=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc	2009-10-16 13:41:43 +0000
+++ b/sql/sql_table.cc	2009-10-21 12:52:25 +0000
@@ -4524,7 +4524,7 @@ static bool mysql_admin_table(THD* thd, 
   item->maybe_null = 1;
   field_list.push_back(item = new Item_empty_string("Msg_text", 255));
   item->maybe_null = 1;
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 
@@ -7819,7 +7819,7 @@ bool mysql_checksum_table(THD *thd, TABL
   field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
                                           MY_INT64_NUM_DECIMAL_DIGITS));
   item->maybe_null= 1;
-  if (protocol->send_fields(&field_list,
+  if (protocol->send_result_set_metadata(&field_list,
                             Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
     DBUG_RETURN(TRUE);
 

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2009-10-13 19:31:03 +0000
+++ b/tests/mysql_client_test.c	2009-10-21 12:52:25 +0000
@@ -1516,6 +1516,568 @@ static void test_prepare_simple()
   myquery(rc);
 }
 
+/************************************************************************/
+
+#define FILE_PATH_SIZE 4096
+
+char mct_log_file_path[FILE_PATH_SIZE];
+FILE *mct_log_file= NULL;
+
+void mct_start_logging(const char *test_case_name)
+{
+  const char *tmp_dir= getenv("MYSQL_TMP_DIR");
+
+  if (!tmp_dir)
+  {
+    printf("Warning: MYSQL_TMP_DIR is not set. Logging is disabled.\n");
+    return;
+  }
+
+  if (mct_log_file)
+  {
+    printf("Warning: can not start logging for test case '%s' "
+           "because log is already open\n",
+           (const char *) test_case_name);
+    return;
+  }
+
+  /*
+    Path is: <tmp_dir>/<test_case_name>.out.log
+    10 is length of '/' + '.out.log' + \0
+  */
+
+  if (strlen(tmp_dir) + strlen(test_case_name) + 10 > FILE_PATH_SIZE)
+  {
+    printf("Warning: MYSQL_TMP_DIR is too long. Logging is disabled.\n");
+    return;
+  }
+
+  my_snprintf(mct_log_file_path, FILE_PATH_SIZE,
+              "%s/%s.out.log",
+              (const char *) tmp_dir,
+              (const char *) test_case_name);
+
+  mct_log_file= my_fopen(mct_log_file_path, O_WRONLY | O_BINARY, MYF(MY_WME));
+
+  if (!mct_log_file)
+  {
+    printf("Warning: can not open log file (%s): %s. Logging is disabled.\n",
+        (const char *) mct_log_file_path,
+        (const char *) strerror(errno));
+    return;
+  }
+}
+
+void mct_log(const char *format, ...)
+{
+  va_list args;
+  va_start(args, format);
+  vprintf(format, args);
+  va_end(args);
+
+  if (mct_log_file)
+  {
+    va_list args;
+    va_start(args, format);
+    vfprintf(mct_log_file, format, args);
+    va_end(args);
+  }
+}
+
+void mct_close_log()
+{
+  if (!mct_log_file)
+    return;
+
+  my_fclose(mct_log_file, MYF(0));
+  mct_log_file= NULL;
+}
+
+#define WL4435_NUM_PARAMS 10
+#define WL4435_STRING_SIZE 30
+
+static void test_wl4435()
+{
+  MYSQL_STMT *stmt;
+  int        rc;
+  char query[MAX_TEST_QUERY_LENGTH];
+
+  char       str_data[20][WL4435_STRING_SIZE];
+  double     dbl_data[20];
+  char       dec_data[20][WL4435_STRING_SIZE];
+  int        int_data[20];
+  ulong      str_length= WL4435_STRING_SIZE;
+  my_bool    is_null;
+  MYSQL_BIND ps_params[WL4435_NUM_PARAMS];
+
+  int exec_counter;
+
+  myheader("test_wl4435");
+  mct_start_logging("test_wl4435");
+
+  rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p2");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "CREATE TABLE t1(a1 INT, a2 CHAR(32), "
+                       "  a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), "
+                       "  b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "INSERT INTO t1 VALUES"
+    "(1, '11', 12.34, 56.7), "
+    "(2, '12', 56.78, 90.1), "
+    "(3, '13', 23.45, 67.8)");
+  myquery(rc);
+
+  rc= mysql_query(mysql, "INSERT INTO t2 VALUES"
+    "(100, 10, '110', 70.70, 10.1), "
+    "(200, 20, '120', 80.80, 20.2), "
+    "(300, 30, '130', 90.90, 30.3)");
+  myquery(rc);
+
+  rc= mysql_query(mysql,
+    "CREATE PROCEDURE p1("
+    "   IN v0 INT, "
+    "   OUT v_str_1 CHAR(32), "
+    "   OUT v_dbl_1 DOUBLE(4, 2), "
+    "   OUT v_dec_1 DECIMAL(6, 3), "
+    "   OUT v_int_1 INT, "
+    "   IN v1 INT, "
+    "   INOUT v_str_2 CHAR(64), "
+    "   INOUT v_dbl_2 DOUBLE(5, 3), "
+    "   INOUT v_dec_2 DECIMAL(7, 4), "
+    "   INOUT v_int_2 INT)"
+    "BEGIN "
+    "   SET v0 = -1; "
+    "   SET v1 = -1; "
+    "   SET v_str_1 = 'test_1'; "
+    "   SET v_dbl_1 = 12.34; "
+    "   SET v_dec_1 = 567.891; "
+    "   SET v_int_1 = 2345; "
+    "   SET v_str_2 = 'test_2'; "
+    "   SET v_dbl_2 = 67.891; "
+    "   SET v_dec_2 = 234.6789; "
+    "   SET v_int_2 = 6789; "
+    "   SELECT * FROM t1; "
+    "   SELECT * FROM t2; "
+    "END");
+  myquery(rc);
+
+  rc= mysql_query(mysql,
+    "CREATE PROCEDURE p2("
+    "   IN i1 VARCHAR(255) CHARACTER SET koi8r, "
+    "   OUT o1 VARCHAR(255) CHARACTER SET cp1251, "
+    "   OUT o2 VARBINARY(255)) "
+    "BEGIN "
+    "   SET o1 = i1; "
+    "   SET o2 = i1; "
+    "END");
+  myquery(rc);
+
+  strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+  stmt= mysql_simple_prepare(mysql, query);
+  check_stmt(stmt);
+
+  /* Init PS-parameters. */
+
+  bzero((char *) ps_params, sizeof (ps_params));
+
+  /* - v0 -- INT */
+
+  ps_params[0].buffer_type= MYSQL_TYPE_LONG;
+  ps_params[0].buffer= (char *) &int_data[0];
+  ps_params[0].length= 0;
+  ps_params[0].is_null= 0;
+
+  /* - v_str_1 -- CHAR(32) */
+
+  ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+  ps_params[1].buffer= (char *) str_data[0];
+  ps_params[1].buffer_length= WL4435_STRING_SIZE;
+  ps_params[1].length= &str_length;
+  ps_params[1].is_null= 0;
+
+  /* - v_dbl_1 -- DOUBLE */
+
+  ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE;
+  ps_params[2].buffer= (char *) &dbl_data[0];
+  ps_params[2].length= 0;
+  ps_params[2].is_null= 0;
+
+  /* - v_dec_1 -- DECIMAL */
+
+  ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL;
+  ps_params[3].buffer= (char *) dec_data[0];
+  ps_params[3].buffer_length= WL4435_STRING_SIZE;
+  ps_params[3].length= 0;
+  ps_params[3].is_null= 0;
+
+  /* - v_int_1 -- INT */
+
+  ps_params[4].buffer_type= MYSQL_TYPE_LONG;
+  ps_params[4].buffer= (char *) &int_data[0];
+  ps_params[4].length= 0;
+  ps_params[4].is_null= 0;
+
+  /* - v1 -- INT */
+
+  ps_params[5].buffer_type= MYSQL_TYPE_LONG;
+  ps_params[5].buffer= (char *) &int_data[0];
+  ps_params[5].length= 0;
+  ps_params[5].is_null= 0;
+
+  /* - v_str_2 -- CHAR(32) */
+
+  ps_params[6].buffer_type= MYSQL_TYPE_STRING;
+  ps_params[6].buffer= (char *) str_data[0];
+  ps_params[6].buffer_length= WL4435_STRING_SIZE;
+  ps_params[6].length= &str_length;
+  ps_params[6].is_null= 0;
+
+  /* - v_dbl_2 -- DOUBLE */
+
+  ps_params[7].buffer_type= MYSQL_TYPE_DOUBLE;
+  ps_params[7].buffer= (char *) &dbl_data[0];
+  ps_params[7].length= 0;
+  ps_params[7].is_null= 0;
+
+  /* - v_dec_2 -- DECIMAL */
+
+  ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL;
+  ps_params[8].buffer= (char *) dec_data[0];
+  ps_params[8].buffer_length= WL4435_STRING_SIZE;
+  ps_params[8].length= 0;
+  ps_params[8].is_null= 0;
+
+  /* - v_int_2 -- INT */
+
+  ps_params[9].buffer_type= MYSQL_TYPE_LONG;
+  ps_params[9].buffer= (char *) &int_data[0];
+  ps_params[9].length= 0;
+  ps_params[9].is_null= 0;
+
+  /* Bind parameters. */
+
+  rc= mysql_stmt_bind_param(stmt, ps_params);
+
+  /* Execute! */
+
+  for (exec_counter= 0; exec_counter < 3; ++exec_counter)
+  {
+    int i;
+    int num_fields;
+    MYSQL_BIND *rs_bind;
+
+    mct_log("\nexec_counter: %d\n", (int) exec_counter);
+
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+
+    while (1)
+    {
+      MYSQL_FIELD *fields;
+
+      MYSQL_RES *rs_metadata= mysql_stmt_result_metadata(stmt);
+
+      num_fields= mysql_stmt_field_count(stmt);
+      fields= mysql_fetch_fields(rs_metadata);
+
+      rs_bind= (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields);
+      bzero(rs_bind, sizeof (MYSQL_BIND) * num_fields);
+
+      mct_log("num_fields: %d\n", (int) num_fields);
+
+      for (i = 0; i < num_fields; ++i)
+      {
+        mct_log("  - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+                "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+                "type: %d; decimals: %d\n",
+                (int) i,
+                (const char *) fields[i].name,
+                (const char *) fields[i].org_name,
+                (const char *) fields[i].table,
+                (const char *) fields[i].org_table,
+                (const char *) fields[i].db,
+                (const char *) fields[i].catalog,
+                (int) fields[i].length,
+                (int) fields[i].max_length,
+                (int) fields[i].type,
+                (int) fields[i].decimals);
+
+        rs_bind[i].buffer_type= fields[i].type;
+        rs_bind[i].is_null= &is_null;
+
+        switch (fields[i].type)
+        {
+          case MYSQL_TYPE_LONG:
+            rs_bind[i].buffer= (char *) &(int_data[i]);
+            rs_bind[i].buffer_length= sizeof (int_data);
+            break;
+
+          case MYSQL_TYPE_STRING:
+            rs_bind[i].buffer= (char *) str_data[i];
+            rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+            rs_bind[i].length= &str_length;
+            break;
+
+          case MYSQL_TYPE_DOUBLE:
+            rs_bind[i].buffer= (char *) &dbl_data[i];
+            rs_bind[i].buffer_length= sizeof (dbl_data);
+            break;
+
+          case MYSQL_TYPE_NEWDECIMAL:
+            rs_bind[i].buffer= (char *) dec_data[i];
+            rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+            rs_bind[i].length= &str_length;
+            break;
+
+          default:
+            fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type);
+            exit(1);
+        }
+      }
+
+      rc= mysql_stmt_bind_result(stmt, rs_bind);
+      check_execute(stmt, rc);
+
+      mct_log("Data:\n");
+
+      while (1)
+      {
+        int rc= mysql_stmt_fetch(stmt);
+
+        if (rc == 1 || rc == MYSQL_NO_DATA)
+          break;
+
+        mct_log(" ");
+
+        for (i = 0; i < num_fields; ++i)
+        {
+          switch (rs_bind[i].buffer_type)
+          {
+            case MYSQL_TYPE_LONG:
+              mct_log(" int: %ld;",
+                      (long) *((int *) rs_bind[i].buffer));
+              break;
+
+            case MYSQL_TYPE_STRING:
+              mct_log(" str: '%s';",
+                      (char *) rs_bind[i].buffer);
+              break;
+
+            case MYSQL_TYPE_DOUBLE:
+              mct_log(" dbl: %lf;",
+                      (double) *((double *) rs_bind[i].buffer));
+              break;
+
+            case MYSQL_TYPE_NEWDECIMAL:
+              mct_log(" dec: '%s';",
+                      (char *) rs_bind[i].buffer);
+              break;
+
+            default:
+              printf("  unexpected type (%d)\n",
+                rs_bind[i].buffer_type);
+          }
+        }
+        mct_log("\n");
+      }
+
+      mct_log("EOF\n");
+
+      rc= mysql_stmt_next_result(stmt);
+      mct_log("mysql_stmt_next_result(): %d; field_count: %d\n",
+              (int) rc, (int) mysql->field_count);
+
+      free(rs_bind);
+      mysql_free_result(rs_metadata);
+
+      if (rc > 0)
+      {
+        printf("Error: %s (errno: %d)\n",
+               mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
+        DIE(rc > 0);
+      }
+
+      if (rc)
+        break;
+
+      if (!mysql->field_count)
+      {
+        /* This is the last OK-packet. No more resultsets. */
+        break;
+      }
+    }
+
+  }
+
+  mysql_stmt_close(stmt);
+
+  mct_close_log();
+
+  rc= mysql_commit(mysql);
+  myquery(rc);
+
+  /* i18n part of test case. */
+
+  {
+    const char *str_koi8r= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
+    const char *str_cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
+    char o1_buffer[255];
+    ulong o1_length;
+    char o2_buffer[255];
+    ulong o2_length;
+
+    MYSQL_BIND rs_bind[2];
+
+    strmov(query, "CALL p2(?, ?, ?)");
+    stmt= mysql_simple_prepare(mysql, query);
+    check_stmt(stmt);
+
+    /* Init PS-parameters. */
+
+    bzero((char *) ps_params, sizeof (ps_params));
+
+    ps_params[0].buffer_type= MYSQL_TYPE_STRING;
+    ps_params[0].buffer= (char *) str_koi8r;
+    ps_params[0].buffer_length= strlen(str_koi8r);
+
+    ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+    ps_params[1].buffer= o1_buffer;
+    ps_params[1].buffer_length= 0;
+
+    ps_params[2].buffer_type= MYSQL_TYPE_STRING;
+    ps_params[2].buffer= o2_buffer;
+    ps_params[2].buffer_length= 0;
+
+    /* Bind parameters. */
+
+    rc= mysql_stmt_bind_param(stmt, ps_params);
+    check_execute(stmt, rc);
+
+    /* Prevent converting to character_set_results. */
+
+    rc= mysql_query(mysql, "SET NAMES binary");
+    myquery(rc);
+
+    /* Execute statement. */
+
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+
+    /* Bind result. */
+
+    bzero(rs_bind, sizeof (rs_bind));
+
+    rs_bind[0].buffer_type= MYSQL_TYPE_STRING;
+    rs_bind[0].buffer= o1_buffer;
+    rs_bind[0].buffer_length= sizeof (o1_buffer);
+    rs_bind[0].length= &o1_length;
+
+    rs_bind[1].buffer_type= MYSQL_TYPE_BLOB;
+    rs_bind[1].buffer= o2_buffer;
+    rs_bind[1].buffer_length= sizeof (o2_buffer);
+    rs_bind[1].length= &o2_length;
+
+    rc= mysql_stmt_bind_result(stmt, rs_bind);
+    check_execute(stmt, rc);
+
+    /* Fetch result. */
+
+    rc= mysql_stmt_fetch(stmt);
+    check_execute(stmt, rc);
+
+    /* Check result. */
+
+    DIE_UNLESS(o1_length == strlen(str_cp1251));
+    DIE_UNLESS(o2_length == strlen(str_koi8r));
+    DIE_UNLESS(!memcmp(o1_buffer, str_cp1251, o1_length));
+    DIE_UNLESS(!memcmp(o2_buffer, str_koi8r, o2_length));
+
+    rc= mysql_stmt_fetch(stmt);
+    DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+    rc= mysql_stmt_next_result(stmt);
+    DIE_UNLESS(rc == 0 && mysql->field_count == 0);
+
+    mysql_stmt_close(stmt);
+
+    rc= mysql_commit(mysql);
+    myquery(rc);
+  }
+}
+
+static void test_wl4435_2()
+{
+  MYSQL_STMT *stmt;
+  int  i;
+  int  rc;
+  char query[MAX_TEST_QUERY_LENGTH];
+
+  myheader("test_wl4435_2");
+  mct_start_logging("test_wl4435_2");
+
+  /*
+    Do a few iterations so that we catch any problem with incorrect
+    handling/flushing prepared statement results.
+  */
+
+  for (i= 0; i < 10; ++i)
+  {
+    /*
+      Prepare a procedure. That can be moved out of the loop, but it was
+      left in the loop for the sake of having as many statements as
+      possible.
+    */
+
+    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);
+
+    /* Invoke a procedure, that returns several result sets. */
+
+    strmov(query, "CALL p1()");
+    stmt= mysql_simple_prepare(mysql, query);
+    check_stmt(stmt);
+
+    /* Execute! */
+
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+
+    /* Flush all the results. */
+
+    mysql_stmt_close(stmt);
+
+    /* Clean up. */
+    rc= mysql_commit(mysql);
+    myquery(rc);
+
+    rc= mysql_query(mysql, "DROP PROCEDURE p1");
+    myquery(rc);
+  }
+}
+
 
 /* Test simple prepare field results */
 
@@ -14476,9 +15038,8 @@ static void test_bug12001()
 
   /* Create connection that supports multi statements */
   if (!mysql_real_connect(mysql_local, opt_host, opt_user,
-                           opt_password, current_db, opt_port,
-                           opt_unix_socket, CLIENT_MULTI_STATEMENTS |
-                           CLIENT_MULTI_RESULTS))
+                          opt_password, current_db, opt_port,
+                          opt_unix_socket, CLIENT_MULTI_STATEMENTS))
   {
     fprintf(stdout, "\n mysql_real_connect() failed");
     exit(1);
@@ -15846,7 +16407,7 @@ static void test_bug15752()
   if (! mysql_real_connect(&mysql_local, opt_host, opt_user,
                            opt_password, current_db, opt_port,
                            opt_unix_socket,
-                           CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS))
+                           CLIENT_MULTI_STATEMENTS))
   {
     printf("Unable connect to MySQL server: %s\n", mysql_error(&mysql_local));
     DIE_UNLESS(0);
@@ -18565,6 +19126,8 @@ static struct my_tests_st my_tests[]= {
   { "test_wl4166_2", test_wl4166_2 },
   { "test_wl4166_3", test_wl4166_3 },
   { "test_wl4166_4", test_wl4166_4 },
+  { "test_wl4435",   test_wl4435 },
+  { "test_wl4435_2", test_wl4435_2 },
   { "test_bug38486", test_bug38486 },
   { "test_bug33831", test_bug33831 },
   { "test_bug40365", test_bug40365 },


Attachment: [text/bzr-bundle] bzr/kostja@sun.com-20091021125225-22moi9hob63kycls.bundle
Thread
bzr commit into mysql-5.5.0-next-mr-runtime branch (kostja:2932)WL#4264Konstantin Osipov21 Oct