List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:July 18 2008 3:54pm
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-07-18
      The second patch for WL#4435: Support OUT-parameters in prepared statements.
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/mysql_priv.h
  sql/protocol.cc
  sql/protocol.h
  sql/sp_head.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/mysql_priv.h
    An aux structure to describe OUT-parameters.
  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().
  sql/sp_head.cc
    Set OUT-parameter descriptions.
  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-07-18 15:54:23 +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-07-18 15:54:23 +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_OUT_PARAMS    (1UL << 18) /* Output parameters 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_OUT_PARAMS | \
                            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-07-18 15:54:23 +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_OUT_PARAMS)
 
 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-07-18 15:54:23 +0000
@@ -1746,6 +1746,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);
@@ -1777,22 +1778,7 @@ static void update_stmt_fields(MYSQL_STM
   MYSQL_FIELD *stmt_field= stmt->fields;
   MYSQL_BIND *my_bind= stmt->bind_result_done ? stmt->bind : 0;
 
-  if (stmt->field_count != stmt->mysql->field_count)
-  {
-    /*
-      The tables used in the statement were altered,
-      and the query now returns a different number of columns.
-      There is no way to continue without reallocating the bind
-      array:
-      - if the number of columns increased, mysql_stmt_fetch()
-      will write beyond allocated memory
-      - if the number of columns decreased, some user-bound
-      buffers will be left unassigned without user knowing
-      that.
-    */
-    set_stmt_error(stmt, CR_NEW_STMT_METADATA, unknown_sqlstate, NULL);
-    return;
-  }
+  DBUG_ASSERT(stmt->field_count == stmt->mysql->field_count);
 
   for (; field < field_end; ++field, ++stmt_field)
   {
@@ -2463,16 +2449,36 @@ static void reinit_result_set_metadata(M
     */
     alloc_stmt_fields(stmt);
   }
+  else if (stmt->field_count != stmt->mysql->field_count)
+  {
+    /*
+      Either prepared statement contains multiple result sets, which have
+      different structure; or the tables used in the statement were
+      altered, and the query now returns a different number of columns.
+
+      There is no way to continue without reallocating the bind array.
+        - if the number of columns increased, mysql_stmt_fetch() will write
+          beyond allocated memory
+        - if the number of columns decreased, some user-bound buffers will
+          be left unassigned without user knowing that.
+
+      So, reset bind-result state in order to force the user to update
+      structures.
+    */
+
+    alloc_stmt_fields(stmt);
+    stmt->bind_result_done= FALSE;
+  }
   else
   {
     /*
       Update result set metadata if it for some reason changed between
       prepare and execute, i.e.:
       - in case of 'SELECT ?' we don't know column type unless data was
-      supplied to mysql_stmt_execute, so updated column type is sent
-      now.
-      - if data dictionary changed between prepare and execute, for
-      example a table used in the query was altered.
+        supplied to mysql_stmt_execute, so updated column type is sent now.
+      - if data dictionary changed between prepare and execute, for example
+        a table used in the query was altered.
+      - if we have multiple result sets.
       Note, that now (4.1.3) we always send metadata in reply to
       COM_STMT_EXECUTE (even if it is not necessary), so either this or
       previous branch always works.
@@ -4828,6 +4834,58 @@ 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");
+
+  rc= mysql_next_result(mysql);
+
+  if (rc)
+    DBUG_RETURN(rc);
+
+  if (!(mysql->server_status & SERVER_MORE_RESULTS_EXISTS))
+    DBUG_RETURN(-1);
+
+  stmt->state= MYSQL_STMT_EXECUTE_DONE;
+  stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+  stmt->unbuffered_fetch_cancelled= FALSE;
+  stmt->read_row_func= stmt_read_row_unbuffered;
+  stmt->mysql->status= MYSQL_STATUS_GET_RESULT;
+
+  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
+    {
+      mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+      stmt->unbuffered_fetch_cancelled= FALSE;
+      stmt->read_row_func= stmt_read_row_unbuffered;
+    }
+  }
+
+  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-07-18 15:54:23 +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-07-18 15:54:23 +0000
@@ -2920,4 +2920,142 @@ 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_str;
+DROP PROCEDURE IF EXISTS p_dbl;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_dec;
+
+CREATE PROCEDURE p_str(
+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_dbl(
+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_dec(
+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_str(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_dbl(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_dec(?, ?, ?, ?)';
+
+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_str;
+DROP PROCEDURE p_dbl;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_dec;
+
+# 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-07-18 15:54:23 +0000
@@ -2998,5 +2998,164 @@ 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
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p_str;
+DROP PROCEDURE IF EXISTS p_dbl;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_dec;
+--enable_warnings
+
+delimiter |;
+
+--echo
+CREATE PROCEDURE p_str(
+  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_dbl(
+  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_dec(
+  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_str(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_dbl(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_dec(?, ?, ?, ?)';
+
+--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_str;
+DROP PROCEDURE p_dbl;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_dec;
+
+--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-07-18 15:54:23 +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,101 @@ 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:
+      return TRUE;
+  }
+
+  item_result_type= value->result_type();
+  item_type= value->type();
+  return FALSE;
+}
+
+void
+Item_param::set_out_param_info(Out_param_info *opi)
+{
+  m_out_param_info= opi;
+}
+
+const Out_param_info *
+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)
+  {
+    // 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->sp_name;
+    field->org_table_name= m_out_param_info->sp_name;
+    field->col_name= m_out_param_info->var_name;
+    field->org_col_name= m_out_param_info->var_name;
+  }
 }
 
 /****************************************************************************

=== modified file 'sql/item.h'
--- a/sql/item.h	2008-06-17 20:04:19 +0000
+++ b/sql/item.h	2008-07-18 15:54:23 +0000
@@ -19,6 +19,7 @@
 #endif
 
 class Protocol;
+class Out_param_info;
 struct TABLE_LIST;
 void item_init(void);			/* Init item functions */
 class Item_field;
@@ -443,6 +444,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(Out_param_info *opi) {}
+
+  virtual const Out_param_info *get_out_param_info() const
+  { return NULL; }
 };
 
 
@@ -1609,7 +1615,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 +1704,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 +1754,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(Out_param_info *opi);
+
+public:
+  virtual const Out_param_info *get_out_param_info() const;
+
+  virtual void make_field(Send_field *field);
+
+private:
+  Out_param_info *m_out_param_info;
 };
 
 

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-06-28 11:00:59 +0000
+++ b/sql/mysql_priv.h	2008-07-18 15:54:23 +0000
@@ -2559,6 +2559,21 @@ bool load_collation(MEM_ROOT *mem_root,
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
+struct Out_param_info
+{
+  const char *db_name;
+  const char *sp_name;
+  const char *var_name;
+
+  inline Out_param_info(const char *p_db_name,
+                        const char *p_sp_name,
+                        const char *p_var_name) :
+    db_name(p_db_name),
+    sp_name(p_sp_name),
+    var_name(p_var_name)
+  { }
+};
+
 #endif /* MYSQL_SERVER */
 #endif /* MYSQL_CLIENT */
 

=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc	2008-06-05 16:11:22 +0000
+++ b/sql/protocol.cc	2008-07-18 15:54:23 +0000
@@ -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),
@@ -1044,6 +1044,43 @@ bool Protocol_text::store_time(MYSQL_TIM
   return net_store_data((uchar*) buff, length);
 }
 
