List:Commits« Previous MessageNext Message »
From:Alexander Nozdrin Date:June 17 2008 8:08am
Subject:bzr commit into mysql-6.0 branch (alik:2652) WL#4435
View as plain text  
#At file:///mnt/raid/alik/MySQL/devel/bug-11638/6.0-rt-wl4435/

 2652 Alexander Nozdrin	2008-06-17
      A draft 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/item_func.cc
  sql/item_func.h
  sql/mysql_priv.h
  sql/protocol.cc
  sql/protocol.h
  sql/sp_head.cc
  sql/sql_class.cc
  sql/sql_class.h
  sql/sql_prepare.cc
  tests/mysql_client_test.c

per-file messages:
  include/mysql.h
    Add a prototype for new mysql_stmt_next_result().
  include/mysql_com.h
    Add CLIENT_PS_OUT_PARAMS -- a client capability indicating that the client supports OUT-parameters.
  libmysql/libmysql.c
    Add mysql_stmt_next_result() -- analogue of mysql_next_result() for binary protocol.
  libmysqld/lib_sql.cc
    Refactoring: change prepare_for_send() so that it accepts only what it really needs -- a number of elements in the list.
  mysql-test/t/ps.test
    A test case for an SQL-part of the problem.
  sql/protocol.cc
    Refactoring: change prepare_for_send() so that it accepts only what it really needs -- a number of elements in the list.
  sql/protocol.h
    Refactoring: change prepare_for_send() so that it accepts only what it really needs -- a number of elements in the list.
  sql/sql_class.cc
    Add a context (THD) attribute to get/indicate the current prepared statement (if any).
  sql/sql_class.h
    Add a context (THD) attribute to get/indicate the current prepared statement (if any).
  tests/mysql_client_test.c
    Add a test case for a binary part of the problem.
=== modified file 'include/mysql.h'
--- a/include/mysql.h	2007-11-26 19:11:48 +0000
+++ b/include/mysql.h	2008-06-17 08:07:59 +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-05-21 10:17:29 +0000
+++ b/include/mysql_com.h	2008-06-17 08:07:59 +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)
 

=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h	2007-09-29 19:31:08 +0000
+++ b/libmysql/client_settings.h	2008-06-17 08:07:59 +0000
@@ -16,9 +16,12 @@
 extern uint		mysql_port;
 extern char *	mysql_unix_port;
 
-#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG |	  \
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
+                             CLIENT_LONG_FLAG | \
                              CLIENT_TRANSACTIONS | \
