List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:August 5 2008 6:04pm
Subject:bzr commit into mysql-6.0 branch (alik:2675) WL#4435
View as plain text  
#At file:///mnt/raid/alik/MySQL/devel/wl4435/6.0-rt-wl4435/

 2675 Alexander Nozdrin	2008-08-05
      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.
modified:
  include/mysql.h
  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/item.cc
  sql/item.h
  sql/protocol.cc
  sql/protocol.h
  sql/sp_head.cc
  sql/sql_class.cc
  sql/sql_handler.cc
  sql/sql_prepare.cc
  tests/mysql_client_test.c

per-file messages:
  include/mysql.h
    Introduce mysql_stmt_next_result().
  include/mysql_com.h
    Introduce CLIENT_PS_OUT_PARAMS -- a new client capability telling that the
    client can support OUT-parameters in prepared statements.  Introduce
    SERVER_PS_OUT_PARAMS -- a new bit in server status attribute of EOF-packet,
    which is intended to explicitly mark the result set containing OUT-parameters.
  libmysql/client_settings.h
    Update CLIENT_CAPABILITIES.
  libmysql/libmysql.c
    1. Fix alloc_stmt_fields() -- MUSQL_FIELD::catalog wasn't assigned as it should have been.
    2. Change update_stmt_fields() so that it works with result sets with different structures.
    3. Implement mysql_stmt_next_result().
  libmysqld/lib_sql.cc
    Refactoring: prepare_for_send() does not need List, only number of items in the collection.
  mysql-test/r/ps.result
    Update result file.
  mysql-test/t/ps.test
    Added a test case for SQL-part of WL#4435.
  sql/item.cc
    Store parameter values in Item_param.
  sql/item.h
    Store parameter values in Item_param.
  sql/protocol.cc
    Implement Protocol::send_out_parameters().
    Protocol_text::send_out_parameters() -- SQL part of WL#4435;
    Protocol_binary::send_out_parameters() -- a C API part of WL#4435.
  sql/protocol.h
    1. Refactoring: prepare_for_send() does not need List, only number of items in
       the collection.
    2. Declare Protocol::send_out_parameters().
    3. Refactoring: send_row_items() -- common functionality to send row data.
  sql/sp_head.cc
    Set OUT-parameter descriptions.
  sql/sql_class.cc
    Refactoring: send_row_items() -- common functionality to send row data.
  sql/sql_handler.cc
    Refactoring: send_row_items() -- common functionality to send row data.
  sql/sql_prepare.cc
    Send OUT-parameters result set after PS execution.
  tests/mysql_client_test.c
    Add a test case for C API part of WL#4435.
=== modified file 'include/mysql.h'
--- a/include/mysql.h	2007-11-26 19:11:48 +0000
+++ b/include/mysql.h	2008-08-05 18:04:27 +0000
@@ -714,6 +714,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_com.h'
--- a/include/mysql_com.h	2008-06-17 20:04:19 +0000
+++ b/include/mysql_com.h	2008-08-05 18:04:27 +0000
@@ -154,6 +154,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)
@@ -177,6 +178,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)
 
@@ -219,6 +221,11 @@ enum enum_server_command
   to use.  See WorkLog 4098.
 */
 #define SERVER_QUERY_WAS_SLOW           2048
+
+/**
+  To mark ResultSet containing output parameter values.
+*/
+#define SERVER_PS_OUT_PARAMS            4096
 
 /**
   Server status flags that must be cleared when starting

=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h	2007-09-29 19:31:08 +0000
+++ b/libmysql/client_settings.h	2008-08-05 18:04:27 +0000
@@ -16,9 +16,12 @@
 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_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	2008-06-17 20:04:19 +0000
+++ b/libmysql/libmysql.c	2008-08-05 18:04:27 +0000
@@ -1727,6 +1727,9 @@ static void alloc_stmt_fields(MYSQL_STMT
 
   stmt->field_count= mysql->field_count;
 
+  if (!stmt->field_count)
+    return;
+
   /*
     Get the field information for non-select statements
     like SHOW and DESCRIBE commands
@@ -1746,6 +1749,7 @@ static void alloc_stmt_fields(MYSQL_STMT
 	 field= stmt->fields;
        field && fields < end; fields++, field++)
   {
+    field->catalog  = strdup_root(alloc,fields->catalog);
     field->db       = strdup_root(alloc,fields->db);
     field->table    = strdup_root(alloc,fields->table);
     field->org_table= strdup_root(alloc,fields->org_table);
@@ -2484,6 +2488,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.
@@ -2551,28 +2582,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));
 }
@@ -4828,6 +4838,44 @@ int STDCALL mysql_next_result(MYSQL *mys
     DBUG_RETURN((*mysql->methods->next_result)(mysql));
 
   DBUG_RETURN(-1);				/* No more results */
