#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 }
};