-			     CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
+			     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-05-21 10:17:29 +0000
+++ b/libmysql/libmysql.c	2008-06-17 08:07:59 +0000
@@ -4832,6 +4832,29 @@ int STDCALL mysql_next_result(MYSQL *mys
 }
 
 
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
+{
+  int rc;
+  DBUG_ENTER("mysql_stmt_next_result");
+
+  rc= mysql_next_result(stmt->mysql);
+
+  if (rc)
+    DBUG_RETURN(rc);
+
+  if (!(stmt->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;
+
+  DBUG_RETURN(0);
+}
+
+
 MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql)
 {
   return (*mysql->methods->use_result)(mysql);

=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc	2008-05-21 10:17:29 +0000
+++ b/libmysqld/lib_sql.cc	2008-06-17 08:07:59 +0000
@@ -963,7 +963,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-21 10:17:29 +0000
+++ b/mysql-test/r/ps.result	2008-06-17 08:07:59 +0000
@@ -2920,4 +2920,33 @@ 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.
+
+#
+# Bug#11638: Cannot prepare and execute a stored procedure with
+# OUT parameter.
+#
+
+DROP PROCEDURE IF EXISTS p1;
+CREATE PROCEDURE p1(OUT v INT) SET v = 1;
+PREPARE stmt FROM 'CALL p1(?)';
+
+SET @x1 = NULL;
+EXECUTE stmt USING @x1;
+SELECT @x1;
+@x1
+1
+
+SET @x2 = 0;
+EXECUTE stmt USING @x2;
+SELECT @x2;
+@x2
+1
+
+DEALLOCATE PREPARE stmt;
+DROP PROCEDURE p1;
+
+# End of Bug#11638.
+
+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-06-17 08:07:59 +0000
@@ -2998,5 +2998,48 @@ execute stmt;
 drop table t1;
 deallocate prepare stmt;
 
+###########################################################################
 
+--echo 
 --echo End of 5.1 tests.
+
+###########################################################################
+
+--echo
+--echo #
+--echo # Bug#11638: Cannot prepare and execute a stored procedure with
+--echo # OUT parameter.
+--echo #
+--echo
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+--enable_warnings
+
+CREATE PROCEDURE p1(OUT v INT) SET v = 1;
+
+PREPARE stmt FROM 'CALL p1(?)';
+
+--echo
+SET @x1 = NULL;
+EXECUTE stmt USING @x1;
+SELECT @x1;
+
+--echo
+SET @x2 = 0;
+EXECUTE stmt USING @x2;
+SELECT @x2;
+
+--echo
+DEALLOCATE PREPARE stmt;
+DROP PROCEDURE p1;
+
+--echo
+--echo # End of Bug#11638.
+
+###########################################################################
+
+--echo
+--echo End of 6.0 tests.
+
+###########################################################################

=== modified file 'sql/item.cc'
--- a/sql/item.cc	2008-05-21 10:17:29 +0000
+++ b/sql/item.cc	2008-06-17 08:07:59 +0000
@@ -1140,7 +1140,10 @@ void Item_splocal::print(String *str, en
 }
 
 
-bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+bool Item_splocal::set_value(THD *thd,
+                             sp_rcontext *ctx,
+                             Item **it,
+                             Out_param_info *)
 {
   return ctx->set_variable(thd, get_var_idx(), it);
 }
@@ -2516,7 +2519,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_user_var_name(NULL)
 {
   name= (char*) "?";
   /* 
@@ -2693,9 +2697,12 @@ bool Item_param::set_longdata(const char
     1 Out of memory
 */
 
-bool Item_param::set_from_user_var(THD *thd, const user_var_entry *entry)
+bool Item_param::set_from_user_var(THD *thd,
+                                   const LEX_STRING *user_var_name,
+                                   const user_var_entry *entry)
 {
   DBUG_ENTER("Item_param::set_from_user_var");
+
   if (entry && entry->value)
   {
     item_result_type= entry->type;
@@ -2761,6 +2768,8 @@ bool Item_param::set_from_user_var(THD *
   else
     set_null();
 
+  m_user_var_name= user_var_name;
+
   DBUG_RETURN(0);
 }
 
@@ -3156,6 +3165,37 @@ Item_param::eq(const Item *arg, bool bin
   return FALSE;
 }
 
+void ps_add_out_parameter(THD *thd,
+                          Item_param *param_item,
+                          Item *value_item,
+                          Out_param_info *info);
+
+
+bool Item_param::set_value(THD *thd,
+                           sp_rcontext *ctx,
+                           Item **it,
+                           Out_param_info *out_param_info)
+{
+  if (m_user_var_name)
+  {
+    Item_func_set_user_var *suv=
+      new Item_func_set_user_var(*m_user_var_name, *it);
+    /*
+      Item_func_set_user_var is not fixed after construction, call
+      fix_fields().
+    */
+    return !suv ||
+           suv->fix_fields(thd, it) ||
+           suv->check(0) ||
+           suv->update();
+  }
+  else
+  {
+    ps_add_out_parameter(thd, this, *it, out_param_info);
+    return FALSE;
+  }
+}
+
 /* End of Item_param related */
 
 void Item_param::print(String *str, enum_query_type query_type)
@@ -6388,7 +6428,10 @@ void Item_trigger_field::set_required_pr
 }
 
 
-bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it)
+bool Item_trigger_field::set_value(THD *thd,
+                                   sp_rcontext * /*ctx*/,
+                                   Item **it,
+                                   Out_param_info *)
 {
   Item *item= sp_prepare_func_item(thd, it);
 

=== modified file 'sql/item.h'
--- a/sql/item.h	2008-05-21 10:17:29 +0000
+++ b/sql/item.h	2008-06-17 08:07:59 +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;
@@ -442,7 +443,10 @@ public:
       FALSE if parameter value has been set,
       TRUE if error has occured.
   */
-  virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+  virtual bool set_value(THD *thd,
+                         sp_rcontext *ctx,
+                         Item **it,
+                         Out_param_info *)= 0;
 };
 
 
@@ -1195,7 +1199,7 @@ public:
   inline enum_field_types field_type() const { return m_field_type; }
 
 private:
-  bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+  bool set_value(THD *thd, sp_rcontext *ctx, Item **it, Out_param_info *);
 
 public:
   Settable_routine_parameter *get_settable_routine_parameter()
@@ -1603,7 +1607,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;
@@ -1694,7 +1699,9 @@ public:
   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);
-  bool set_from_user_var(THD *thd, const user_var_entry *entry);
+  bool set_from_user_var(THD *thd,
+                         const LEX_STRING *user_var_name,
+                         const user_var_entry *entry);
   void reset();
   /*
     Assign placeholder value from bind data.
@@ -1740,6 +1747,20 @@ public:
   /** Item is a argument to a limit clause. */
   bool limit_clause_param;
   void set_param_type_and_swap_value(Item_param *from);
+
+  virtual inline Settable_routine_parameter *
+    get_settable_routine_parameter()
+  {
+    return this;
+  }
+
+  virtual bool set_value(THD *thd,
+                         sp_rcontext *ctx,
+                         Item **it,
+                         Out_param_info *out_param_info);
+
+private:
+  const LEX_STRING *m_user_var_name;
 };
 
 
@@ -2741,7 +2762,7 @@ public:
 
 private:
   void set_required_privilege(bool rw);
-  bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+  bool set_value(THD *thd, sp_rcontext *ctx, Item **it, Out_param_info *);
 
 public:
   Settable_routine_parameter *get_settable_routine_parameter()
@@ -2751,7 +2772,7 @@ public:
 
   bool set_value(THD *thd, Item **it)
   {
-    return set_value(thd, NULL, it);
+    return set_value(thd, NULL, it, NULL);
   }
 
 private:

=== modified file 'sql/item_func.cc'
--- a/sql/item_func.cc	2008-04-17 14:31:44 +0000
+++ b/sql/item_func.cc	2008-06-17 08:07:59 +0000
@@ -4763,7 +4763,9 @@ bool Item_func_get_user_var::eq(const It
 
 
 bool Item_func_get_user_var::set_value(THD *thd,
-                                       sp_rcontext * /*ctx*/, Item **it)
+                                       sp_rcontext * /*ctx*/,
+                                       Item **it,
+                                       Out_param_info *)
 {
   Item_func_set_user_var *suv= new Item_func_set_user_var(get_name(), *it);
   /*

=== modified file 'sql/item_func.h'
--- a/sql/item_func.h	2008-03-27 19:02:15 +0000
+++ b/sql/item_func.h	2008-06-17 08:07:59 +0000
@@ -1371,7 +1371,7 @@ public:
   { return const_item() ? 0 : RAND_TABLE_BIT; }
   bool eq(const Item *item, bool binary_cmp) const;
 private:
-  bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+  bool set_value(THD *thd, sp_rcontext *ctx, Item **it, struct Out_param_info *);
 
 public:
   Settable_routine_parameter *get_settable_routine_parameter()

=== modified file 'sql/mysql_priv.h'
--- a/sql/mysql_priv.h	2008-05-21 10:17:29 +0000
+++ b/sql/mysql_priv.h	2008-06-17 08:07:59 +0000
@@ -2568,6 +2568,13 @@ bool load_collation(MEM_ROOT *mem_root,
                     CHARSET_INFO *dflt_cl,
                     CHARSET_INFO **cl);
 
+struct Out_param_info
+{
+  LEX_STRING db_name;
+  LEX_STRING sp_name;
+  LEX_STRING var_name;
+};
+
 #endif /* MYSQL_SERVER */
 #endif /* MYSQL_CLIENT */
 

=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc	2008-05-08 16:01:15 +0000
+++ b/sql/protocol.cc	2008-06-17 08:07:59 +0000
@@ -702,7 +702,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),
@@ -1062,14 +1062,15 @@ 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;
 }
 
 