+}
+
+
+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)
+    DBUG_RETURN(rc);
+
+  alloc_stmt_fields(stmt);
+  stmt->bind_result_done= FALSE;
+
+  if (!(mysql->server_status & SERVER_MORE_RESULTS_EXISTS))
+    DBUG_RETURN(-1);
+
+  stmt->state= MYSQL_STMT_EXECUTE_DONE;
+  stmt->mysql->status= MYSQL_STATUS_GET_RESULT;
+
+  prepare_to_fetch_result(stmt);
+
+  DBUG_RETURN(0);
 }
 
 

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2008-06-20 11:40:01 +0000
+++ b/libmysqld/lib_sql.cc	2008-08-05 18:04:27 +0000
@@ -972,7 +972,7 @@ bool Protocol::send_fields(List<Item> *l
   if (flags & SEND_EOF)
     write_eof_packet(thd, thd->server_status, thd->total_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	2008-05-22 18:40:15 +0000
+++ b/mysql-test/r/ps.result	2008-08-05 18:04:27 +0000
@@ -2920,4 +2920,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	2008-05-21 10:17:29 +0000
+++ b/mysql-test/t/ps.test	2008-08-05 18:04:27 +0000
@@ -2998,5 +2998,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/item.cc'
--- a/sql/item.cc	2008-06-17 20:04:19 +0000
+++ b/sql/item.cc	2008-08-05 18:04:27 +0000
@@ -2512,7 +2512,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*) "?";
   /* 
@@ -2593,6 +2594,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.
@@ -3212,6 +3224,132 @@ Item_param::set_param_type_and_swap_valu
   decimal_value.swap(src->decimal_value);
   str_value.swap(src->str_value);
   str_value_ptr.swap(src->str_value_ptr);
+}
+
+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;
+}
+
+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;
 }
 
 /****************************************************************************

=== modified file 'sql/item.h'
--- a/sql/item.h	2008-06-17 20:04:19 +0000
+++ b/sql/item.h	2008-08-05 18:04:27 +0000
@@ -443,6 +443,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; }
 };
 
 
@@ -1609,7 +1614,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;
@@ -1697,6 +1703,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);
@@ -1746,6 +1753,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;
 };
 
 

=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc	2008-06-05 16:11:22 +0000
+++ b/sql/protocol.cc	2008-08-05 18:04:27 +0000
@@ -566,7 +566,7 @@ bool Protocol::send_fields(List<Item> *l
 {
   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();
@@ -704,7 +704,7 @@ bool Protocol::send_fields(List<Item> *l
     */
     write_eof_packet(thd, &thd->net, thd->server_status, thd->total_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),
@@ -722,6 +722,28 @@ bool Protocol::write()
 #endif /* EMBEDDED_LIBRARY */
 
 
+bool Protocol::send_row_items(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_row_items");
+
+  for (Item *item= it++; item; item= it++)
+  {
+    if (item->send(this, &str_buffer) || thd->is_error())
+    {
+      this->free();				// Free used buffer
+      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+      DBUG_RETURN(TRUE);
+    }
+  }
+
+  DBUG_RETURN(FALSE);
+}
+
+
 /**
   Send \\0 end terminated string.
 
@@ -1044,6 +1066,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
@@ -1064,14 +1133,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;
 }
 
 
@@ -1258,4 +1326,80 @@ bool Protocol_binary::store_time(MYSQL_T
     length=0;
   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.
+
+      out_param_lst.push_back(item_param);
+    }
+  }
+
+  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_fields().
+  */
+
+  thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
+
+  /* Send meta-data. */
+  if (send_fields(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+    return TRUE;
+
+  /* Send data. */
+
+  prepare_for_resend();
+
+  if (send_row_items(&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, thd->total_warn_count);
+
+  return FALSE;
 }

=== modified file 'sql/protocol.h'
--- a/sql/protocol.h	2008-02-11 16:11:22 +0000
+++ b/sql/protocol.h	2008-08-05 18:04:27 +0000
@@ -20,6 +20,7 @@
 
 class i_string;
 class THD;
+class Item_param;
 typedef struct st_mysql_field MYSQL_FIELD;
 typedef struct st_mysql_rows MYSQL_ROWS;
 
@@ -54,6 +55,7 @@ public:
 
   enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
   virtual bool send_fields(List<Item> *list, uint flags);
+  bool send_row_items(List<Item> *row_items);
 
   bool store(I_List<i_string> *str_list);
   bool store(const char *from, CHARSET_INFO *cs);
@@ -71,9 +73,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();
@@ -95,6 +97,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() {}
@@ -136,6 +140,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
@@ -150,7 +156,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();
@@ -171,6 +177,9 @@ 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; };
 };
 

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2008-06-28 11:00:59 +0000
+++ b/sql/sp_head.cc	2008-08-05 18:04:27 +0000
@@ -2004,6 +2004,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);
     }
   }
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2008-07-03 21:26:51 +0000
+++ b/sql/sql_class.cc	2008-08-05 18:04:27 +0000
@@ -1582,10 +1582,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);
   }
 
   /*
@@ -1595,31 +1598,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) || thd->is_error())
-    {
-      protocol->free();				// Free used buffer
-      my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
-      break;
-    }
-  }
-  if (thd->is_error())
+  if (protocol->send_row_items(&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_handler.cc'
--- a/sql/sql_handler.cc	2008-06-20 13:11:20 +0000
+++ b/sql/sql_handler.cc	2008-08-05 18:04:27 +0000
@@ -661,18 +661,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_row_items(&list))
+        goto err;
+
       protocol->write();
     }
     num_rows++;

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2008-06-28 11:00:59 +0000
+++ b/sql/sql_prepare.cc	2008-08-05 18:04:27 +0000
@@ -3566,6 +3566,9 @@ bool Prepared_statement::execute(String 
   if (state == Query_arena::PREPARED)
     state= Query_arena::EXECUTED;
 
+  if (this->lex->sql_command == SQLCOM_CALL)
+    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 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2008-06-28 11:00:59 +0000
+++ b/tests/mysql_client_test.c	2008-08-05 18:04:27 +0000
@@ -1499,6 +1499,528 @@ static void test_prepare_simple()
   myquery(rc);
 }
 
+/************************************************************************/
+
+#define FILE_PATH_SIZE 4096
+
+char mct_log_file_path[FILE_PATH_SIZE];
+FILE *mct_log_file;
+
+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 (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, 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);
+  }
+}
+
+int mct_check_result(const char *result_file_name)
+{
+  const int CMD_BUFFER_SIZE= 8192;
+  char diff_cmd[CMD_BUFFER_SIZE];
+
+  const char *test_dir= getenv("MYSQL_TEST_DIR");
+  char result_file_path[FILE_PATH_SIZE];
+
+  if (!mct_log_file)
+  {
+    printf("Warning: logging was disabled. Result checking is impossible.\n");
+    return 0;
+  }
+
+  if (!test_dir)
+  {
+    printf("Warning: MYSQL_TEST_DIR is not set. "
+           "Result checking is impossible.\n");
+    return 0;
+  }
+
+  if (strlen(test_dir) + strlen(result_file_name) + 10 > FILE_PATH_SIZE)
+  {
+    printf("Warning: MYSQL_TEST_DIR is too long. "
+           "Result checking is impossible.\n");
+    return 0;
+  }
+
+  my_snprintf(result_file_path, FILE_PATH_SIZE,
+              "%s/%s",
+              (const char *) test_dir,
+              (const char *) result_file_name);
+
+  my_fclose(mct_log_file, MYF(0));
+  mct_log_file= NULL;
+
+  snprintf(diff_cmd, CMD_BUFFER_SIZE, "diff -u '%s' '%s'",
+           (const char *) result_file_path,
+           (const char *) mct_log_file_path);
+
+  puts("");
+  fflush(stdout);
+  fflush(stderr);
+
+  return system(diff_cmd);
+}
+
+#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);
+
+    mct_log("server_status: %04x\n", (int) stmt->mysql->server_status);
+
+    while (1)
+    {
+      MYSQL_FIELD *fields;
+
+      MYSQL_RES *rs= mysql_stmt_result_metadata(stmt);
+
+      num_fields= mysql_num_fields(rs);
+      fields= mysql_fetch_fields(rs);
+
+      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; server_status: %04x\n",
+                 (int) rc, (int) stmt->mysql->server_status);
+
+      if (rc > 0)
+      {
+        printf("Error: %s (errno: %d)\n",
+               mysql_error(mysql), mysql_errno(mysql));
+        DIE(rc > 0);
+      }
+
+      if (rc)
+        break;
+    }
+
+    free(rs_bind);
+  }
+
+  mysql_stmt_close(stmt);
+
+  rc= mysql_commit(mysql);
+  myquery(rc);
+
+  rc= mct_check_result("r/test_wl4435.result");
+  mytest_r(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 == -1);
+
+    mysql_stmt_close(stmt);
+
+    rc= mysql_commit(mysql);
+    myquery(rc);
+  }
+}
 
 /* Test simple prepare field results */
 
@@ -18139,6 +18661,7 @@ static struct my_tests_st my_tests[]= {
   { "test_wl4166_3", test_wl4166_3 },
   { "test_wl4166_4", test_wl4166_4 },
   { "test_bug36004", test_bug36004 },
+  { "test_wl4435",   test_wl4435 },
   { 0, 0 }
 };
 
@@ -18265,7 +18788,7 @@ int main(int argc, char **argv)
                         (char**) embedded_server_groups))
     DIE("Can't initialize MySQL server");
 
-  client_connect(0);       /* connect to server */
+  client_connect(CLIENT_MULTI_RESULTS);       /* connect to server */
 
   total_time= 0;
   for (iter_count= 1; iter_count <= opt_count; iter_count++)

Thread
bzr commit into mysql-6.0 branch (alik:2675) WL#4435Alexander Nozdrin5 Aug
  • Re: bzr commit into mysql-6.0 branch (alik:2675) WL#4435Konstantin Osipov6 Aug