2691 Alexander Nozdrin 2008-08-07
Patch for WL#4435: Support OUT-parameters in prepared statements.
After execution of a prepared statement, send OUT parameters of the invoked
stored procedure, if any, to the client.
When using the binary protocol, send the parameters in an additional result
set over the wire. When using the text protocol, assign out parameters to
the user variables from the CALL(@var1, @var2, ...) specification.
The following refactoring has been made:
- Protocol::send_fields() was renamed to Protocol::send_result_set_metadata();
- A new Protocol::send_result_set_row() was introduced to incapsulate
common functionality for sending row data.
- Signature of Protocol::prepare_for_send() was changed: this operation
does not need a list of items, the number of items is fully sufficient.
The following backward incompatible changes have been made:
- CLIENT_MULTI_RESULTS is now enabled by default in the client;
- CLIENT_PS_MULTI_RESUTLS is now enabled by default in the client.
added:
mysql-test/r/test_wl4435.result
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/backup/backup_test.cc
sql/backup/kernel.cc
sql/events.cc
sql/handler.cc
sql/item.cc
sql/item.h
sql/procedure.h
sql/protocol.cc
sql/protocol.h
sql/repl_failsafe.cc
sql/slave.cc
sql/sp_head.cc
sql/sql_acl.cc
sql/sql_class.cc
sql/sql_class.h
sql/sql_cursor.cc
sql/sql_error.cc
sql/sql_handler.cc
sql/sql_help.cc
sql/sql_prepare.cc
sql/sql_profile.cc
sql/sql_repl.cc
sql/sql_select.cc
sql/sql_select.h
sql/sql_show.cc
sql/sql_table.cc
tests/mysql_client_test.c
2690 Alexander Nozdrin 2008-08-05 [merge]
Merge from 6.0.
=== modified file 'include/mysql.h'
--- a/include/mysql.h 2007-11-26 19:11:48 +0000
+++ b/include/mysql.h 2008-08-07 17:52:43 +0000
@@ -714,6 +714,7 @@ my_bool STDCALL mysql_rollback(MYSQL * m
my_bool STDCALL mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool STDCALL mysql_more_results(MYSQL *mysql);
int STDCALL mysql_next_result(MYSQL *mysql);
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt);
void STDCALL mysql_close(MYSQL *sock);
=== modified file 'include/mysql_com.h'
--- a/include/mysql_com.h 2008-06-17 20:04:19 +0000
+++ b/include/mysql_com.h 2008-08-07 17:52:43 +0000
@@ -154,6 +154,7 @@ enum enum_server_command
#define CLIENT_SECURE_CONNECTION 32768 /* New 4.1 authentication */
#define CLIENT_MULTI_STATEMENTS (1UL << 16) /* Enable/disable multi-stmt support */
#define CLIENT_MULTI_RESULTS (1UL << 17) /* Enable/disable multi-results */
+#define CLIENT_PS_MULTI_RESULTS (1UL << 18) /* Multi-results in PS-protocol */
#define CLIENT_SSL_VERIFY_SERVER_CERT (1UL << 30)
#define CLIENT_REMEMBER_OPTIONS (1UL << 31)
@@ -177,6 +178,7 @@ enum enum_server_command
CLIENT_SECURE_CONNECTION | \
CLIENT_MULTI_STATEMENTS | \
CLIENT_MULTI_RESULTS | \
+ CLIENT_PS_MULTI_RESULTS | \
CLIENT_SSL_VERIFY_SERVER_CERT | \
CLIENT_REMEMBER_OPTIONS)
@@ -219,6 +221,11 @@ enum enum_server_command
to use. See WorkLog 4098.
*/
#define SERVER_QUERY_WAS_SLOW 2048
+
+/**
+ To mark ResultSet containing output parameter values.
+*/
+#define SERVER_PS_OUT_PARAMS 4096
/**
Server status flags that must be cleared when starting
=== modified file 'libmysql/client_settings.h'
--- a/libmysql/client_settings.h 2007-09-29 19:31:08 +0000
+++ b/libmysql/client_settings.h 2008-08-07 17:52:43 +0000
@@ -16,9 +16,13 @@
extern uint mysql_port;
extern char * mysql_unix_port;
-#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | CLIENT_LONG_FLAG | \
- CLIENT_TRANSACTIONS | \
- CLIENT_PROTOCOL_41 | CLIENT_SECURE_CONNECTION)
+#define CLIENT_CAPABILITIES (CLIENT_LONG_PASSWORD | \
+ CLIENT_LONG_FLAG | \
+ CLIENT_TRANSACTIONS | \
+ CLIENT_PROTOCOL_41 | \
+ CLIENT_SECURE_CONNECTION | \
+ CLIENT_MULTI_RESULTS | \
+ CLIENT_PS_MULTI_RESULTS)
sig_handler my_pipe_sig_handler(int sig);
void read_user_name(char *name);
=== modified file 'libmysql/libmysql.c'
--- a/libmysql/libmysql.c 2008-06-17 20:04:19 +0000
+++ b/libmysql/libmysql.c 2008-08-07 17:52:43 +0000
@@ -1725,6 +1725,8 @@ static void alloc_stmt_fields(MYSQL_STMT
MEM_ROOT *alloc= &stmt->mem_root;
MYSQL *mysql= stmt->mysql;
+ DBUG_ASSERT(mysql->field_count);
+
stmt->field_count= mysql->field_count;
/*
@@ -1746,6 +1748,7 @@ static void alloc_stmt_fields(MYSQL_STMT
field= stmt->fields;
field && fields < end; fields++, field++)
{
+ field->catalog = strdup_root(alloc,fields->catalog);
field->db = strdup_root(alloc,fields->db);
field->table = strdup_root(alloc,fields->table);
field->org_table= strdup_root(alloc,fields->org_table);
@@ -2484,6 +2487,33 @@ static void reinit_result_set_metadata(M
}
+static void prepare_to_fetch_result(MYSQL_STMT *stmt)
+{
+ if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
+ {
+ stmt->mysql->status= MYSQL_STATUS_READY;
+ stmt->read_row_func= stmt_read_row_from_cursor;
+ }
+ else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
+ {
+ /*
+ This is a single-row result set, a result set with no rows, EXPLAIN,
+ SHOW VARIABLES, or some other command which either a) bypasses the
+ cursors framework in the server and writes rows directly to the
+ network or b) is more efficient if all (few) result set rows are
+ precached on client and server's resources are freed.
+ */
+ mysql_stmt_store_result(stmt);
+ }
+ else
+ {
+ stmt->mysql->unbuffered_fetch_owner= &stmt->unbuffered_fetch_cancelled;
+ stmt->unbuffered_fetch_cancelled= FALSE;
+ stmt->read_row_func= stmt_read_row_unbuffered;
+ }
+}
+
+
/*
Send placeholders data to server (if there are placeholders)
and execute prepared statement.
@@ -2551,28 +2581,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STM
if (mysql->field_count)
{
reinit_result_set_metadata(stmt);
- if (stmt->server_status & SERVER_STATUS_CURSOR_EXISTS)
- {
- mysql->status= MYSQL_STATUS_READY;
- stmt->read_row_func= stmt_read_row_from_cursor;
- }
- else if (stmt->flags & CURSOR_TYPE_READ_ONLY)
- {
- /*
- This is a single-row result set, a result set with no rows, EXPLAIN,
- SHOW VARIABLES, or some other command which either a) bypasses the
- cursors framework in the server and writes rows directly to the
- network or b) is more efficient if all (few) result set rows are
- precached on client and server's resources are freed.
- */
- mysql_stmt_store_result(stmt);
- }
- else
- {
- stmt->mysql->unbuffered_fetch_owner=
&stmt->unbuffered_fetch_cancelled;
- stmt->unbuffered_fetch_cancelled= FALSE;
- stmt->read_row_func= stmt_read_row_unbuffered;
- }
+ prepare_to_fetch_result(stmt);
}
DBUG_RETURN(test(stmt->last_errno));
}
@@ -4828,6 +4837,46 @@ int STDCALL mysql_next_result(MYSQL *mys
DBUG_RETURN((*mysql->methods->next_result)(mysql));
DBUG_RETURN(-1); /* No more results */
+}
+
+
+int STDCALL mysql_stmt_next_result(MYSQL_STMT *stmt)
+{
+ MYSQL *mysql= stmt->mysql;
+ int rc;
+ DBUG_ENTER("mysql_stmt_next_result");
+
+ if (!mysql)
+ DBUG_RETURN(1);
+
+ if (stmt->last_errno)
+ DBUG_RETURN(stmt->last_errno);
+
+ if (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
+ {
+ if (reset_stmt_handle(stmt, RESET_STORE_RESULT))
+ DBUG_RETURN(1);
+ }
+
+ rc= mysql_next_result(mysql);
+
+ if (rc)
+ DBUG_RETURN(rc);
+
+ stmt->state= MYSQL_STMT_EXECUTE_DONE;
+ stmt->bind_result_done= FALSE;
+
+ if (mysql->field_count)
+ {
+ alloc_stmt_fields(stmt);
+ prepare_to_fetch_result(stmt);
+ }
+ else
+ {
+ stmt->field_count= mysql->field_count;
+ }
+
+ DBUG_RETURN(0);
}
=== modified file 'libmysqld/lib_sql.cc'
--- a/libmysqld/lib_sql.cc 2008-06-20 11:40:01 +0000
+++ b/libmysqld/lib_sql.cc 2008-08-07 17:52:43 +0000
@@ -870,7 +870,7 @@ void Protocol_text::remove_last_row()
}
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
@@ -879,7 +879,7 @@ bool Protocol::send_fields(List<Item> *l
CHARSET_INFO *thd_cs= thd->variables.character_set_results;
CHARSET_INFO *cs= system_charset_info;
MYSQL_DATA *data;
- DBUG_ENTER("send_fields");
+ DBUG_ENTER("send_result_set_metadata");
if (!thd->mysql) // bootstrap file handling
DBUG_RETURN(0);
@@ -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-07-24 10:00:56 +0000
+++ b/mysql-test/r/ps.result 2008-08-07 17:52:43 +0000
@@ -2920,4 +2920,165 @@ execute stmt;
Db Name Definer Time zone Type Execute at Interval value Interval
field Starts Ends Status Originator character_set_client collation_connection Database
Collation
drop table t1;
deallocate prepare stmt;
+
End of 5.1 tests.
+
+#
+# WL#4435: Support OUT-parameters in prepared statements.
+#
+
+DROP PROCEDURE IF EXISTS p_string;
+DROP PROCEDURE IF EXISTS p_double;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_decimal;
+
+CREATE PROCEDURE p_string(
+IN v0 INT,
+OUT v1 CHAR(32),
+IN v2 CHAR(32),
+INOUT v3 CHAR(32))
+BEGIN
+SET v0 = -1;
+SET v1 = 'test_v1';
+SET v2 = 'n/a';
+SET v3 = 'test_v3';
+END|
+
+CREATE PROCEDURE p_double(
+IN v0 INT,
+OUT v1 DOUBLE(4, 2),
+IN v2 DOUBLE(4, 2),
+INOUT v3 DOUBLE(4, 2))
+BEGIN
+SET v0 = -1;
+SET v1 = 12.34;
+SET v2 = 98.67;
+SET v3 = 56.78;
+END|
+
+CREATE PROCEDURE p_int(
+IN v0 CHAR(10),
+OUT v1 INT,
+IN v2 INT,
+INOUT v3 INT)
+BEGIN
+SET v0 = 'n/a';
+SET v1 = 1234;
+SET v2 = 9876;
+SET v3 = 5678;
+END|
+
+CREATE PROCEDURE p_decimal(
+IN v0 INT,
+OUT v1 DECIMAL(4, 2),
+IN v2 DECIMAL(4, 2),
+INOUT v3 DECIMAL(4, 2))
+BEGIN
+SET v0 = -1;
+SET v1 = 12.34;
+SET v2 = 98.67;
+SET v3 = 56.78;
+END|
+
+PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)';
+
+SET @x_str_1 = NULL;
+SET @x_str_2 = NULL;
+SET @x_str_3 = NULL;
+SET @x_dbl_1 = NULL;
+SET @x_dbl_2 = NULL;
+SET @x_dbl_3 = NULL;
+SET @x_int_1 = NULL;
+SET @x_int_2 = NULL;
+SET @x_int_3 = NULL;
+SET @x_dec_1 = NULL;
+SET @x_dec_2 = NULL;
+SET @x_dec_3 = NULL;
+
+-- Testing strings...
+
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+@x_int_1 @x_str_1 @x_str_2 @x_str_3
+NULL test_v1 NULL test_v3
+
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+@x_int_1 @x_str_1 @x_str_2 @x_str_3
+NULL test_v1 NULL test_v3
+
+-- Testing doubles...
+
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+@x_int_1 @x_dbl_1 @x_dbl_2 @x_dbl_3
+NULL 12.34 NULL 56.78
+
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+@x_int_1 @x_dbl_1 @x_dbl_2 @x_dbl_3
+NULL 12.34 NULL 56.78
+
+-- Testing ints...
+
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+@x_str_1 @x_int_1 @x_int_2 @x_int_3
+test_v1 1234 NULL 5678
+
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+@x_str_1 @x_int_1 @x_int_2 @x_int_3
+test_v1 1234 NULL 5678
+
+-- Testing decs...
+
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+@x_int_1 @x_dec_1 @x_dec_2 @x_dec_3
+1234 12.34 NULL 56.78
+
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+@x_int_1 @x_dec_1 @x_dec_2 @x_dec_3
+1234 12.34 NULL 56.78
+
+DEALLOCATE PREPARE stmt_str;
+DEALLOCATE PREPARE stmt_dbl;
+DEALLOCATE PREPARE stmt_int;
+DEALLOCATE PREPARE stmt_dec;
+
+DROP PROCEDURE p_string;
+DROP PROCEDURE p_double;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_decimal;
+
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+
+CREATE PROCEDURE p1(OUT v1 CHAR(10))
+SET v1 = 'test1';
+
+CREATE PROCEDURE p2(OUT v2 CHAR(10))
+BEGIN
+SET @query = 'CALL p1(?)';
+PREPARE stmt1 FROM @query;
+EXECUTE stmt1 USING @u1;
+DEALLOCATE PREPARE stmt1;
+SET v2 = @u1;
+END|
+
+CALL p2(@a);
+SELECT @a;
+@a
+test1
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+# End of WL#4435.
+
+End of 6.0 tests.
=== added file 'mysql-test/r/test_wl4435.result'
--- a/mysql-test/r/test_wl4435.result 1970-01-01 00:00:00 +0000
+++ b/mysql-test/r/test_wl4435.result 2008-08-07 17:52:43 +0000
@@ -0,0 +1,114 @@
+
+exec_counter: 0
+num_fields: 4
+ - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 1; str: '11'; dbl: 12.340000; dec: '56.7';
+ int: 2; str: '12'; dbl: 56.780000; dec: '90.1';
+ int: 3; str: '13'; dbl: 23.450000; dec: '67.8';
+EOF
+mysql_stmt_next_result(): 0; field_count: 5
+num_fields: 5
+ - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1';
+ int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2';
+ int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3';
+EOF
+mysql_stmt_next_result(): 0; field_count: 8
+num_fields: 8
+ - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 32; max_length: 0; type: 254; decimals: 0
+ - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 4; max_length: 0; type: 5; decimals: 2
+ - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 8; max_length: 0; type: 246; decimals: 3
+ - 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
+ - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 64; max_length: 0; type: 254; decimals: 0
+ - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 5; max_length: 0; type: 5; decimals: 3
+ - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 9; max_length: 0; type: 246; decimals: 4
+ - 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
+Data:
+ str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl:
67.891000; dec: '234.6789'; int: 6789;
+EOF
+mysql_stmt_next_result(): 0; field_count: 0
+
+exec_counter: 1
+num_fields: 4
+ - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 1; str: '11'; dbl: 12.340000; dec: '56.7';
+ int: 2; str: '12'; dbl: 56.780000; dec: '90.1';
+ int: 3; str: '13'; dbl: 23.450000; dec: '67.8';
+EOF
+mysql_stmt_next_result(): 0; field_count: 5
+num_fields: 5
+ - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1';
+ int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2';
+ int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3';
+EOF
+mysql_stmt_next_result(): 0; field_count: 8
+num_fields: 8
+ - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 32; max_length: 0; type: 254; decimals: 0
+ - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 4; max_length: 0; type: 5; decimals: 2
+ - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 8; max_length: 0; type: 246; decimals: 3
+ - 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
+ - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 64; max_length: 0; type: 254; decimals: 0
+ - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 5; max_length: 0; type: 5; decimals: 3
+ - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 9; max_length: 0; type: 246; decimals: 4
+ - 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
+Data:
+ str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl:
67.891000; dec: '234.6789'; int: 6789;
+EOF
+mysql_stmt_next_result(): 0; field_count: 0
+
+exec_counter: 2
+num_fields: 4
+ - 0: name: 'a1'/'a1'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'a2'/'a2'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 2: name: 'a3'/'a3'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 3: name: 'a4'/'a4'; table: 't1'/'t1'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 1; str: '11'; dbl: 12.340000; dec: '56.7';
+ int: 2; str: '12'; dbl: 56.780000; dec: '90.1';
+ int: 3; str: '13'; dbl: 23.450000; dec: '67.8';
+EOF
+mysql_stmt_next_result(): 0; field_count: 5
+num_fields: 5
+ - 0: name: 'b0'/'b0'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 1: name: 'b1'/'b1'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
11; max_length: 0; type: 3; decimals: 0
+ - 2: name: 'b2'/'b2'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
32; max_length: 0; type: 254; decimals: 0
+ - 3: name: 'b3'/'b3'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
4; max_length: 0; type: 5; decimals: 2
+ - 4: name: 'b4'/'b4'; table: 't2'/'t2'; db: 'client_test_db'; catalog: 'def'; length:
5; max_length: 0; type: 246; decimals: 1
+Data:
+ int: 100; int: 10; str: '110'; dbl: 70.700000; dec: '10.1';
+ int: 200; int: 20; str: '120'; dbl: 80.800000; dec: '20.2';
+ int: 300; int: 30; str: '130'; dbl: 90.900000; dec: '30.3';
+EOF
+mysql_stmt_next_result(): 0; field_count: 8
+num_fields: 8
+ - 0: name: 'v_str_1'/'v_str_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 32; max_length: 0; type: 254; decimals: 0
+ - 1: name: 'v_dbl_1'/'v_dbl_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 4; max_length: 0; type: 5; decimals: 2
+ - 2: name: 'v_dec_1'/'v_dec_1'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 8; max_length: 0; type: 246; decimals: 3
+ - 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
+ - 4: name: 'v_str_2'/'v_str_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 64; max_length: 0; type: 254; decimals: 0
+ - 5: name: 'v_dbl_2'/'v_dbl_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 5; max_length: 0; type: 5; decimals: 3
+ - 6: name: 'v_dec_2'/'v_dec_2'; table: 'p1'/'p1'; db: 'client_test_db'; catalog: 'def';
length: 9; max_length: 0; type: 246; decimals: 4
+ - 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
+Data:
+ str: 'test_1'; dbl: 12.340000; dec: '567.891'; int: 2345; str: 'test_2'; dbl:
67.891000; dec: '234.6789'; int: 6789;
+EOF
+mysql_stmt_next_result(): 0; field_count: 0
=== modified file 'mysql-test/t/ps.test'
--- a/mysql-test/t/ps.test 2008-05-21 10:17:29 +0000
+++ b/mysql-test/t/ps.test 2008-08-07 17:52:43 +0000
@@ -2998,5 +2998,209 @@ execute stmt;
drop table t1;
deallocate prepare stmt;
+###########################################################################
+--echo
--echo End of 5.1 tests.
+
+###########################################################################
+
+--echo
+--echo #
+--echo # WL#4435: Support OUT-parameters in prepared statements.
+--echo #
+--echo
+
+# The idea of this test case is to check that
+# - OUT-parameters of four allowed types (string, double, int, decimal) work
+# properly;
+# - INOUT and OUT parameters work properly;
+# - A mix of IN and OUT parameters work properly;
+
+--disable_warnings
+DROP PROCEDURE IF EXISTS p_string;
+DROP PROCEDURE IF EXISTS p_double;
+DROP PROCEDURE IF EXISTS p_int;
+DROP PROCEDURE IF EXISTS p_decimal;
+--enable_warnings
+
+delimiter |;
+
+--echo
+CREATE PROCEDURE p_string(
+ IN v0 INT,
+ OUT v1 CHAR(32),
+ IN v2 CHAR(32),
+ INOUT v3 CHAR(32))
+BEGIN
+ SET v0 = -1;
+ SET v1 = 'test_v1';
+ SET v2 = 'n/a';
+ SET v3 = 'test_v3';
+END|
+
+--echo
+CREATE PROCEDURE p_double(
+ IN v0 INT,
+ OUT v1 DOUBLE(4, 2),
+ IN v2 DOUBLE(4, 2),
+ INOUT v3 DOUBLE(4, 2))
+BEGIN
+ SET v0 = -1;
+ SET v1 = 12.34;
+ SET v2 = 98.67;
+ SET v3 = 56.78;
+END|
+
+--echo
+CREATE PROCEDURE p_int(
+ IN v0 CHAR(10),
+ OUT v1 INT,
+ IN v2 INT,
+ INOUT v3 INT)
+BEGIN
+ SET v0 = 'n/a';
+ SET v1 = 1234;
+ SET v2 = 9876;
+ SET v3 = 5678;
+END|
+
+--echo
+CREATE PROCEDURE p_decimal(
+ IN v0 INT,
+ OUT v1 DECIMAL(4, 2),
+ IN v2 DECIMAL(4, 2),
+ INOUT v3 DECIMAL(4, 2))
+BEGIN
+ SET v0 = -1;
+ SET v1 = 12.34;
+ SET v2 = 98.67;
+ SET v3 = 56.78;
+END|
+
+delimiter ;|
+
+--echo
+PREPARE stmt_str FROM 'CALL p_string(?, ?, ?, ?)';
+PREPARE stmt_dbl FROM 'CALL p_double(?, ?, ?, ?)';
+PREPARE stmt_int FROM 'CALL p_int(?, ?, ?, ?)';
+PREPARE stmt_dec FROM 'CALL p_decimal(?, ?, ?, ?)';
+
+--echo
+SET @x_str_1 = NULL;
+SET @x_str_2 = NULL;
+SET @x_str_3 = NULL;
+SET @x_dbl_1 = NULL;
+SET @x_dbl_2 = NULL;
+SET @x_dbl_3 = NULL;
+SET @x_int_1 = NULL;
+SET @x_int_2 = NULL;
+SET @x_int_3 = NULL;
+SET @x_dec_1 = NULL;
+SET @x_dec_2 = NULL;
+SET @x_dec_3 = NULL;
+
+--echo
+--echo -- Testing strings...
+
+--echo
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+
+--echo
+EXECUTE stmt_str USING @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+SELECT @x_int_1, @x_str_1, @x_str_2, @x_str_3;
+
+--echo
+--echo -- Testing doubles...
+
+--echo
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+
+--echo
+EXECUTE stmt_dbl USING @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+SELECT @x_int_1, @x_dbl_1, @x_dbl_2, @x_dbl_3;
+
+--echo
+--echo -- Testing ints...
+
+--echo
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+
+--echo
+EXECUTE stmt_int USING @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+SELECT @x_str_1, @x_int_1, @x_int_2, @x_int_3;
+
+--echo
+--echo -- Testing decs...
+
+--echo
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+
+--echo
+EXECUTE stmt_dec USING @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+SELECT @x_int_1, @x_dec_1, @x_dec_2, @x_dec_3;
+
+--echo
+DEALLOCATE PREPARE stmt_str;
+DEALLOCATE PREPARE stmt_dbl;
+DEALLOCATE PREPARE stmt_int;
+DEALLOCATE PREPARE stmt_dec;
+
+--echo
+DROP PROCEDURE p_string;
+DROP PROCEDURE p_double;
+DROP PROCEDURE p_int;
+DROP PROCEDURE p_decimal;
+
+#
+# Another test case for WL#4435: check out parameters in Dynamic SQL.
+#
+
+--echo
+--disable_warnings
+DROP PROCEDURE IF EXISTS p1;
+DROP PROCEDURE IF EXISTS p2;
+--enable_warnings
+
+--echo
+
+CREATE PROCEDURE p1(OUT v1 CHAR(10))
+ SET v1 = 'test1';
+
+--echo
+
+delimiter |;
+CREATE PROCEDURE p2(OUT v2 CHAR(10))
+BEGIN
+ SET @query = 'CALL p1(?)';
+ PREPARE stmt1 FROM @query;
+ EXECUTE stmt1 USING @u1;
+ DEALLOCATE PREPARE stmt1;
+
+ SET v2 = @u1;
+END|
+delimiter ;|
+
+--echo
+
+CALL p2(@a);
+SELECT @a;
+
+--echo
+
+DROP PROCEDURE p1;
+DROP PROCEDURE p2;
+
+--echo
+--echo # End of WL#4435.
+
+###########################################################################
+
+--echo
+--echo End of 6.0 tests.
+
+###########################################################################
=== modified file 'sql/backup/backup_test.cc'
--- a/sql/backup/backup_test.cc 2008-07-19 03:03:39 +0000
+++ b/sql/backup/backup_test.cc 2008-08-07 17:52:43 +0000
@@ -42,7 +42,7 @@ int execute_backup_test_command(THD *thd
field_list.push_back(new Item_empty_string("name", 5));
field_list.push_back(new Item_empty_string("type", 4));
field_list.push_back(new Item_empty_string("serialization", 13));
- protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF);
+ protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF);
obs::ObjIterator *it= obs::get_databases(thd);
=== modified file 'sql/backup/kernel.cc'
--- a/sql/backup/kernel.cc 2008-07-19 03:03:39 +0000
+++ b/sql/backup/kernel.cc 2008-08-07 17:52:43 +0000
@@ -272,7 +272,7 @@ int send_reply(Backup_restore_ctx &conte
Send field list.
*/
field_list.push_back(new Item_empty_string(STRING_WITH_LEN("backup_id")));
- protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF);
+ protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF);
/*
Send field data.
=== modified file 'sql/events.cc'
--- a/sql/events.cc 2008-07-26 16:38:20 +0000
+++ b/sql/events.cc 2008-08-07 17:52:43 +0000
@@ -682,7 +682,7 @@ send_show_create_event(THD *thd, Event_t
field_list.push_back(
new Item_empty_string("Database Collation", MY_CS_NAME_SIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/handler.cc'
--- a/sql/handler.cc 2008-07-25 17:21:55 +0000
+++ b/sql/handler.cc 2008-08-07 17:52:43 +0000
@@ -1584,7 +1584,7 @@ bool mysql_xa_recover(THD *thd)
field_list.push_back(new Item_int("bqual_length", 0, MY_INT32_NUM_DECIMAL_DIGITS));
field_list.push_back(new Item_empty_string("data",XIDDATASIZE));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -5107,7 +5107,7 @@ bool ha_show_status(THD *thd, handlerton
field_list.push_back(new Item_empty_string("Name",FN_REFLEN));
field_list.push_back(new Item_empty_string("Status",10));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
return TRUE;
=== modified file 'sql/item.cc'
--- a/sql/item.cc 2008-07-24 10:00:56 +0000
+++ b/sql/item.cc 2008-08-07 17:52:43 +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.
@@ -3214,6 +3226,158 @@ Item_param::set_param_type_and_swap_valu
str_value_ptr.swap(src->str_value_ptr);
}
+
+/**
+ This operation is intended to store some item value in Item_param to be
+ used later.
+
+ @param thd thread context
+ @param ctx stored procedure runtime context
+ @param it a pointer to an item in the tree
+
+ @return Error status
+ @retval TRUE on error
+ @retval FALSE on success
+*/
+
+bool
+Item_param::set_value(THD *thd, sp_rcontext *ctx, Item **it)
+{
+ Item *value= *it;
+
+ if (value->is_null())
+ {
+ set_null();
+ return FALSE;
+ }
+
+ null_value= FALSE;
+
+ switch (value->result_type()) {
+ case STRING_RESULT:
+ {
+ char str_buffer[STRING_BUFFER_USUAL_SIZE];
+ String sv_buffer(str_buffer, sizeof(str_buffer), &my_charset_bin);
+ String *sv= value->val_str(&sv_buffer);
+
+ if (!sv)
+ return TRUE;
+
+ set_str(sv->c_ptr_safe(), sv->length());
+ str_value_ptr.set(str_value.ptr(),
+ str_value.length(),
+ str_value.charset());
+ collation.set(str_value.charset(), DERIVATION_COERCIBLE);
+ decimals= 0;
+ param_type= MYSQL_TYPE_STRING;
+
+ break;
+ }
+
+ case REAL_RESULT:
+ set_double(value->val_real());
+ param_type= MYSQL_TYPE_DOUBLE;
+ break;
+
+ case INT_RESULT:
+ set_int(value->val_int(), value->max_length);
+ param_type= MYSQL_TYPE_LONG;
+ break;
+
+ case DECIMAL_RESULT:
+ {
+ my_decimal dv_buf;
+ my_decimal *dv= value->val_decimal(&dv_buf);
+
+ if (!dv)
+ return TRUE;
+
+ set_decimal(dv);
+ param_type= MYSQL_TYPE_NEWDECIMAL;
+
+ break;
+ }
+
+ default:
+ /* That can not happen. */
+
+ DBUG_ASSERT(TRUE); // Abort in debug mode.
+
+ set_null(); // Set to NULL in release mode.
+ return FALSE;
+ }
+
+ item_result_type= value->result_type();
+ item_type= value->type();
+ return FALSE;
+}
+
+
+/**
+ Setter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be set in sp_head::execute() after
+ Item_param::set_value() is called.
+*/
+
+void
+Item_param::set_out_param_info(Send_field *info)
+{
+ m_out_param_info= info;
+}
+
+
+/**
+ Getter of Item_param::m_out_param_info.
+
+ m_out_param_info is used to store information about store routine
+ OUT-parameters, such as stored routine name, database, stored routine
+ variable name. It is supposed to be retrieved in
+ Protocol_binary::send_out_parameters() during creation of OUT-parameter
+ result set.
+*/
+
+const Send_field *
+Item_param::get_out_param_info() const
+{
+ return m_out_param_info;
+}
+
+
+/**
+ Fill meta-data information for the corresponding column in a result set.
+ If this is an OUT-parameter of a stored procedure, preserve meta-data of
+ stored-routine variable.
+
+ @param field container for meta-data to be filled
+*/
+
+void Item_param::make_field(Send_field *field)
+{
+ Item::make_field(field);
+
+ if (!m_out_param_info)
+ return;
+
+ /*
+ This is an OUT-parameter of stored procedure. We should use
+ OUT-parameter info to fill out the names.
+ */
+
+ field->db_name= m_out_param_info->db_name;
+ field->table_name= m_out_param_info->table_name;
+ field->org_table_name= m_out_param_info->org_table_name;
+ field->col_name= m_out_param_info->col_name;
+ field->org_col_name= m_out_param_info->org_col_name;
+ field->length= m_out_param_info->length;
+ field->charsetnr= m_out_param_info->charsetnr;
+ field->flags= m_out_param_info->flags;
+ field->decimals= m_out_param_info->decimals;
+ field->type= m_out_param_info->type;
+}
+
/****************************************************************************
Item_copy_string
****************************************************************************/
@@ -3247,7 +3411,7 @@ my_decimal *Item_copy_string::val_decima
/*
- Functions to convert item to field (for send_fields)
+ Functions to convert item to field (for send_result_set_metadata)
*/
/* ARGSUSED */
=== modified file 'sql/item.h'
--- a/sql/item.h 2008-06-17 20:04:19 +0000
+++ b/sql/item.h 2008-08-07 17:52:43 +0000
@@ -443,6 +443,11 @@ public:
TRUE if error has occured.
*/
virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it)= 0;
+
+ virtual void set_out_param_info(Send_field *info) {}
+
+ virtual const Send_field *get_out_param_info() const
+ { return NULL; }
};
@@ -1609,7 +1614,8 @@ public:
/* Item represents one placeholder ('?') of prepared statement */
-class Item_param :public Item
+class Item_param :public Item,
+ private Settable_routine_parameter
{
char cnvbuf[MAX_FIELD_WIDTH];
String cnvstr;
@@ -1697,6 +1703,7 @@ public:
void set_int(longlong i, uint32 max_length_arg);
void set_double(double i);
void set_decimal(const char *str, ulong length);
+ void set_decimal(const my_decimal *dv);
bool set_str(const char *str, ulong length);
bool set_longdata(const char *str, ulong length);
void set_time(MYSQL_TIME *tm, timestamp_type type, uint32 max_length_arg);
@@ -1746,6 +1753,25 @@ public:
/** Item is a argument to a limit clause. */
bool limit_clause_param;
void set_param_type_and_swap_value(Item_param *from);
+
+private:
+ virtual inline Settable_routine_parameter *
+ get_settable_routine_parameter()
+ {
+ return this;
+ }
+
+ virtual bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
+
+ virtual void set_out_param_info(Send_field *info);
+
+public:
+ virtual const Send_field *get_out_param_info() const;
+
+ virtual void make_field(Send_field *field);
+
+private:
+ Send_field *m_out_param_info;
};
@@ -2098,7 +2124,7 @@ public:
/**
Item_empty_string -- is a utility class to put an item into List<Item>
- which is then used in protocol.send_fields() when sending SHOW output to
+ which is then used in protocol.send_result_set_metadata() when sending SHOW output to
the client.
*/
=== modified file 'sql/procedure.h'
--- a/sql/procedure.h 2008-03-21 15:48:28 +0000
+++ b/sql/procedure.h 2008-08-07 17:52:43 +0000
@@ -23,7 +23,7 @@
#define PROC_NO_SORT 1 /**< Bits in flags */
#define PROC_GROUP 2 /**< proc must have group */
-/* Procedure items used by procedures to store values for send_fields */
+/* Procedure items used by procedures to store values for send_result_set_metadata */
class Item_proc :public Item
{
=== modified file 'sql/protocol.cc'
--- a/sql/protocol.cc 2008-06-05 16:11:22 +0000
+++ b/sql/protocol.cc 2008-08-07 17:52:43 +0000
@@ -68,7 +68,7 @@ bool Protocol_binary::net_store_data(con
exactly one byte to store length. It allows not to use
the "convert" member as a temporary buffer, conversion
is done directly to the "packet" member.
- The limit 251 is good enough to optimize send_fields()
+ The limit 251 is good enough to optimize send_result_set_metadata()
because column, table, database names fit into this limit.
*/
@@ -246,7 +246,7 @@ static uchar eof_buff[1]= { (uchar) 254
@param thd Thread handler
@param no_flush Set to 1 if there will be more data to the client,
- like in send_fields().
+ like in send_result_set_metadata().
*/
void
@@ -562,16 +562,16 @@ bool Protocol::flush()
1 Error (Note that in this case the error is not sent to the
client)
*/
-bool Protocol::send_fields(List<Item> *list, uint flags)
+bool Protocol::send_result_set_metadata(List<Item> *list, uint flags)
{
List_iterator_fast<Item> it(*list);
Item *item;
- uchar buff[80];
+ uchar buff[MAX_FIELD_WIDTH];
String tmp((char*) buff,sizeof(buff),&my_charset_bin);
Protocol_text prot(thd);
String *local_packet= prot.storage_packet();
CHARSET_INFO *thd_charset= thd->variables.character_set_results;
- DBUG_ENTER("send_fields");
+ DBUG_ENTER("send_result_set_metadata");
if (flags & SEND_NUM_ROWS)
{ // Packet with number of elements
@@ -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),
@@ -723,6 +723,38 @@ bool Protocol::write()
/**
+ Send one result set row.
+
+ @param row_items a collection of column values for that row
+
+ @return Error status.
+ @retval TRUE Error.
+ @retval FALSE Success.
+*/
+
+bool Protocol::send_result_set_row(List<Item> *row_items)
+{
+ char buffer[MAX_FIELD_WIDTH];
+ String str_buffer(buffer, sizeof (buffer), &my_charset_bin);
+ List_iterator_fast<Item> it(*row_items);
+
+ DBUG_ENTER("Protocol::send_result_set_row");
+
+ for (Item *item= it++; item; item= it++)
+ {
+ if (item->send(this, &str_buffer) || thd->is_error())
+ {
+ this->free(); // Free used buffer
+ my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
+ DBUG_RETURN(TRUE);
+ }
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
Send \\0 end terminated string.
@param from NullS or \\0 terminated string
@@ -1044,6 +1076,53 @@ bool Protocol_text::store_time(MYSQL_TIM
return net_store_data((uchar*) buff, length);
}
+/**
+ Assign OUT-parameters to user variables.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_text::send_out_parameters(List<Item_param> *sp_params)
+{
+ DBUG_ASSERT(sp_params->elements ==
+ thd->lex->prepared_stmt_params.elements);
+
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+ List_iterator_fast<LEX_STRING>
user_var_name_it(thd->lex->prepared_stmt_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+ LEX_STRING *user_var_name= user_var_name_it++;
+
+ if (!item_param || !user_var_name)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ Item_func_set_user_var *suv=
+ new Item_func_set_user_var(*user_var_name, item_param);
+ /*
+ Item_func_set_user_var is not fixed after construction, call
+ fix_fields().
+ */
+ if (suv->fix_fields(thd, NULL))
+ return TRUE;
+
+ if (suv->check(FALSE))
+ return TRUE;
+
+ if (suv->update())
+ return TRUE;
+ }
+
+ return FALSE;
+}
/****************************************************************************
Functions to handle the binary protocol used with prepared statements
@@ -1064,14 +1143,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 +1336,81 @@ bool Protocol_binary::store_time(MYSQL_T
length=0;
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFER_EXTRA_ALLOC);
+}
+
+/**
+ Send a result set with OUT-parameter values by means of PS-protocol.
+
+ @param sp_params List of PS/SP parameters (both input and output).
+
+ @return Error status.
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool Protocol_binary::send_out_parameters(List<Item_param> *sp_params)
+{
+ if (!(thd->client_capabilities & CLIENT_PS_MULTI_RESULTS))
+ {
+ /* The client does not support OUT-parameters. */
+ return FALSE;
+ }
+
+ List<Item> out_param_lst;
+
+ {
+ List_iterator_fast<Item_param> item_param_it(*sp_params);
+
+ while (true)
+ {
+ Item_param *item_param= item_param_it++;
+
+ if (!item_param)
+ break;
+
+ if (!item_param->get_out_param_info())
+ continue; // It's an IN-parameter.
+
+ if (out_param_lst.push_back(item_param))
+ return TRUE;
+ }
+ }
+
+ if (!out_param_lst.elements)
+ return FALSE;
+
+ /*
+ We have to set SERVER_PS_OUT_PARAMS in THD::server_status, because it
+ is used in send_result_set_metadata().
+ */
+
+ thd->server_status|= SERVER_PS_OUT_PARAMS | SERVER_MORE_RESULTS_EXISTS;
+
+ /* Send meta-data. */
+ if (send_result_set_metadata(&out_param_lst, SEND_NUM_ROWS | SEND_EOF))
+ return TRUE;
+
+ /* Send data. */
+
+ prepare_for_resend();
+
+ if (send_result_set_row(&out_param_lst))
+ return TRUE;
+
+ if (write())
+ return TRUE;
+
+ /* Restore THD::server_status. */
+ thd->server_status&= ~SERVER_PS_OUT_PARAMS;
+
+ /*
+ Reset SERVER_MORE_RESULTS_EXISTS bit, because this is the last packet
+ for sure.
+ */
+ thd->server_status&= ~SERVER_MORE_RESULTS_EXISTS;
+
+ /* Send EOF-packet. */
+ net_send_eof(thd, thd->server_status, 0);
+
+ return FALSE;
}
=== modified file 'sql/protocol.h'
--- a/sql/protocol.h 2008-02-11 16:11:22 +0000
+++ b/sql/protocol.h 2008-08-07 17:52:43 +0000
@@ -20,6 +20,7 @@
class i_string;
class THD;
+class Item_param;
typedef struct st_mysql_field MYSQL_FIELD;
typedef struct st_mysql_rows MYSQL_ROWS;
@@ -53,7 +54,8 @@ public:
void init(THD* thd_arg);
enum { SEND_NUM_ROWS= 1, SEND_DEFAULTS= 2, SEND_EOF= 4 };
- virtual bool send_fields(List<Item> *list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> *list, uint flags);
+ bool send_result_set_row(List<Item> *row_items);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
@@ -71,9 +73,9 @@ public:
inline bool store(String *str)
{ return store((char*) str->ptr(), str->length(), str->charset()); }
- virtual bool prepare_for_send(List<Item> *item_list)
+ virtual bool prepare_for_send(uint num_columns)
{
- field_count=item_list->elements;
+ field_count= num_columns;
return 0;
}
virtual bool flush();
@@ -95,6 +97,8 @@ public:
virtual bool store_date(MYSQL_TIME *time)=0;
virtual bool store_time(MYSQL_TIME *time)=0;
virtual bool store(Field *field)=0;
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params)=0;
#ifdef EMBEDDED_LIBRARY
int begin_dataset();
virtual void remove_last_row() {}
@@ -136,6 +140,8 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
@@ -150,7 +156,7 @@ private:
public:
Protocol_binary() {}
Protocol_binary(THD *thd_arg) :Protocol(thd_arg) {}
- virtual bool prepare_for_send(List<Item> *item_list);
+ virtual bool prepare_for_send(uint num_columns);
virtual void prepare_for_resend();
#ifdef EMBEDDED_LIBRARY
virtual bool write();
@@ -171,6 +177,9 @@ public:
virtual bool store(float nr, uint32 decimals, String *buffer);
virtual bool store(double from, uint32 decimals, String *buffer);
virtual bool store(Field *field);
+
+ virtual bool send_out_parameters(List<Item_param> *sp_params);
+
virtual enum enum_protocol_type type() { return PROTOCOL_BINARY; };
};
=== modified file 'sql/repl_failsafe.cc'
--- a/sql/repl_failsafe.cc 2008-03-27 19:02:15 +0000
+++ b/sql/repl_failsafe.cc 2008-08-07 17:52:43 +0000
@@ -470,7 +470,7 @@ bool show_new_master(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 20));
field_list.push_back(new Item_return_int("Log_pos", 10,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -664,7 +664,7 @@ bool show_slave_hosts(THD* thd)
field_list.push_back(new Item_return_int("Master_id", 10,
MYSQL_TYPE_LONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/slave.cc'
--- a/sql/slave.cc 2008-07-21 03:55:09 +0000
+++ b/sql/slave.cc 2008-08-07 17:52:43 +0000
@@ -1240,7 +1240,7 @@ bool show_master_info(THD* thd, Master_i
field_list.push_back(new Item_return_int("Last_SQL_Errno", 4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Last_SQL_Error", 20));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc 2008-07-25 17:21:55 +0000
+++ b/sql/sp_head.cc 2008-08-07 17:52:43 +0000
@@ -2012,6 +2012,16 @@ sp_head::execute_procedure(THD *thd, Lis
err_status= TRUE;
break;
}
+
+ Send_field *out_param_info= new Send_field();
+ nctx->get_item(i)->make_field(out_param_info);
+ out_param_info->db_name= m_db.str;
+ out_param_info->table_name= m_name.str;
+ out_param_info->org_table_name= m_name.str;
+ out_param_info->col_name= spvar->name.str;
+ out_param_info->org_col_name= spvar->name.str;
+
+ srp->set_out_param_info(out_param_info);
}
}
@@ -2420,7 +2430,7 @@ sp_head::show_create_routine(THD *thd, i
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (protocol->send_fields(&fields,
+ if (protocol->send_result_set_metadata(&fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_RETURN(TRUE);
@@ -2605,7 +2615,7 @@ sp_head::show_routine_code(THD *thd)
// 1024 is for not to confuse old clients
field_list.push_back(new Item_empty_string("Instruction",
max(buffer.length(), 1024)));
- if (protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc 2008-07-25 17:21:55 +0000
+++ b/sql/sql_acl.cc 2008-08-07 17:52:43 +0000
@@ -4626,7 +4626,7 @@ bool mysql_show_grants(THD *thd,LEX_USER
strxmov(buff,"Grants for ",lex_user->user.str,"@",
lex_user->host.str,NullS);
field_list.push_back(field);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
pthread_mutex_unlock(&acl_cache->lock);
=== modified file 'sql/sql_class.cc'
--- a/sql/sql_class.cc 2008-07-25 17:21:55 +0000
+++ b/sql/sql_class.cc 2008-08-07 17:52:43 +0000
@@ -1405,7 +1405,7 @@ int THD::send_explain_fields(select_resu
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
- return (result->send_fields(field_list,
+ return (result->send_result_set_metadata(field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
@@ -1551,10 +1551,10 @@ sql_exchange::sql_exchange(char *name, b
cs= NULL;
}
-bool select_send::send_fields(List<Item> &list, uint flags)
+bool select_send::send_result_set_metadata(List<Item> &list, uint flags)
{
bool res;
- if (!(res= thd->protocol->send_fields(&list, flags)))
+ if (!(res= thd->protocol->send_result_set_metadata(&list, flags)))
is_result_set_started= 1;
return res;
}
@@ -1597,10 +1597,13 @@ void select_send::cleanup()
bool select_send::send_data(List<Item> &items)
{
+ Protocol *protocol= thd->protocol;
+ DBUG_ENTER("select_send::send_data");
+
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
- return 0;
+ DBUG_RETURN(FALSE);
}
/*
@@ -1610,31 +1613,18 @@ bool select_send::send_data(List<Item> &
*/
ha_release_temporary_latches(thd);
- List_iterator_fast<Item> li(items);
- Protocol *protocol= thd->protocol;
- char buff[MAX_FIELD_WIDTH];
- String buffer(buff, sizeof(buff), &my_charset_bin);
- DBUG_ENTER("select_send::send_data");
-
protocol->prepare_for_resend();
- Item *item;
- while ((item=li++))
- {
- if (item->send(protocol, &buffer) || thd->is_error())
- {
- protocol->free(); // Free used buffer
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- break;
- }
- }
- if (thd->is_error())
+ if (protocol->send_result_set_row(&items))
{
protocol->remove_last_row();
- DBUG_RETURN(1);
+ DBUG_RETURN(TRUE);
}
+
thd->sent_row_count++;
+
if (thd->vio_ok())
DBUG_RETURN(protocol->write());
+
DBUG_RETURN(0);
}
=== modified file 'sql/sql_class.h'
--- a/sql/sql_class.h 2008-07-26 16:38:20 +0000
+++ b/sql/sql_class.h 2008-08-07 17:52:43 +0000
@@ -2451,7 +2451,7 @@ public:
*/
virtual uint field_count(List<Item> &fields) const
{ return fields.elements; }
- virtual bool send_fields(List<Item> &list, uint flags)=0;
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags)=0;
virtual bool send_data(List<Item> &items)=0;
virtual bool initialize_tables (JOIN *join=0) { return 0; }
virtual void send_error(uint errcode,const char *err);
@@ -2490,7 +2490,7 @@ class select_result_interceptor: public
public:
select_result_interceptor() {} /* Remove gcc warning */
uint field_count(List<Item> &fields) const { return 0; }
- bool send_fields(List<Item> &fields, uint flag) { return FALSE; }
+ bool send_result_set_metadata(List<Item> &fields, uint flag) { return FALSE;
}
};
@@ -2503,7 +2503,7 @@ class select_send :public select_result
bool is_result_set_started;
public:
select_send() :is_result_set_started(FALSE) {}
- bool send_fields(List<Item> &list, uint flags);
+ bool send_result_set_metadata(List<Item> &list, uint flags);
bool send_data(List<Item> &items);
bool send_eof();
virtual bool check_simple_select() const { return FALSE; }
=== modified file 'sql/sql_cursor.cc'
--- a/sql/sql_cursor.cc 2008-06-06 19:19:04 +0000
+++ b/sql/sql_cursor.cc 2008-08-07 17:52:43 +0000
@@ -88,7 +88,7 @@ class Materialized_cursor: public Server
public:
Materialized_cursor(select_result *result, TABLE *table);
- int fill_item_list(THD *thd, List<Item> &send_fields);
+ int fill_item_list(THD *thd, List<Item> &send_result_set_metadata);
virtual bool is_open() const { return table != 0; }
virtual int open(JOIN *join __attribute__((unused)));
virtual void fetch(ulong num_rows);
@@ -113,7 +113,7 @@ public:
Materialized_cursor *materialized_cursor;
Select_materialize(select_result *result_arg)
:result(result_arg), materialized_cursor(0) {}
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
};
@@ -360,12 +360,12 @@ Sensitive_cursor::open(JOIN *join_arg)
join->change_result(result);
/*
Send fields description to the client; server_status is sent
- in 'EOF' packet, which follows send_fields().
- We don't simply use SEND_EOF flag of send_fields because we also
+ in 'EOF' packet, which follows send_result_set_metadata().
+ We don't simply use SEND_EOF flag of send_result_set_metadata because we also
want to flush the network buffer, which is done only in a standalone
send_eof().
*/
- result->send_fields(*join->fields, Protocol::SEND_NUM_ROWS);
+ result->send_result_set_metadata(*join->fields, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -549,14 +549,14 @@ Materialized_cursor::Materialized_cursor
Preserve the original metadata that would be sent to the client.
@param thd Thread identifier.
- @param send_fields List of fields that would be sent.
+ @param send_result_set_metadata List of fields that would be sent.
*/
-int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
+int Materialized_cursor::fill_item_list(THD *thd, List<Item>
&send_result_set_metadata)
{
Query_arena backup_arena;
int rc;
- List_iterator_fast<Item> it_org(send_fields);
+ List_iterator_fast<Item> it_org(send_result_set_metadata);
List_iterator_fast<Item> it_dst(item_list);
Item *item_org;
Item *item_dst;
@@ -566,7 +566,7 @@ int Materialized_cursor::fill_item_list(
if ((rc= table->fill_item_list(&item_list)))
goto end;
- DBUG_ASSERT(send_fields.elements == item_list.elements);
+ DBUG_ASSERT(send_result_set_metadata.elements == item_list.elements);
/*
Unless we preserve the original metadata, it will be lost,
@@ -605,17 +605,17 @@ int Materialized_cursor::open(JOIN *join
{
/*
Now send the result set metadata to the client. We need to
- do it here, as in Select_materialize::send_fields the items
- for column types are not yet created (send_fields requires
+ do it here, as in Select_materialize::send_result_set_metadata the items
+ for column types are not yet created (send_result_set_metadata requires
a list of items). The new types may differ from the original
ones sent at prepare if some of them were altered by MySQL
HEAP tables mechanism -- used when create_tmp_field_from_item
may alter the original column type.
- We can't simply supply SEND_EOF flag to send_fields, because
- send_fields doesn't flush the network buffer.
+ We can't simply supply SEND_EOF flag to send_result_set_metadata, because
+ send_result_set_metadata doesn't flush the network buffer.
*/
- rc= result->send_fields(item_list, Protocol::SEND_NUM_ROWS);
+ rc= result->send_result_set_metadata(item_list, Protocol::SEND_NUM_ROWS);
thd->server_status|= SERVER_STATUS_CURSOR_EXISTS;
result->send_eof();
thd->server_status&= ~SERVER_STATUS_CURSOR_EXISTS;
@@ -698,7 +698,7 @@ Materialized_cursor::~Materialized_curso
Select_materialize
****************************************************************************/
-bool Select_materialize::send_fields(List<Item> &list, uint flags)
+bool Select_materialize::send_result_set_metadata(List<Item> &list, uint flags)
{
DBUG_ASSERT(table == 0);
if (create_result_table(unit->thd, unit->get_unit_column_types(),
=== modified file 'sql/sql_error.cc'
--- a/sql/sql_error.cc 2008-02-19 12:59:19 +0000
+++ b/sql/sql_error.cc 2008-08-07 17:52:43 +0000
@@ -219,7 +219,7 @@ bool mysqld_show_warnings(THD *thd, ulon
field_list.push_back(new Item_return_int("Code",4, MYSQL_TYPE_LONG));
field_list.push_back(new Item_empty_string("Message",MYSQL_ERRMSG_SIZE));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/sql_handler.cc'
--- a/sql/sql_handler.cc 2008-06-20 13:11:20 +0000
+++ b/sql/sql_handler.cc 2008-08-07 17:52:43 +0000
@@ -539,7 +539,7 @@ retry:
tables->db, tables->alias, &it, 0))
goto err;
- protocol->send_fields(&list, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
+ protocol->send_result_set_metadata(&list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF);
/*
In ::external_lock InnoDB resets the fields which tell it that
@@ -661,18 +661,11 @@ retry:
continue;
if (num_rows >= offset_limit_cnt)
{
- Item *item;
protocol->prepare_for_resend();
- it.rewind();
- while ((item=it++))
- {
- if (item->send(thd->protocol, &buffer))
- {
- protocol->free(); // Free used
- my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
- goto err;
- }
- }
+
+ if (protocol->send_result_set_row(&list))
+ goto err;
+
protocol->write();
}
num_rows++;
=== modified file 'sql/sql_help.cc'
--- a/sql/sql_help.cc 2008-07-17 18:26:55 +0000
+++ b/sql/sql_help.cc 2008-08-07 17:52:43 +0000
@@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, St
field_list.push_back(new Item_empty_string("description",1000));
field_list.push_back(new Item_empty_string("example",1000));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(1);
@@ -463,7 +463,7 @@ int send_answer_1(Protocol *protocol, St
+- -+
RETURN VALUES
- result of protocol->send_fields
+ result of protocol->send_result_set_metadata
*/
int send_header_2(Protocol *protocol, bool for_category)
@@ -474,7 +474,7 @@ int send_header_2(Protocol *protocol, bo
field_list.push_back(new Item_empty_string("source_category_name",64));
field_list.push_back(new Item_empty_string("name",64));
field_list.push_back(new Item_empty_string("is_it_category",1));
- DBUG_RETURN(protocol->send_fields(&field_list, Protocol::SEND_NUM_ROWS |
+ DBUG_RETURN(protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF));
}
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc 2008-07-15 16:29:51 +0000
+++ b/sql/sql_prepare.cc 2008-08-07 17:52:43 +0000
@@ -105,7 +105,7 @@ class Select_fetch_protocol_binary: publ
Protocol_binary protocol;
public:
Select_fetch_protocol_binary(THD *thd);
- virtual bool send_fields(List<Item> &list, uint flags);
+ virtual bool send_result_set_metadata(List<Item> &list, uint flags);
virtual bool send_data(List<Item> &items);
virtual bool send_eof();
#ifdef EMBEDDED_LIBRARY
@@ -258,7 +258,7 @@ static bool send_prep_stmt(Prepared_stat
error= my_net_write(net, buff, sizeof(buff));
if (stmt->param_count && ! error)
{
- error= thd->protocol_text.send_fields((List<Item> *)
+ error= thd->protocol_text.send_result_set_metadata((List<Item> *)
&stmt->lex->param_list,
Protocol::SEND_EOF);
}
@@ -1373,7 +1373,7 @@ static int mysql_test_select(Prepared_st
unit->prepare call above.
*/
if (send_prep_stmt(stmt, lex->result->field_count(fields)) ||
- lex->result->send_fields(fields, Protocol::SEND_EOF) ||
+ lex->result->send_result_set_metadata(fields, Protocol::SEND_EOF) ||
thd->protocol->flush())
goto error;
DBUG_RETURN(2);
@@ -2774,19 +2774,19 @@ Select_fetch_protocol_binary::Select_fet
:protocol(thd_arg)
{}
-bool Select_fetch_protocol_binary::send_fields(List<Item> &list, uint flags)
+bool Select_fetch_protocol_binary::send_result_set_metadata(List<Item> &list,
uint flags)
{
bool rc;
Protocol *save_protocol= thd->protocol;
/*
- Protocol::send_fields caches the information about column types:
+ Protocol::send_result_set_metadata caches the information about column types:
this information is later used to send data. Therefore, the same
dedicated Protocol object must be used for all operations with
a cursor.
*/
thd->protocol= &protocol;
- rc= select_send::send_fields(list, flags);
+ rc= select_send::send_result_set_metadata(list, flags);
thd->protocol= save_protocol;
return rc;
@@ -3596,6 +3596,9 @@ bool Prepared_statement::execute(String
if (state == Query_arena::PREPARED)
state= Query_arena::EXECUTED;
+
+ if (this->lex->sql_command == SQLCOM_CALL)
+ protocol->send_out_parameters(&this->lex->param_list);
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
=== modified file 'sql/sql_profile.cc'
--- a/sql/sql_profile.cc 2008-06-25 16:28:57 +0000
+++ b/sql/sql_profile.cc 2008-08-07 17:52:43 +0000
@@ -412,7 +412,7 @@ bool PROFILING::show_profiles()
MYSQL_TYPE_DOUBLE));
field_list.push_back(new Item_empty_string("Query", 40));
- if (thd->protocol->send_fields(&field_list,
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/sql_repl.cc'
--- a/sql/sql_repl.cc 2008-07-09 07:12:43 +0000
+++ b/sql/sql_repl.cc 2008-08-07 17:52:43 +0000
@@ -1448,7 +1448,7 @@ bool mysql_show_binlog_events(THD* thd)
DBUG_ENTER("mysql_show_binlog_events");
Log_event::init_show_field_list(&field_list);
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -1598,7 +1598,7 @@ bool show_binlog_info(THD* thd)
field_list.push_back(new Item_empty_string("Binlog_Do_DB",255));
field_list.push_back(new Item_empty_string("Binlog_Ignore_DB",255));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -1653,7 +1653,7 @@ bool show_binlogs(THD* thd)
field_list.push_back(new Item_empty_string("Log_name", 255));
field_list.push_back(new Item_return_int("File_size", 20,
MYSQL_TYPE_LONGLONG));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc 2008-07-25 17:21:55 +0000
+++ b/sql/sql_select.cc 2008-08-07 17:52:43 +0000
@@ -2274,7 +2274,7 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
- result->send_fields(*columns_list,
+ result->send_result_set_metadata(*columns_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
/*
We have to test for 'conds' here as the WHERE may not be constant
@@ -2665,7 +2665,7 @@ JOIN::exec()
}
if (curr_join->group_list || curr_join->order)
{
- DBUG_PRINT("info",("Sorting for send_fields"));
+ DBUG_PRINT("info",("Sorting for send_result_set_metadata"));
thd_proc_info(thd, "Sorting result");
/* If we have already done the group, add HAVING to sorted table */
if (curr_join->tmp_having && ! curr_join->group_list &&
@@ -2805,7 +2805,7 @@ JOIN::exec()
{
thd_proc_info(thd, "Sending data");
DBUG_PRINT("info", ("%s", thd->proc_info));
- result->send_fields((procedure ? curr_join->procedure_fields_list :
+ result->send_result_set_metadata((procedure ? curr_join->procedure_fields_list
:
*curr_fields_list),
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF);
error= do_select(curr_join, curr_fields_list, NULL, procedure);
@@ -8959,7 +8959,7 @@ return_zero_rows(JOIN *join, select_resu
if (having && having->val_int() == 0)
send_row=0;
}
- if (!(result->send_fields(fields,
+ if (!(result->send_result_set_metadata(fields,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF)))
{
if (send_row)
@@ -11579,7 +11579,7 @@ void setup_tmp_table_column_bitmaps(TABL
Create a temp table according to a field list.
Given field pointers are changed to point at tmp_table for
- send_fields. The table object is self contained: it's
+ send_result_set_metadata. The table object is self contained: it's
allocated in its own memory root, as well as Field objects
created for table columns.
This function will replace Item_sum items in 'fields' list with
@@ -17800,7 +17800,7 @@ int test_if_item_cache_changed(List<Cach
Only FIELD_ITEM:s and FUNC_ITEM:s needs to be saved between groups.
Change old item_field to use a new field with points at saved fieldvalue
- This function is only called before use of send_fields.
+ This function is only called before use of send_result_set_metadata.
@param thd THD pointer
@param param temporary table parameters
@@ -18031,7 +18031,7 @@ bool JOIN::alloc_func_list()
Initialize 'sum_funcs' array with all Item_sum objects.
@param field_list All items
- @param send_fields Items in select list
+ @param send_result_set_metadata Items in select list
@param before_group_by Set to 1 if this is called before GROUP BY handling
@param recompute Set to TRUE if sum_funcs must be recomputed
@@ -18041,7 +18041,7 @@ bool JOIN::alloc_func_list()
1 error
*/
-bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item>
&send_fields,
+bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item>
&send_result_set_metadata,
bool before_group_by, bool recompute)
{
List_iterator_fast<Item> it(field_list);
@@ -18063,7 +18063,7 @@ bool JOIN::make_sum_func_list(List<Item>
if (before_group_by && rollup.state == ROLLUP::STATE_INITED)
{
rollup.state= ROLLUP::STATE_READY;
- if (rollup_make_fields(field_list, send_fields, &func))
+ if (rollup_make_fields(field_list, send_result_set_metadata, &func))
DBUG_RETURN(TRUE); // Should never happen
}
else if (rollup.state == ROLLUP::STATE_NONE)
=== modified file 'sql/sql_select.h'
--- a/sql/sql_select.h 2008-07-11 16:22:44 +0000
+++ b/sql/sql_select.h 2008-08-07 17:52:43 +0000
@@ -635,7 +635,7 @@ public:
bool alloc_func_list();
bool flatten_subqueries();
bool setup_subquery_materialization();
- bool make_sum_func_list(List<Item> &all_fields, List<Item>
&send_fields,
+ bool make_sum_func_list(List<Item> &all_fields, List<Item>
&send_result_set_metadata,
bool before_group_by, bool recompute= FALSE);
inline void set_items_ref_array(Item **ptr)
=== modified file 'sql/sql_show.cc'
--- a/sql/sql_show.cc 2008-07-24 07:44:21 +0000
+++ b/sql/sql_show.cc 2008-08-07 17:52:43 +0000
@@ -216,7 +216,7 @@ bool mysqld_show_authors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -250,7 +250,7 @@ bool mysqld_show_contributors(THD *thd)
field_list.push_back(new Item_empty_string("Location",40));
field_list.push_back(new Item_empty_string("Comment",80));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -326,7 +326,7 @@ bool mysqld_show_privileges(THD *thd)
field_list.push_back(new Item_empty_string("Context",15));
field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -563,7 +563,7 @@ mysqld_show_create(THD *thd, TABLE_LIST
max(buffer.length(),1024)));
}
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
protocol->prepare_for_resend();
@@ -641,7 +641,7 @@ bool mysqld_show_create_db(THD *thd, cha
field_list.push_back(new Item_empty_string("Database",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Create Database",1024));
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -691,7 +691,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST
}
restore_record(table, s->default_values); // Get empty record
table->use_all_columns();
- if (thd->protocol->send_fields(&field_list, Protocol::SEND_DEFAULTS))
+ if (thd->protocol->send_result_set_metadata(&field_list,
Protocol::SEND_DEFAULTS))
DBUG_VOID_RETURN;
my_eof(thd);
DBUG_VOID_RETURN;
@@ -1657,7 +1657,7 @@ void mysqld_list_processes(THD *thd,cons
field->maybe_null=1;
field_list.push_back(field=new Item_empty_string("Info",max_query_length));
field->maybe_null=1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_VOID_RETURN;
@@ -7182,7 +7182,7 @@ static bool show_create_trigger_impl(THD
fields.push_back(new Item_empty_string("Database Collation",
MY_CS_NAME_SIZE));
- if (p->send_fields(&fields, Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
+ if (p->send_result_set_metadata(&fields, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
return TRUE;
/* Send data. */
=== modified file 'sql/sql_table.cc'
--- a/sql/sql_table.cc 2008-07-26 16:38:20 +0000
+++ b/sql/sql_table.cc 2008-08-07 17:52:43 +0000
@@ -4165,7 +4165,7 @@ static bool mysql_admin_table(THD* thd,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_text", 255, cs));
item->maybe_null = 1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
@@ -7459,7 +7459,7 @@ bool mysql_checksum_table(THD *thd, TABL
field_list.push_back(item= new Item_int("Checksum", (longlong) 1,
MY_INT64_NUM_DECIMAL_DIGITS));
item->maybe_null= 1;
- if (protocol->send_fields(&field_list,
+ if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
=== modified file 'tests/mysql_client_test.c'
--- a/tests/mysql_client_test.c 2008-07-26 16:38:20 +0000
+++ b/tests/mysql_client_test.c 2008-08-07 17:52:43 +0000
@@ -1499,6 +1499,542 @@ static void test_prepare_simple()
myquery(rc);
}
+/************************************************************************/
+
+#define FILE_PATH_SIZE 4096
+
+char mct_log_file_path[FILE_PATH_SIZE];
+FILE *mct_log_file;
+
+void mct_start_logging(const char *test_case_name)
+{
+ const char *tmp_dir= getenv("MYSQL_TMP_DIR");
+
+ if (!tmp_dir)
+ {
+ printf("Warning: MYSQL_TMP_DIR is not set. Logging is disabled.\n");
+ return;
+ }
+
+ /*
+ Path is: <tmp_dir>/<test_case_name>.out.log
+ 10 is length of '/' + '.out.log' + \0
+ */
+
+ if (strlen(tmp_dir) + strlen(test_case_name) + 10 > FILE_PATH_SIZE)
+ {
+ printf("Warning: MYSQL_TMP_DIR is too long. Logging is disabled.\n");
+ return;
+ }
+
+ my_snprintf(mct_log_file_path, FILE_PATH_SIZE,
+ "%s/%s.out.log",
+ (const char *) tmp_dir,
+ (const char *) test_case_name);
+
+ mct_log_file= my_fopen(mct_log_file_path, O_WRONLY, MYF(MY_WME));
+
+ if (!mct_log_file)
+ {
+ printf("Warning: can not open log file (%s): %s. Logging is disabled.\n",
+ (const char *) mct_log_file_path,
+ (const char *) strerror(errno));
+ return;
+ }
+}
+
+void mct_log(const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vprintf(format, args);
+ va_end(args);
+
+ if (mct_log_file)
+ {
+ va_list args;
+ va_start(args, format);
+ vfprintf(mct_log_file, format, args);
+ va_end(args);
+ }
+}
+
+int mct_check_result(const char *result_file_name)
+{
+ const int CMD_BUFFER_SIZE= 8192;
+ char diff_cmd[CMD_BUFFER_SIZE];
+
+ const char *test_dir= getenv("MYSQL_TEST_DIR");
+ char result_file_path[FILE_PATH_SIZE];
+
+ if (!mct_log_file)
+ {
+ printf("Warning: logging was disabled. Result checking is impossible.\n");
+ return 0;
+ }
+
+ if (!test_dir)
+ {
+ printf("Warning: MYSQL_TEST_DIR is not set. "
+ "Result checking is impossible.\n");
+ return 0;
+ }
+
+ /*
+ Path is: <test_dir>/<result_file_name>
+ 2 is length of '/' + \0
+ */
+
+ if (strlen(test_dir) + strlen(result_file_name) + 2 > FILE_PATH_SIZE)
+ {
+ printf("Warning: MYSQL_TEST_DIR is too long. "
+ "Result checking is impossible.\n");
+ return 0;
+ }
+
+ my_snprintf(result_file_path, FILE_PATH_SIZE,
+ "%s/%s",
+ (const char *) test_dir,
+ (const char *) result_file_name);
+
+ my_fclose(mct_log_file, MYF(0));
+ mct_log_file= NULL;
+
+ snprintf(diff_cmd, CMD_BUFFER_SIZE, "diff -u '%s' '%s'",
+ (const char *) result_file_path,
+ (const char *) mct_log_file_path);
+
+ puts("");
+ fflush(stdout);
+ fflush(stderr);
+
+ return system(diff_cmd);
+}
+
+#define WL4435_NUM_PARAMS 10
+#define WL4435_STRING_SIZE 30
+
+static void test_wl4435()
+{
+ MYSQL_STMT *stmt;
+ int rc;
+ char query[MAX_TEST_QUERY_LENGTH];
+
+ char str_data[20][WL4435_STRING_SIZE];
+ double dbl_data[20];
+ char dec_data[20][WL4435_STRING_SIZE];
+ int int_data[20];
+ ulong str_length= WL4435_STRING_SIZE;
+ my_bool is_null;
+ MYSQL_BIND ps_params[WL4435_NUM_PARAMS];
+
+ int exec_counter;
+
+ myheader("test_wl4435");
+ mct_start_logging("test_wl4435");
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t1");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "DROP TABLE IF EXISTS t2");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t1(a1 INT, a2 CHAR(32), "
+ " a3 DOUBLE(4, 2), a4 DECIMAL(3, 1))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "CREATE TABLE t2(b0 INT, b1 INT, b2 CHAR(32), "
+ " b3 DOUBLE(4, 2), b4 DECIMAL(3, 1))");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t1 VALUES"
+ "(1, '11', 12.34, 56.7), "
+ "(2, '12', 56.78, 90.1), "
+ "(3, '13', 23.45, 67.8)");
+ myquery(rc);
+
+ rc= mysql_query(mysql, "INSERT INTO t2 VALUES"
+ "(100, 10, '110', 70.70, 10.1), "
+ "(200, 20, '120', 80.80, 20.2), "
+ "(300, 30, '130', 90.90, 30.3)");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE PROCEDURE p1("
+ " IN v0 INT, "
+ " OUT v_str_1 CHAR(32), "
+ " OUT v_dbl_1 DOUBLE(4, 2), "
+ " OUT v_dec_1 DECIMAL(6, 3), "
+ " OUT v_int_1 INT, "
+ " IN v1 INT, "
+ " INOUT v_str_2 CHAR(64), "
+ " INOUT v_dbl_2 DOUBLE(5, 3), "
+ " INOUT v_dec_2 DECIMAL(7, 4), "
+ " INOUT v_int_2 INT)"
+ "BEGIN "
+ " SET v0 = -1; "
+ " SET v1 = -1; "
+ " SET v_str_1 = 'test_1'; "
+ " SET v_dbl_1 = 12.34; "
+ " SET v_dec_1 = 567.891; "
+ " SET v_int_1 = 2345; "
+ " SET v_str_2 = 'test_2'; "
+ " SET v_dbl_2 = 67.891; "
+ " SET v_dec_2 = 234.6789; "
+ " SET v_int_2 = 6789; "
+ " SELECT * FROM t1; "
+ " SELECT * FROM t2; "
+ "END");
+ myquery(rc);
+
+ rc= mysql_query(mysql,
+ "CREATE PROCEDURE p2("
+ " IN i1 VARCHAR(255) CHARACTER SET koi8r, "
+ " OUT o1 VARCHAR(255) CHARACTER SET cp1251, "
+ " OUT o2 VARBINARY(255)) "
+ "BEGIN "
+ " SET o1 = i1; "
+ " SET o2 = i1; "
+ "END");
+ myquery(rc);
+
+ strmov(query, "CALL p1(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /* Init PS-parameters. */
+
+ bzero((char *) ps_params, sizeof (ps_params));
+
+ /* - v0 -- INT */
+
+ ps_params[0].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[0].buffer= (char *) &int_data[0];
+ ps_params[0].length= 0;
+ ps_params[0].is_null= 0;
+
+ /* - v_str_1 -- CHAR(32) */
+
+ ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[1].buffer= (char *) str_data[0];
+ ps_params[1].buffer_length= WL4435_STRING_SIZE;
+ ps_params[1].length= &str_length;
+ ps_params[1].is_null= 0;
+
+ /* - v_dbl_1 -- DOUBLE */
+
+ ps_params[2].buffer_type= MYSQL_TYPE_DOUBLE;
+ ps_params[2].buffer= (char *) &dbl_data[0];
+ ps_params[2].length= 0;
+ ps_params[2].is_null= 0;
+
+ /* - v_dec_1 -- DECIMAL */
+
+ ps_params[3].buffer_type= MYSQL_TYPE_NEWDECIMAL;
+ ps_params[3].buffer= (char *) dec_data[0];
+ ps_params[3].buffer_length= WL4435_STRING_SIZE;
+ ps_params[3].length= 0;
+ ps_params[3].is_null= 0;
+
+ /* - v_int_1 -- INT */
+
+ ps_params[4].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[4].buffer= (char *) &int_data[0];
+ ps_params[4].length= 0;
+ ps_params[4].is_null= 0;
+
+ /* - v1 -- INT */
+
+ ps_params[5].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[5].buffer= (char *) &int_data[0];
+ ps_params[5].length= 0;
+ ps_params[5].is_null= 0;
+
+ /* - v_str_2 -- CHAR(32) */
+
+ ps_params[6].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[6].buffer= (char *) str_data[0];
+ ps_params[6].buffer_length= WL4435_STRING_SIZE;
+ ps_params[6].length= &str_length;
+ ps_params[6].is_null= 0;
+
+ /* - v_dbl_2 -- DOUBLE */
+
+ ps_params[7].buffer_type= MYSQL_TYPE_DOUBLE;
+ ps_params[7].buffer= (char *) &dbl_data[0];
+ ps_params[7].length= 0;
+ ps_params[7].is_null= 0;
+
+ /* - v_dec_2 -- DECIMAL */
+
+ ps_params[8].buffer_type= MYSQL_TYPE_DECIMAL;
+ ps_params[8].buffer= (char *) dec_data[0];
+ ps_params[8].buffer_length= WL4435_STRING_SIZE;
+ ps_params[8].length= 0;
+ ps_params[8].is_null= 0;
+
+ /* - v_int_2 -- INT */
+
+ ps_params[9].buffer_type= MYSQL_TYPE_LONG;
+ ps_params[9].buffer= (char *) &int_data[0];
+ ps_params[9].length= 0;
+ ps_params[9].is_null= 0;
+
+ /* Bind parameters. */
+
+ rc= mysql_stmt_bind_param(stmt, ps_params);
+
+ /* Execute! */
+
+ for (exec_counter= 0; exec_counter < 3; ++exec_counter)
+ {
+ int i;
+ int num_fields;
+ MYSQL_BIND *rs_bind;
+
+ mct_log("\nexec_counter: %d\n", (int) exec_counter);
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ while (1)
+ {
+ MYSQL_FIELD *fields;
+
+ MYSQL_RES *rs= mysql_stmt_result_metadata(stmt);
+
+ num_fields= mysql_num_fields(rs);
+ fields= mysql_fetch_fields(rs);
+
+ rs_bind= (MYSQL_BIND *) malloc(sizeof (MYSQL_BIND) * num_fields);
+ bzero(rs_bind, sizeof (MYSQL_BIND) * num_fields);
+
+ mct_log("num_fields: %d\n", (int) num_fields);
+
+ for (i = 0; i < num_fields; ++i)
+ {
+ mct_log(" - %d: name: '%s'/'%s'; table: '%s'/'%s'; "
+ "db: '%s'; catalog: '%s'; length: %d; max_length: %d; "
+ "type: %d; decimals: %d\n",
+ (int) i,
+ (const char *) fields[i].name,
+ (const char *) fields[i].org_name,
+ (const char *) fields[i].table,
+ (const char *) fields[i].org_table,
+ (const char *) fields[i].db,
+ (const char *) fields[i].catalog,
+ (int) fields[i].length,
+ (int) fields[i].max_length,
+ (int) fields[i].type,
+ (int) fields[i].decimals);
+
+ rs_bind[i].buffer_type= fields[i].type;
+ rs_bind[i].is_null= &is_null;
+
+ switch (fields[i].type)
+ {
+ case MYSQL_TYPE_LONG:
+ rs_bind[i].buffer= (char *) &(int_data[i]);
+ rs_bind[i].buffer_length= sizeof (int_data);
+ break;
+
+ case MYSQL_TYPE_STRING:
+ rs_bind[i].buffer= (char *) str_data[i];
+ rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+ rs_bind[i].length= &str_length;
+ break;
+
+ case MYSQL_TYPE_DOUBLE:
+ rs_bind[i].buffer= (char *) &dbl_data[i];
+ rs_bind[i].buffer_length= sizeof (dbl_data);
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ rs_bind[i].buffer= (char *) dec_data[i];
+ rs_bind[i].buffer_length= WL4435_STRING_SIZE;
+ rs_bind[i].length= &str_length;
+ break;
+
+ default:
+ fprintf(stderr, "ERROR: unexpected type: %d.\n", fields[i].type);
+ exit(1);
+ }
+ }
+
+ rc= mysql_stmt_bind_result(stmt, rs_bind);
+ check_execute(stmt, rc);
+
+ mct_log("Data:\n");
+
+ while (1)
+ {
+ int rc= mysql_stmt_fetch(stmt);
+
+ if (rc == 1 || rc == MYSQL_NO_DATA)
+ break;
+
+ mct_log(" ");
+
+ for (i = 0; i < num_fields; ++i)
+ {
+ switch (rs_bind[i].buffer_type)
+ {
+ case MYSQL_TYPE_LONG:
+ mct_log(" int: %ld;",
+ (long) *((int *) rs_bind[i].buffer));
+ break;
+
+ case MYSQL_TYPE_STRING:
+ mct_log(" str: '%s';",
+ (char *) rs_bind[i].buffer);
+ break;
+
+ case MYSQL_TYPE_DOUBLE:
+ mct_log(" dbl: %lf;",
+ (double) *((double *) rs_bind[i].buffer));
+ break;
+
+ case MYSQL_TYPE_NEWDECIMAL:
+ mct_log(" dec: '%s';",
+ (char *) rs_bind[i].buffer);
+ break;
+
+ default:
+ printf(" unexpected type (%d)\n",
+ rs_bind[i].buffer_type);
+ }
+ }
+ mct_log("\n");
+ }
+
+ mct_log("EOF\n");
+
+ rc= mysql_stmt_next_result(stmt);
+ mct_log("mysql_stmt_next_result(): %d; field_count: %d\n",
+ (int) rc, (int) mysql->field_count);
+
+ if (rc > 0)
+ {
+ printf("Error: %s (errno: %d)\n",
+ mysql_error(mysql), mysql_errno(mysql));
+ DIE(rc > 0);
+ }
+
+ if (rc)
+ break;
+
+ if (!mysql->field_count)
+ {
+ /* This is the last OK-packet. No more resultsets. */
+ break;
+ }
+ }
+
+ free(rs_bind);
+ }
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+
+ rc= mct_check_result("r/test_wl4435.result");
+ mytest_r(rc);
+
+ /* i18n part of test case. */
+
+ {
+ const char *str_koi8r= "\xee\xd5\x2c\x20\xda\xc1\x20\xd2\xd9\xc2\xc1\xcc\xcb\xd5";
+ const char *str_cp1251= "\xcd\xf3\x2c\x20\xe7\xe0\x20\xf0\xfb\xe1\xe0\xeb\xea\xf3";
+ char o1_buffer[255];
+ ulong o1_length;
+ char o2_buffer[255];
+ ulong o2_length;
+
+ MYSQL_BIND rs_bind[2];
+
+ strmov(query, "CALL p2(?, ?, ?)");
+ stmt= mysql_simple_prepare(mysql, query);
+ check_stmt(stmt);
+
+ /* Init PS-parameters. */
+
+ bzero((char *) ps_params, sizeof (ps_params));
+
+ ps_params[0].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[0].buffer= (char *) str_koi8r;
+ ps_params[0].buffer_length= strlen(str_koi8r);
+
+ ps_params[1].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[1].buffer= o1_buffer;
+ ps_params[1].buffer_length= 0;
+
+ ps_params[2].buffer_type= MYSQL_TYPE_STRING;
+ ps_params[2].buffer= o2_buffer;
+ ps_params[2].buffer_length= 0;
+
+ /* Bind parameters. */
+
+ rc= mysql_stmt_bind_param(stmt, ps_params);
+ check_execute(stmt, rc);
+
+ /* Prevent converting to character_set_results. */
+
+ rc= mysql_query(mysql, "SET NAMES binary");
+ myquery(rc);
+
+ /* Execute statement. */
+
+ rc= mysql_stmt_execute(stmt);
+ check_execute(stmt, rc);
+
+ /* Bind result. */
+
+ bzero(rs_bind, sizeof (rs_bind));
+
+ rs_bind[0].buffer_type= MYSQL_TYPE_STRING;
+ rs_bind[0].buffer= o1_buffer;
+ rs_bind[0].buffer_length= sizeof (o1_buffer);
+ rs_bind[0].length= &o1_length;
+
+ rs_bind[1].buffer_type= MYSQL_TYPE_BLOB;
+ rs_bind[1].buffer= o2_buffer;
+ rs_bind[1].buffer_length= sizeof (o2_buffer);
+ rs_bind[1].length= &o2_length;
+
+ rc= mysql_stmt_bind_result(stmt, rs_bind);
+ check_execute(stmt, rc);
+
+ /* Fetch result. */
+
+ rc= mysql_stmt_fetch(stmt);
+ check_execute(stmt, rc);
+
+ /* Check result. */
+
+ DIE_UNLESS(o1_length == strlen(str_cp1251));
+ DIE_UNLESS(o2_length == strlen(str_koi8r));
+ DIE_UNLESS(!memcmp(o1_buffer, str_cp1251, o1_length));
+ DIE_UNLESS(!memcmp(o2_buffer, str_koi8r, o2_length));
+
+ rc= mysql_stmt_fetch(stmt);
+ DIE_UNLESS(rc == MYSQL_NO_DATA);
+
+ rc= mysql_stmt_next_result(stmt);
+ DIE_UNLESS(rc == 0 && mysql->field_count == 0);
+
+ mysql_stmt_close(stmt);
+
+ rc= mysql_commit(mysql);
+ myquery(rc);
+ }
+}
/* Test simple prepare field results */
@@ -14488,9 +15024,8 @@ static void test_bug12001()
/* Create connection that supports multi statements */
if (!mysql_real_connect(mysql_local, opt_host, opt_user,
- opt_password, current_db, opt_port,
- opt_unix_socket, CLIENT_MULTI_STATEMENTS |
- CLIENT_MULTI_RESULTS))
+ opt_password, current_db, opt_port,
+ opt_unix_socket, CLIENT_MULTI_STATEMENTS))
{
fprintf(stdout, "\n mysql_real_connect() failed");
exit(1);
@@ -15862,7 +16397,7 @@ static void test_bug15752()
if (! mysql_real_connect(&mysql_local, opt_host, opt_user,
opt_password, current_db, opt_port,
opt_unix_socket,
- CLIENT_MULTI_STATEMENTS|CLIENT_MULTI_RESULTS))
+ CLIENT_MULTI_STATEMENTS))
{
printf("Unable connect to MySQL server: %s\n", mysql_error(&mysql_local));
DIE_UNLESS(0);
@@ -18193,6 +18728,7 @@ static struct my_tests_st my_tests[]= {
{ "test_wl4166_4", test_wl4166_4 },
{ "test_bug36004", test_bug36004 },
{ "test_wl4284_1", test_wl4284_1 },
+ { "test_wl4435", test_wl4435 },
{ 0, 0 }
};
| Thread |
|---|
| • bzr push into mysql-6.0-runtime branch (alik:2690 to 2691) WL#4435 | Alexander Nozdrin | 7 Aug |