=== modified file 'sql/protocol.h'
--- a/sql/protocol.h	2008-02-11 16:11:22 +0000
+++ b/sql/protocol.h	2008-06-17 08:07:59 +0000
@@ -71,9 +71,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();
@@ -150,7 +150,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();

=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc	2008-05-21 10:17:29 +0000
+++ b/sql/sp_head.cc	2008-06-17 08:07:59 +0000
@@ -1825,6 +1825,8 @@ err_with_cleanup:
     TRUE   on error
 */
 
+extern bool ps_send_out_parameters(THD *thd);
+
 bool
 sp_head::execute_procedure(THD *thd, List<Item> *args)
 {
@@ -1995,16 +1997,27 @@ sp_head::execute_procedure(THD *thd, Lis
       if (spvar->mode == sp_param_in)
         continue;
 
+      Out_param_info out_param_info;
+      out_param_info.db_name= m_db;
+      out_param_info.sp_name= m_name;
+      out_param_info.var_name= spvar->name;
+
       Settable_routine_parameter *srp=
         arg_item->get_settable_routine_parameter();
 
       DBUG_ASSERT(srp);
 
-      if (srp->set_value(thd, octx, nctx->get_item_addr(i)))
+      if (srp->set_value(thd, octx, nctx->get_item_addr(i), &out_param_info))
       {
         err_status= TRUE;
         break;
       }
+    }
+
+    if (!err_status)
+    {
+      ps_send_out_parameters(thd);
+      thd->main_da.reset_diagnostics_area();
     }
   }
 