+bool Protocol_text::send_out_parameters(Statement *ps)
+{
+  DBUG_ASSERT(ps->lex->param_list.elements ==
+              thd->lex->prepared_stmt_params.elements);
+
+  List_iterator_fast<Item_param> item_param_it(ps->lex->param_list);
+  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 +1101,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 +1294,78 @@ 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);
+}
+
+bool Protocol_binary::send_out_parameters(Statement *ps)
+{
+  if (!(thd->client_capabilities & CLIENT_PS_OUT_PARAMS))
+  {
+    // The client does not support OUT-parameters.
+    return FALSE;
+  }
+
+  List<Item> out_param_lst;
+
+  {
+    List_iterator_fast<Item_param> item_param_it(ps->lex->param_list);
+
+    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.
+
+  {
+    char str_buffer[STRING_BUFFER_USUAL_SIZE];
+    String str(str_buffer, sizeof (str_buffer), &my_charset_bin);
+
+    prepare_for_resend();
+
+    List_iterator_fast<Item> item_it(out_param_lst);
+
+    while (true)
+    {
+      Item *item= item_it++;
+
+      if (!item)
+        break;
+
+      item->send(this, &str);
+    }
+
+    write();
+  }
+
+  // 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-07-18 15:54:23 +0000
@@ -20,6 +20,7 @@
 
 class i_string;
 class THD;
+class Statement;
 typedef struct st_mysql_field MYSQL_FIELD;
 typedef struct st_mysql_rows MYSQL_ROWS;
 
@@ -71,9 +72,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 +96,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(Statement *ps)=0;
 #ifdef EMBEDDED_LIBRARY
   int begin_dataset();
   virtual void remove_last_row() {}
@@ -136,6 +139,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(Statement *ps);
 #ifdef EMBEDDED_LIBRARY
   void remove_last_row();
 #endif
@@ -150,7 +155,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 +176,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(Statement *ps);
+
   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-07-18 15:54:23 +0000
@@ -1994,6 +1994,10 @@ sp_head::execute_procedure(THD *thd, Lis
       if (spvar->mode == sp_param_in)
         continue;
 
+      Out_param_info *out_param_info=
+        new Out_param_info(m_db.str, m_name.str, spvar->name.str);
+      // XXX: it seems, it will be allocate at PS's memroot => not freed.
+
       Settable_routine_parameter *srp=
         arg_item->get_settable_routine_parameter();
 
@@ -2004,6 +2008,8 @@ sp_head::execute_procedure(THD *thd, Lis
         err_status= TRUE;
         break;
       }
+
+      srp->set_out_param_info(out_param_info);
     }
   }
 

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2008-06-28 11:00:59 +0000
+++ b/sql/sql_prepare.cc	2008-07-18 15:54:23 +0000
@@ -3566,6 +3566,8 @@ bool Prepared_statement::execute(String 
   if (state == Query_arena::PREPARED)
     state= Query_arena::EXECUTED;
 
+  protocol->send_out_parameters(this);
+
   /*
     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-07-18 15:54:23 +0000
@@ -1499,6 +1499,503 @@ static void test_prepare_simple()
   myquery(rc);
 }
 
+///////////////////////////////////////////////////////////////////////////
+
+#define WL4435_NUM_PARAMS 10
+#define WL4435_STRING_SIZE 30
+
+#define WL4435_TEST_RESULT_SIZE 10000
+
+static void test_wl4435()
+{
+  MYSQL *con;
+  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;
+
+  char test_result[WL4435_TEST_RESULT_SIZE];
+  test_result[0]= 0;
+
+  char expected_test_result[] =
+    "\n"
+    "exec_counter: 0\n"
+    "server_status: 002a\n"
+    "num_fields: 4\n"
+    "  - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 1; str: '11'; dbl: 12.340000%; dec: '56.7';\n"
+    "  int: 2; str: '12'; dbl: 56.780000%; dec: '90.1';\n"
+    "  int: 3; str: '13'; dbl: 23.450000%; dec: '67.8';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 002a\n"
+    "num_fields: 5\n"
+    "  - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 100; int: 10; str: '110'; dbl: 70.700000%; dec: '10.1';\n"
+    "  int: 200; int: 20; str: '120'; dbl: 80.800000%; dec: '20.2';\n"
+    "  int: 300; int: 30; str: '130'; dbl: 90.900000%; dec: '30.3';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 102a\n"
+    "num_fields: 8\n"
+    "  - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 7; max_length: 0; type: 246; decimals: 3\n"
+    "  - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 4\n"
+    "  - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "Data:\n"
+    "  str: 'test_1'; dbl: 12.340000%; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000%; dec: '234.6789'; int: 6789;\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): -1; server_status: 0022\n"
+    "\n"
+    "exec_counter: 1\n"
+    "server_status: 002a\n"
+    "num_fields: 4\n"
+    "  - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 1; str: '11'; dbl: 12.340000%; dec: '56.7';\n"
+    "  int: 2; str: '12'; dbl: 56.780000%; dec: '90.1';\n"
+    "  int: 3; str: '13'; dbl: 23.450000%; dec: '67.8';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 002a\n"
+    "num_fields: 5\n"
+    "  - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 100; int: 10; str: '110'; dbl: 70.700000%; dec: '10.1';\n"
+    "  int: 200; int: 20; str: '120'; dbl: 80.800000%; dec: '20.2';\n"
+    "  int: 300; int: 30; str: '130'; dbl: 90.900000%; dec: '30.3';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 102a\n"
+    "num_fields: 8\n"
+    "  - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 7; max_length: 0; type: 246; decimals: 3\n"
+    "  - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 4\n"
+    "  - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "Data:\n"
+    "  str: 'test_1'; dbl: 12.340000%; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000%; dec: '234.6789'; int: 6789;\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): -1; server_status: 0022\n"
+    "\n"
+    "exec_counter: 2\n"
+    "server_status: 002a\n"
+    "num_fields: 4\n"
+    "  - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 1; str: '11'; dbl: 12.340000%; dec: '56.7';\n"
+    "  int: 2; str: '12'; dbl: 56.780000%; dec: '90.1';\n"
+    "  int: 3; str: '13'; dbl: 23.450000%; dec: '67.8';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 002a\n"
+    "num_fields: 5\n"
+    "  - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 32; max_length: 0; type: 254; decimals: 0\n"
+    "  - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 4; max_length: 0; type: 5; decimals: 2\n"
+    "  - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length: 5; max_length: 0; type: 246; decimals: 1\n"
+    "Data:\n"
+    "  int: 100; int: 10; str: '110'; dbl: 70.700000%; dec: '10.1';\n"
+    "  int: 200; int: 20; str: '120'; dbl: 80.800000%; dec: '20.2';\n"
+    "  int: 300; int: 30; str: '130'; dbl: 90.900000%; dec: '30.3';\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): 0; server_status: 102a\n"
+    "num_fields: 8\n"
+    "  - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 7; max_length: 0; type: 246; decimals: 3\n"
+    "  - 3: name: 'v_int_1'/'v_int_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "  - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 6; max_length: 0; type: 254; decimals: 0\n"
+    "  - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 23; max_length: 0; type: 5; decimals: 31\n"
+    "  - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 8; max_length: 0; type: 246; decimals: 4\n"
+    "  - 7: name: 'v_int_2'/'v_int_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def'; length: 11; max_length: 0; type: 3; decimals: 0\n"
+    "Data:\n"
+    "  str: 'test_1'; dbl: 12.340000%; dec: '567.891'; int: 2345; str: 'test_2'; dbl: 67.891000%; dec: '234.6789'; int: 6789;\n"
+    "EOF\n"
+    "mysql_stmt_next_result(): -1; server_status: 0022\n";
+
+
+  myheader("test_wl4435");
+
+  con= mysql_init(NULL);
+  if (!con)
+  {
+    fprintf(stderr, "\n mysql_init() failed");
+    exit(1);
+  }
+
+  if (!(mysql_real_connect(con, opt_host, opt_user,
+                           opt_password, current_db, opt_port,
+                           opt_unix_socket, CLIENT_MULTI_RESULTS)))
+  {
+    fprintf(stderr, "\n connection failed(%s)", mysql_error(con));
+    exit(1);
+  }
+  con->reconnect= 1;
+
+  rc= mysql_query(con, "DROP PROCEDURE IF EXISTS p1");
+  myquery(rc);
+
+  rc= mysql_query(con, "DROP TABLE IF EXISTS t1");
+  myquery(rc);
+
+  rc= mysql_query(con, "DROP TABLE IF EXISTS t2");
+  myquery(rc);
+
+  rc= mysql_query(con, "CREATE TABLE t1(a1 INT, a2 CHAR(32), "
+                       "  a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
+  myquery(rc);
+
+  rc= mysql_query(con, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), "
+                       "  b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
+  myquery(rc);
+
+  rc= mysql_query(con, "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(con, "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(con,
+    "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);
+
+  strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+  stmt= mysql_simple_prepare(con, 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;
+
+    my_snprintf(test_result,
+                WL4435_TEST_RESULT_SIZE,
+                "%s\nexec_counter: %d\n",
+                (const char *) test_result,
+                (int) exec_counter);
+
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+
+    my_snprintf(test_result,
+                WL4435_TEST_RESULT_SIZE,
+                "%sserver_status: %04x\n",
+                (const char *) test_result,
+                (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);
+
+      my_snprintf(test_result,
+                  WL4435_TEST_RESULT_SIZE,
+                  "%snum_fields: %d\n",
+                  (const char *) test_result,
+                  (int) num_fields);
+
+      for (i = 0; i < num_fields; ++i)
+      {
+        my_snprintf(test_result,
+                    WL4435_TEST_RESULT_SIZE,
+                    "%s  - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+                    "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+                    "type: %d; decimals: %d\n",
+                    (const char *) test_result,
+                    (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);
+
+      my_snprintf(test_result,
+                  WL4435_TEST_RESULT_SIZE,
+                  "%sData:\n",
+                  (const char *) test_result);
+
+      while (1)
+      {
+        int rc= mysql_stmt_fetch(stmt);
+
+        if (rc == 1 || rc == MYSQL_NO_DATA)
+          break;
+
+        my_snprintf(test_result, WL4435_TEST_RESULT_SIZE, "%s ",
+                    (const char *) test_result);
+
+        for (i = 0; i < num_fields; ++i)
+        {
+          switch (rs_bind[i].buffer_type)
+          {
+            case MYSQL_TYPE_LONG:
+              my_snprintf(test_result, WL4435_TEST_RESULT_SIZE,
+                          "%s int: %ld;",
+                          (const char *) test_result,
+                          (long) *((int *) rs_bind[i].buffer));
+              break;
+
+            case MYSQL_TYPE_STRING:
+              my_snprintf(test_result, WL4435_TEST_RESULT_SIZE,
+                          "%s str: '%s';",
+                          (const char *) test_result,
+                          (char *) rs_bind[i].buffer);
+              break;
+
+            case MYSQL_TYPE_DOUBLE:
+              my_snprintf(test_result, WL4435_TEST_RESULT_SIZE,
+                          "%s dbl: %lf;",
+                          (const char *) test_result,
+                          (double) *((double *) rs_bind[i].buffer));
+              break;
+
+            case MYSQL_TYPE_NEWDECIMAL:
+              my_snprintf(test_result, WL4435_TEST_RESULT_SIZE,
+                          "%s dec: '%s';",
+                          (const char *) test_result,
+                          (char *) rs_bind[i].buffer);
+              break;
+
+            default:
+              printf("  unexpected type (%d)\n",
+                rs_bind[i].buffer_type);
+          }
+        }
+        my_snprintf(test_result, WL4435_TEST_RESULT_SIZE, "%s\n",
+                    (const char *) test_result);
+      }
+
+      my_snprintf(test_result, WL4435_TEST_RESULT_SIZE, "%sEOF\n",
+                  (const char *) test_result);
+
+      rc= mysql_stmt_next_result(stmt);
+      my_snprintf(test_result, WL4435_TEST_RESULT_SIZE,
+                  "%smysql_stmt_next_result(): %d; server_status: %04x\n",
+                  (const char *) test_result,
+                  (int) rc, (int) stmt->mysql->server_status);
+
+      if (rc > 0)
+      {
+        printf("Error: %s (errno: %d)\n",
+               mysql_error(con), mysql_errno(con));
+        DIE(rc > 0);
+      }
+
+      if (rc)
+        break;
+    }
+  }
+
+  mysql_stmt_close(stmt);
+
+  rc= mysql_commit(con);
+  myquery(rc);
+
+  mysql_close(con);
+
+  printf("\n---------\n%s-------------\n", test_result);
+
+  mytest_r(strcmp(test_result, expected_test_result));
+}
 
 /* Test simple prepare field results */
 
@@ -18139,6 +18636,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 }
 };
 

Thread
bzr commit into mysql-6.0 branch (alik:2675) WL#4435Alexander Nozdrin18 Jul
  • Re: bzr commit into mysql-6.0 branch (alik:2675) WL#4435Konstantin Osipov23 Jul