=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc	2008-05-21 10:17:29 +0000
+++ b/sql/sql_class.cc	2008-06-17 08:07:59 +0000
@@ -533,6 +533,7 @@ THD::THD()
    derived_tables_processing(FALSE),
    spcont(NULL),
    m_lip(NULL),
+   current_ps(NULL),
   /*
     @todo The following is a work around for online backup and the DDL blocker.
           It should be removed when the generalized solution is in place.

=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h	2008-05-21 10:17:29 +0000
+++ b/sql/sql_class.h	2008-06-17 08:07:59 +0000
@@ -1303,6 +1303,7 @@ public:
   pthread_mutex_t LOCK_delete;		// Locked before thd is deleted
   /* all prepared statements and cursors of this connection */
   Statement_map stmt_map;
+  Statement *current_ps; // mat be NULL
   /*
     A pointer to the stack frame of handle_one_connection(),
     which is called first in the thread for handling a client

=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc	2008-05-21 19:44:56 +0000
+++ b/sql/sql_prepare.cc	2008-06-17 08:07:59 +0000
@@ -119,6 +119,42 @@ public:
 /****************************************************************************/
 
 /**
+  Out_param: OUT-parameter representation.
+*/
+
+struct Out_param
+{
+  inline Out_param(const LEX_STRING *p_db_name,
+                   const LEX_STRING *p_sp_name,
+                   const LEX_STRING *p_var_name,
+                   Item_param *p_param_item,
+                   Item *p_value_item);
+
+  LEX_STRING db_name;
+  LEX_STRING sp_name;
+  LEX_STRING var_name;
+
+  Item_param *param_item;
+  Item *value_item;
+};
+
+inline Out_param::Out_param(const LEX_STRING *p_db_name,
+                            const LEX_STRING *p_sp_name,
+                            const LEX_STRING *p_var_name,
+                            Item_param *p_param_item,
+                            Item *p_value_item)
+{
+  db_name= *p_db_name;
+  sp_name= *p_sp_name;
+  var_name= *p_var_name;
+
+  param_item= p_param_item;
+  value_item= p_value_item;
+}
+
+/****************************************************************************/
+
+/**
   Prepared_statement: a statement that can contain placeholders.
 */
 
@@ -138,6 +174,9 @@ public:
   uint last_errno;
   uint flags;
   char last_error[MYSQL_ERRMSG_SIZE];
+
+  List<Out_param> m_out_param_lst;
+
 #ifndef EMBEDDED_LIBRARY
   bool (*set_params)(Prepared_statement *st, uchar *data, uchar *data_end,
                      uchar *read_pos, String *expanded_query);
@@ -1026,7 +1065,7 @@ static bool insert_params_from_vars(Prep
     entry= (user_var_entry*)hash_search(&stmt->thd->user_vars,
                                         (uchar*) varname->str,
                                          varname->length);
-    if (param->set_from_user_var(stmt->thd, entry) ||
+    if (param->set_from_user_var(stmt->thd, varname, entry) ||
         param->convert_str_value(stmt->thd))
       DBUG_RETURN(1);
   }
@@ -1078,7 +1117,7 @@ static bool insert_params_from_vars_with
       (e.g. value.cs_info.character_set_client is used in the query_val_str()).
     */
     setup_one_conversion_function(thd, param, param->param_type);
-    if (param->set_from_user_var(thd, entry))
+    if (param->set_from_user_var(thd, varname, entry))
       DBUG_RETURN(1);
     val= param->query_val_str(&buf);
 
@@ -3538,7 +3577,9 @@ bool Prepared_statement::execute(String 
     if (query_cache_send_result_to_client(thd, thd->query,
                                           thd->query_length) <= 0)
     {
+      thd->current_ps= this;
       error= mysql_execute_command(thd);
+      thd->current_ps= NULL;
     }
   }
 
@@ -3599,4 +3640,227 @@ void Prepared_statement::deallocate()
   status_var_increment(thd->status_var.com_stmt_close);
   /* Statement map calls delete stmt on erase */
   thd->stmt_map.erase(this);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+void ps_add_out_parameter(THD *thd,
+                          Item_param *param_item,
+                          Item *value_item,
+                          Out_param_info *info)
+{
+  Prepared_statement *ps= (Prepared_statement *) thd->current_ps;
+
+  if (!ps)
+  {
+    // We've been called not from PS.
+    return;
+  }
+
+  if (!(thd->client_capabilities & CLIENT_PS_OUT_PARAMS))
+  {
+    // The client does not support OUT-parameters.
+    return;
+  }
+
+  ps->m_out_param_lst.push_back(new Out_param(&info->db_name,
+                                              &info->sp_name,
+                                              &info->var_name,
+                                              param_item,
+                                              value_item));
+}
+
+// FIXME
+void net_send_eof(THD *thd, uint server_status, uint total_warn_count);
+
+// TODO: this is actually a copy&paste from Protocol::send_field().
+
+bool ps_send_out_parameters(THD *thd)
+{
+  Prepared_statement *ps= (Prepared_statement *) thd->current_ps;
+
+  if (!ps)
+  {
+    // We've been called not from PS.
+    return FALSE;
+  }
+
+  if (!(thd->client_capabilities & CLIENT_PS_OUT_PARAMS))
+  {
+    // The client does not support OUT-parameters.
+    return FALSE;
+  }
+
+  // Write a number of columns (what's called SEND_NUM_ROWS).
+
+  {
+    uchar buffer[10];
+    uchar *pos= net_store_length(buffer, ps->m_out_param_lst.elements);
+    my_net_write(&thd->net, buffer, (size_t) (pos - buffer));
+  }
+
+  // Send meta-data (a number of Field-packets).
+  // Meta-data is encoded using text-protocol.
+
+  {
+    Protocol_text p(thd);
+    List_iterator_fast<Out_param> it(ps->m_out_param_lst);
+
+    while (true)
+    {
+      Out_param *out_param= it++;
+
+      if (!out_param)
+        break;
+
+      Send_field value_fld;
+      out_param->value_item->make_field(&value_fld);
+
+      p.prepare_for_resend();
+
+      bool status= FALSE;
+      CHARSET_INFO *sys_cs= system_charset_info;
+      CHARSET_INFO *res_cs= thd->variables.character_set_results;
+
+      // Store the following fields of Field-packet:
+      //  - 'def'
+      //  - database name
+      //  - table name (procedure name)
+      //  - original table name (procedure name)
+      //  - column name (parameter name)
+      //  - original column name (parameter name)
+
+      status= status || p.store(STRING_WITH_LEN("def"), sys_cs, res_cs);
+      status= status || p.store(out_param->db_name.str,
+                                out_param->db_name.length,
+                                sys_cs, res_cs);
+      status= status || p.store(out_param->sp_name.str,
+                                out_param->sp_name.length,
+                                sys_cs, res_cs);
+      status= status || p.store(out_param->sp_name.str,
+                                out_param->sp_name.length,
+                                sys_cs, res_cs);
+      status= status || p.store(out_param->var_name.str,
+                                out_param->var_name.length,
+                                sys_cs, res_cs);
+      status= status || p.store(out_param->var_name.str,
+                                out_param->var_name.length,
+                                sys_cs, res_cs);
+
+      if (status)
+      {
+        my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+        return TRUE;
+      }
+
+      String *packet_str= p.storage_packet();
+
+      if (packet_str->realloc(packet_str->length() + 12))
+      {
+        my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+        return TRUE;
+      }
+
+      // Store the filler (0x0c).
+
+      char *pos= (char *) packet_str->ptr() + packet_str->length();
+      *pos= 0x0c;
+      ++pos;
+
+      // Store collation.
+
+      if (out_param->param_item->collation.collation == &my_charset_bin ||
+          res_cs == NULL)
+      {
+        /* No conversion */
+        int2store(pos, value_fld.charsetnr);
+        int4store(pos + 2, value_fld.length);
+      }
+      else
+      {
+        /* With conversion */
+        uint max_char_len;
+        int2store(pos, res_cs->number);
+        /*
+          For TEXT/BLOB columns, field_length describes the maximum data
+          length in bytes. There is no limit to the number of characters
+          that a TEXT column can store, as long as the data fits into
+          the designated space.
+          For the rest of textual columns, field_length is evaluated as
+          char_count * mbmaxlen, where character count is taken from the
+          definition of the column. In other words, the maximum number
+          of characters here is limited by the column definition.
+        */
+        max_char_len= (value_fld.type >= (int) MYSQL_TYPE_TINY_BLOB &&
+                      value_fld.type <= (int) MYSQL_TYPE_BLOB) ?
+                      value_fld.length /
+                        out_param->value_item->collation.collation->mbminlen :
+                      value_fld.length /
+                        out_param->value_item->collation.collation->mbmaxlen;
+        int4store(pos + 2, max_char_len * res_cs->mbmaxlen);
+      }
+
+      // Store other fields:
+      //   - Field type;
+
+      pos[6]= value_fld.type;
+
+      //   - Flags;
+
+      int2store(pos + 7, value_fld.flags);
+
+      //   - Decimals;
+
+      pos[9]= (char) value_fld.decimals;
+      pos[10]= 0;
+      pos[11]= 0;
+
+      // That's it.
+
+      pos+= 12;
+      packet_str->length(pos - packet_str->ptr());
+
+      if (p.write())
+      {
+        my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+        return TRUE;
+      }
+    }
+  }
+
+  // Send EOF-packet.
+
+  net_send_eof(thd, thd->server_status, thd->total_warn_count);
+
+  // Send data.
+
+  {
+    Protocol *p= thd->protocol;
+    p->prepare_for_resend();
+
+    List_iterator_fast<Out_param> it(ps->m_out_param_lst);
+
+    while (true)
+    {
+      Out_param *out_param= it++;
+
+      if (!out_param)
+        break;
+
+      out_param->value_item->send(p, NULL);
+    }
+
+    p->write();
+    p->flush();
+  }
+
+  // Send EOF-packet.
+
+  net_send_eof(thd, thd->server_status, thd->total_warn_count);
+
+  // Clear Out-parameter list.
+
+  ps->m_out_param_lst.delete_elements();
+
+  return FALSE;
 }

=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c	2008-05-26 12:12:28 +0000
+++ b/tests/mysql_client_test.c	2008-06-17 08:07:59 +0000
@@ -1497,6 +1497,163 @@ static void test_prepare_simple()
   myquery(rc);
 }
 
+///////////////////////////////////////////////////////////////////////////
+
+static void test_alik()
+{
+  MYSQL *con;
+  MYSQL_STMT *stmt;
+  int        rc;
+  char query[MAX_TEST_QUERY_LENGTH];
+
+  char       str_data[20];
+  ulong      length;
+  my_bool    is_null;
+  MYSQL_BIND ps_params[2];
+
+  int exec_counter;
+
+  myheader("test_alik");
+
+  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 FUNCTION IF EXISTS f1");
+  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 CHAR(10), a2 CHAR(10))");
+  rc= mysql_query(con, "CREATE TABLE t1(a1 INT, a2 INT)");
+  myquery(rc);
+
+  rc= mysql_query(con, "INSERT INTO t1 VALUES(1, 2), (3, 4), (5, 6)");
+  myquery(rc);
+
+  rc= mysql_query(con, "CREATE PROCEDURE p1(OUT v1 INT, OUT v2 INT) "
+                         "BEGIN "
+                         "  SET v1 = 123; "
+                         "  SET v2 = 456; "
+                         "  SELECT * FROM t1; "
+                         "  SELECT * FROM t1; "
+                         "END");
+  myquery(rc);
+
+  strmov(query, "CALL p1(?, ?)");
+  stmt= mysql_simple_prepare(con, query);
+  check_stmt(stmt);
+
+  bzero((char *) ps_params, sizeof (ps_params));
+
+  {
+    int i;
+    for (i = 0; i < 2; ++i)
+    {
+      ps_params[i].buffer_type= MYSQL_TYPE_STRING;
+      ps_params[i].buffer= str_data;
+      ps_params[i].buffer_length= 20;
+      ps_params[i].length= &length;
+      ps_params[i].is_null= &is_null;
+      is_null= 0;
+      length= 0;
+    }
+  }
+
+  rc= mysql_stmt_bind_param(stmt, ps_params);
+
+  for (exec_counter= 0; exec_counter < 3; ++exec_counter)
+  {
+    rc= mysql_stmt_execute(stmt);
+    check_execute(stmt, rc);
+
+    while (1)
+    {
+      // char str_data[20][2];
+      int int_data[2];
+      my_bool is_null[2];
+      unsigned long length[2];
+      my_bool error[2];
+      MYSQL_BIND results[2];
+      int c;
+
+      memset(results, 0, sizeof (results));
+
+      for (c = 0; c < 2; ++c)
+      {
+        // results[c].buffer_type= MYSQL_TYPE_STRING;
+        // results[c].buffer= (char *)str_data[c];
+        results[c].buffer_type= MYSQL_TYPE_LONG;
+        results[c].buffer= (void *) &int_data[c];
+        results[c].buffer_length= 20;
+        results[c].is_null= &is_null[c];
+        results[c].length= &length[c];
+        results[c].error= &error[c];
+      }
+
+      rc= mysql_stmt_bind_result(stmt, results);
+      check_execute(stmt, rc);
+
+      {
+        printf("--> Result set:\n");
+
+        while (1)
+        {
+          int rc= mysql_stmt_fetch(stmt);
+
+          if (rc == 1 || rc == MYSQL_NO_DATA)
+            break;
+
+  // debug        printf("    - Row: '%.*s', '%.*s'\n",
+  // debug          (int) length[0],
+  // debug          (const char *) str_data[0],
+  // debug          (int) length[1],
+  // debug          (const char *) str_data[1]);
+          printf("    - Row: %d, %d\n",
+            (int) int_data[0], (int) int_data[1]);
+        }
+      }
+
+      rc= mysql_more_results(con);
+      printf("--> mysql_more_results(): %d\n", (int) rc);
+
+      rc= mysql_stmt_next_result(stmt);
+      printf("--> mysql_stmt_next_result(): %d\n", (int) rc);
+
+      if (rc > 0)
+        printf("--> Error: %s (errno: %d)\n", mysql_error(con), mysql_errno(con));
+
+      if (rc)
+        break;
+    }
+  }
+
+  mysql_stmt_close(stmt);
+
+  rc= mysql_commit(con);
+  myquery(rc);
+
+  mysql_close(con);
+}
 
 /* Test simple prepare field results */
 
@@ -18138,6 +18295,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_alik",     test_alik },
   { 0, 0 }
 };
 

Thread
bzr commit into mysql-6.0 branch (alik:2652) WL#4435Alexander Nozdrin17 Jun
  • Re: bzr commit into mysql-6.0 branch (alik:2652) WL#4435Konstantin Osipov30 Jun