Backport of revno 2630.28.10, 2630.28.31, 2630.28.26, 2630.33.1,

2630.39.1, 2630.28.29, 2630.34.3, 2630.34.2, 2630.34.1, 2630.29.29,
2630.29.28, 2630.31.1, 2630.28.13, 2630.28.10, 2617.23.14 and
some other minor revisions.

This patch implements: 

WL#4264 "Backup: Stabilize Service Interface" -- all the
server prerequisites except si_objects.{h,cc} themselves (they can
be just copied over, when needed).

WL#4435: Support OUT-parameters in prepared statements.

(and all issues in the initial patches for these two
tasks, that were discovered in pushbuild and during testing).

Bug#39519: mysql_stmt_close() should flush all data
associated with the statement.

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.

include/mysql.h:
  Add a new flag to MYSQL_METHODS::flush_use_result
  function pointer. This flag determines if all results
  should be flushed or only the first one:
      
  - if flush_all_results is TRUE, then cli_flush_use_result()
    will read/flush all pending results. I.e. it will read
    all packets while server status attribute indicates that
    there are more results. This is a new semantic, required
    to fix the bug.
              
  - if flush_all_results is FALSE, the old sematic
    is preserved -- i.e. cli_flush_use_result() reads data
    until first EOF-packet.
include/mysql.h.pp:
  Update the ABI with new calls (compatible changes).
include/mysql_com.h:
  Add CLIENT_PS_OUT_PARAMS -- a client capability indicating that the client supportsю
libmysql/libmysql.c:
  Add mysql_stmt_next_result() -- analogue of mysql_next_result() for binary protocol.
  Fix a minor bug in alloc_fields() -- not all members were copied over,
  and some only shallow-copied (catalog).
  Flush all results in mysql_stmt_close() (Bug#39519).
libmysqld/lib_sql.cc:
  Rename send_fields() -> send_result_set_metadata().
  Refactoring: change prepare_for_send() so that it accepts only 
  what it really needs -- a number of elements in the list.
mysql-test/r/ps.result:
  Update results: WL#4435.
mysql-test/t/ps.test:
  WL#4435: A test case for an SQL-part of the problem.
sql-common/client.c:
  Bug#39519.
  Implement new functionality in cli_flush_use_result():
  if flush_all_delete is TRUE, then it should read/flush
  all pending results.
sql/Makefile.am:
  Add a new header sql_prepare.h to the list
  of build headers.
sql/events.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/handler.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/mysql_priv.h:
  Move sql_prepare.cc-specific declarations to a new
  header - sql_prepare.h.
sql/procedure.h:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/protocol.cc:
  Move the logic responsible for sending of one result
  set row to the Protocol class. Define a template
  for end-of-statement action. 
  Refactoring: change prepare_for_send() so that it accepts 
  only what it really needs -- a number of elements in the list.
  Rename send_fields() to send_result_set_metadata().
sql/protocol.h:
  Update with new declarations (WL#4435).
  Rename send_fields() -> send_result_set_metadata().
  prepare_for_send() only needs the number of columns to send,
  and doesn't use the item list - update signature to require
  only what's needed.
  Add a new protocol type -- Protocol_local.
sql/repl_failsafe.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/slave.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_acl.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_base.cc:
  Include sql_prepare.h (for Reprepare_observer).
sql/sql_cache.cc:
  Extend the query cache flags block to be able
  to store a numeric id for the result format,
  not just a flag binary/non-binary.
sql/sql_class.cc:
  Update to use the rename of Protocol::send_fields()
  to Protocol::send_result_set_metadata().
  Use Protocol::send_one_result_set_row().
sql/sql_class.h:
  Move the declaration of Reprepare_observer to the 
  new header - sql_prepare.h.
  Update to the new signature of class Protocol::send_fields().
sql/sql_connect.cc:
  Use a protocol template method instead of
  raw NET layer API at the end of a statement.
sql/sql_cursor.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_error.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_handler.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
  Use new method Protocol::send_one_result_set_row().
sql/sql_help.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_lex.cc:
  Initialize multi_statements variable.
  Add a handy constant for empty lex
  string.
sql/sql_lex.h:
  Add a separate member for a standalone
  parsing option - multi-statements support.
sql/sql_list.cc:
  sql_list.h is a standalone header now, 
  no need to include mysql_priv.h.
sql/sql_list.h:
  Make sql_list.h a stand-alone header.
sql/sql_parse.cc:
  Include sql_prepare.h for prepared
  statements- related declarations.
  Use a new Protocol template method to end
  each statement (send OK, EOF or ERROR to
  the client).
sql/sql_prepare.cc:
  Implement Execute Direct API (WL#4264), 
  currently unused. It will be used by the service
  interface (Backup).
  Use a new header - sql_prepare.h.
  Add support for OUT parameters in the 
  binary and text protocol (prepared statements 
  only).
sql/sql_prepare.h:
  Add a new header to contain (for now)
  all prepared statement- external
  related declarations.
sql/sql_profile.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_repl.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_select.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_show.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_string.h:
  Add a way to convert a String to LEX_STRING.
sql/sql_table.cc:
  Rename: Protocol::send_fields() -> 
  Protocol::send_result_set_metadata().
sql/sql_update.cc:
  Remove an extraneous my_error(). The error
  is already reported in update_non_unique_table_error().
sql/sql_yacc.yy:
  Support for multi-statements is an independent
  property of parsing, not derived from 
  the protocol type.
tests/mysql_client_test.c:
  Add tests for WL#4435 (binary protocol).
This commit is contained in:
Konstantin Osipov 2009-10-22 00:02:06 +04:00
parent f0ccd917cc
commit 8ec23470f1
47 changed files with 2899 additions and 328 deletions

View file

@ -656,7 +656,7 @@ typedef struct st_mysql_methods
MYSQL_RES * (*use_result)(MYSQL *mysql);
void (*fetch_lengths)(unsigned long *to,
MYSQL_ROW column, unsigned int field_count);
void (*flush_use_result)(MYSQL *mysql);
void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
#if !defined(MYSQL_SERVER) || defined(EMBEDDED_LIBRARY)
MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
@ -716,6 +716,7 @@ my_bool STDCALL mysql_rollback(MYSQL * mysql);
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);

View file

@ -560,7 +560,7 @@ typedef struct st_mysql_methods
MYSQL_RES * (*use_result)(MYSQL *mysql);
void (*fetch_lengths)(unsigned long *to,
MYSQL_ROW column, unsigned int field_count);
void (*flush_use_result)(MYSQL *mysql);
void (*flush_use_result)(MYSQL *mysql, my_bool flush_all_results);
MYSQL_FIELD * (*list_fields)(MYSQL *mysql);
my_bool (*read_prepare_result)(MYSQL *mysql, MYSQL_STMT *stmt);
int (*stmt_execute)(MYSQL_STMT *stmt);
@ -615,4 +615,5 @@ my_bool mysql_rollback(MYSQL * mysql);
my_bool mysql_autocommit(MYSQL * mysql, my_bool auto_mode);
my_bool mysql_more_results(MYSQL *mysql);
int mysql_next_result(MYSQL *mysql);
int mysql_stmt_next_result(MYSQL_STMT *stmt);
void mysql_close(MYSQL *sock);

View file

@ -144,6 +144,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)
@ -167,6 +168,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)
@ -204,6 +206,11 @@ enum enum_server_command
*/
#define SERVER_STATUS_METADATA_CHANGED 1024
/**
To mark ResultSet containing output parameter values.
*/
#define SERVER_PS_OUT_PARAMS 4096
/**
Server status flags that must be cleared when starting
execution of a new SQL statement.

View file

@ -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);

View file

@ -1722,6 +1722,8 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt)
MEM_ROOT *alloc= &stmt->mem_root;
MYSQL *mysql= stmt->mysql;
DBUG_ASSERT(mysql->field_count);
stmt->field_count= mysql->field_count;
/*
@ -1743,18 +1745,21 @@ static void alloc_stmt_fields(MYSQL_STMT *stmt)
field= stmt->fields;
field && fields < end; fields++, field++)
{
field->db = strdup_root(alloc,fields->db);
field->table = strdup_root(alloc,fields->table);
field->org_table= strdup_root(alloc,fields->org_table);
field->name = strdup_root(alloc,fields->name);
field->org_name = strdup_root(alloc,fields->org_name);
field->charsetnr= fields->charsetnr;
field->length = fields->length;
field->type = fields->type;
field->flags = fields->flags;
field->decimals = fields->decimals;
field->def = fields->def ? strdup_root(alloc,fields->def): 0;
field->max_length= 0;
*field= *fields; /* To copy all numeric parts. */
field->catalog= strmake_root(alloc, fields->catalog,
fields->catalog_length);
field->db= strmake_root(alloc, fields->db, fields->db_length);
field->table= strmake_root(alloc, fields->table, fields->table_length);
field->org_table= strmake_root(alloc, fields->org_table,
fields->org_table_length);
field->name= strmake_root(alloc, fields->name, fields->name_length);
field->org_name= strmake_root(alloc, fields->org_name,
fields->org_name_length);
field->def= fields->def ? strmake_root(alloc, fields->def,
fields->def_length) : 0;
field->def_length= field->def ? fields->def_length : 0;
field->extension= 0; /* Avoid dangling links. */
field->max_length= 0; /* max_length is set in mysql_stmt_store_result() */
}
}
@ -2481,6 +2486,33 @@ static void reinit_result_set_metadata(MYSQL_STMT *stmt)
}
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.
@ -2548,28 +2580,7 @@ int STDCALL mysql_stmt_execute(MYSQL_STMT *stmt)
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));
}
@ -4034,7 +4045,6 @@ static my_bool setup_one_fetch_function(MYSQL_BIND *param, MYSQL_FIELD *field)
field->max_length= 10; /* 2003-11-11 */
param->skip_result= skip_result_with_length;
break;
break;
case MYSQL_TYPE_DATETIME:
case MYSQL_TYPE_TIMESTAMP:
param->skip_result= skip_result_with_length;
@ -4614,7 +4624,7 @@ static my_bool reset_stmt_handle(MYSQL_STMT *stmt, uint flags)
if (stmt->field_count && mysql->status != MYSQL_STATUS_READY)
{
/* There is a result set and it belongs to this statement */
(*mysql->methods->flush_use_result)(mysql);
(*mysql->methods->flush_use_result)(mysql, FALSE);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
@ -4698,7 +4708,7 @@ my_bool STDCALL mysql_stmt_close(MYSQL_STMT *stmt)
Flush result set of the connection. If it does not belong
to this statement, set a warning.
*/
(*mysql->methods->flush_use_result)(mysql);
(*mysql->methods->flush_use_result)(mysql, TRUE);
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;
mysql->status= MYSQL_STATUS_READY;
@ -4846,6 +4856,49 @@ int STDCALL mysql_next_result(MYSQL *mysql)
}
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)
{
set_stmt_errmsg(stmt, &mysql->net);
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);
}
MYSQL_RES * STDCALL mysql_use_result(MYSQL *mysql)
{
return (*mysql->methods->use_result)(mysql);

View file

@ -148,7 +148,7 @@ emb_advanced_command(MYSQL *mysql, enum enum_server_command command,
return result;
}
static void emb_flush_use_result(MYSQL *mysql)
static void emb_flush_use_result(MYSQL *mysql, my_bool)
{
THD *thd= (THD*) mysql->thd;
if (thd->cur_data)
@ -653,7 +653,7 @@ int check_embedded_connection(MYSQL *mysql, const char *db)
strmake(sctx->priv_host, (char*) my_localhost, MAX_HOSTNAME-1);
sctx->priv_user= sctx->user= my_strdup(mysql->user, MYF(0));
result= check_user(thd, COM_CONNECT, NULL, 0, db, true);
net_end_statement(thd);
thd->protocol->end_statement();
emb_read_query_result(mysql);
return result;
}
@ -879,7 +879,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;
@ -888,7 +888,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
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);
@ -982,7 +982,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
write_eof_packet(thd, thd->server_status,
thd->warning_info->statement_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 */

View file

@ -2926,4 +2926,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.

View file

@ -3009,5 +3009,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.
###########################################################################

View file

@ -710,10 +710,14 @@ err:
}
#endif
/*****************************************************************************
/**
Read a packet from server. Give error message if socket was down
or packet is an error message
*****************************************************************************/
@retval packet_error An error occurred during reading.
Error message is set.
@retval
*/
ulong
cli_safe_read(MYSQL *mysql)
@ -879,31 +883,132 @@ void free_old_query(MYSQL *mysql)
DBUG_VOID_RETURN;
}
/**
Finish reading of a partial result set from the server.
Get the EOF packet, and update mysql->status
and mysql->warning_count.
@return TRUE if a communication or protocol error, an error
is set in this case, FALSE otherwise.
*/
my_bool flush_one_result(MYSQL *mysql)
{
ulong packet_length;
DBUG_ASSERT(mysql->status != MYSQL_STATUS_READY);
do
{
packet_length= cli_safe_read(mysql);
/*
There is an error reading from the connection,
or (sic!) there were no error and no
data in the stream, i.e. no more data from the server.
Since we know our position in the stream (somewhere in
the middle of a result set), this latter case is an error too
-- each result set must end with a EOF packet.
cli_safe_read() has set an error for us, just return.
*/
if (packet_length == packet_error)
return TRUE;
}
while (packet_length > 8 || mysql->net.read_pos[0] != 254);
/* Analyze EOF packet of the result set. */
if (protocol_41(mysql))
{
char *pos= (char*) mysql->net.read_pos + 1;
mysql->warning_count=uint2korr(pos);
pos+=2;
mysql->server_status=uint2korr(pos);
pos+=2;
}
return FALSE;
}
/**
Read a packet from network. If it's an OK packet, flush it.
@return TRUE if error, FALSE otherwise. In case of
success, is_ok_packet is set to TRUE or FALSE,
based on what we got from network.
*/
my_bool opt_flush_ok_packet(MYSQL *mysql, my_bool *is_ok_packet)
{
ulong packet_length= cli_safe_read(mysql);
if (packet_length == packet_error)
return TRUE;
/* cli_safe_read always reads a non-empty packet. */
DBUG_ASSERT(packet_length);
*is_ok_packet= mysql->net.read_pos[0] == 0;
if (*is_ok_packet)
{
uchar *pos= mysql->net.read_pos + 1;
net_field_length_ll(&pos); /* affected rows */
net_field_length_ll(&pos); /* insert id */
mysql->server_status=uint2korr(pos);
pos+=2;
if (protocol_41(mysql))
{
mysql->warning_count=uint2korr(pos);
pos+=2;
}
}
return FALSE;
}
/*
Flush result set sent from server
*/
static void cli_flush_use_result(MYSQL *mysql)
static void cli_flush_use_result(MYSQL *mysql, my_bool flush_all_results)
{
/* Clear the current execution status */
DBUG_ENTER("cli_flush_use_result");
DBUG_PRINT("warning",("Not all packets read, clearing them"));
for (;;)
if (flush_one_result(mysql))
DBUG_VOID_RETURN; /* An error occurred */
if (! flush_all_results)
DBUG_VOID_RETURN;
while (mysql->server_status & SERVER_MORE_RESULTS_EXISTS)
{
ulong pkt_len;
if ((pkt_len=cli_safe_read(mysql)) == packet_error)
break;
if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
my_bool is_ok_packet;
if (opt_flush_ok_packet(mysql, &is_ok_packet))
DBUG_VOID_RETURN; /* An error occurred. */
if (is_ok_packet)
{
if (protocol_41(mysql))
{
char *pos= (char*) mysql->net.read_pos + 1;
mysql->warning_count=uint2korr(pos); pos+=2;
mysql->server_status=uint2korr(pos); pos+=2;
}
break; /* End of data */
/*
Indeed what we got from network was an OK packet, and we
know that OK is the last one in a multi-result-set, so
just return.
*/
DBUG_VOID_RETURN;
}
/*
It's a result set, not an OK packet. A result set contains
of two result set subsequences: field metadata, terminated
with EOF packet, and result set data, again terminated with
EOF packet. Read and flush them.
*/
if (flush_one_result(mysql) || flush_one_result(mysql))
DBUG_VOID_RETURN; /* An error occurred. */
}
DBUG_VOID_RETURN;
}
@ -1009,7 +1114,7 @@ mysql_free_result(MYSQL_RES *result)
mysql->unbuffered_fetch_owner= 0;
if (mysql->status == MYSQL_STATUS_USE_RESULT)
{
(*mysql->methods->flush_use_result)(mysql);
(*mysql->methods->flush_use_result)(mysql, FALSE);
mysql->status=MYSQL_STATUS_READY;
if (mysql->unbuffered_fetch_owner)
*mysql->unbuffered_fetch_owner= TRUE;

View file

@ -110,7 +110,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
sql_plugin.h authors.h event_parse_data.h \
event_data_objects.h event_scheduler.h \
sql_partition.h partition_info.h partition_element.h \
contributors.h sql_servers.h sql_signal.h records.h
contributors.h sql_servers.h sql_signal.h records.h \
sql_prepare.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \

View file

@ -757,7 +757,7 @@ send_show_create_event(THD *thd, Event_timed *et, Protocol *protocol)
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);

View file

@ -1644,7 +1644,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);
@ -4434,7 +4434,7 @@ bool ha_show_status(THD *thd, handlerton *db_type, enum ha_stat_type stat)
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;

View file

@ -2583,7 +2583,8 @@ Item_param::Item_param(uint pos_in_query_arg) :
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*) "?";
/*
@ -2665,6 +2666,17 @@ void Item_param::set_decimal(const char *str, ulong length)
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.
@ -3287,6 +3299,158 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
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
****************************************************************************/
@ -3518,7 +3682,7 @@ void Item_copy_decimal::copy()
/*
Functions to convert item to field (for send_fields)
Functions to convert item to field (for send_result_set_metadata)
*/
/* ARGSUSED */

View file

@ -450,6 +450,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; }
};
@ -1560,7 +1565,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;
@ -1648,6 +1654,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);
@ -1697,6 +1704,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;
};
@ -2054,7 +2080,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.
*/

View file

@ -274,7 +274,7 @@ protected:
#define TABLE_OPEN_CACHE_DEFAULT 400
#define TABLE_DEF_CACHE_DEFAULT 400
/**
We must have room for at least 256 table definitions in the table
We must have room for at least 400 table definitions in the table
cache, since otherwise there is no chance prepared
statements that use these many tables can work.
Prepared statements use table definition cache ids (table_map_id)
@ -886,7 +886,7 @@ struct Query_cache_query_flags
{
unsigned int client_long_flag:1;
unsigned int client_protocol_41:1;
unsigned int result_in_binary_protocol:1;
unsigned int protocol_type:2;
unsigned int more_results_exists:1;
unsigned int in_trans:1;
unsigned int autocommit:1;
@ -1426,19 +1426,6 @@ enum enum_schema_tables get_schema_table_idx(ST_SCHEMA_TABLE *schema_table);
#define is_schema_db(X) \
!my_strcasecmp(system_charset_info, INFORMATION_SCHEMA_NAME.str, (X))
/* sql_prepare.cc */
void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysqld_stmt_close(THD *thd, char *packet);
void mysql_sql_stmt_prepare(THD *thd);
void mysql_sql_stmt_execute(THD *thd);
void mysql_sql_stmt_close(THD *thd);
void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysqld_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reinit_stmt_before_use(THD *thd, LEX *lex);
/* sql_handler.cc */
bool mysql_ha_open(THD *thd, TABLE_LIST *tables, bool reopen);
bool mysql_ha_close(THD *thd, TABLE_LIST *tables);

View file

@ -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
{

View file

@ -28,6 +28,7 @@
#include <stdarg.h>
static const unsigned int PACKET_BUFFER_EXTRA_ALLOC= 1024;
/* Declared non-static only because of the embedded library. */
bool net_send_error_packet(THD *, uint, const char *, const char *);
/* Declared non-static only because of the embedded library. */
bool net_send_ok(THD *, uint, uint, ulonglong, ulonglong, const char *);
@ -141,6 +142,7 @@ bool Protocol::net_store_data(const uchar *from, size_t length,
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate)
{
bool error;
DBUG_ENTER("net_send_error");
DBUG_ASSERT(!thd->spcont);
@ -148,7 +150,6 @@ bool net_send_error(THD *thd, uint sql_errno, const char *err,
DBUG_ASSERT(err);
DBUG_PRINT("enter",("sql_errno: %d err: %s", sql_errno, err));
bool error;
if (sqlstate == NULL)
sqlstate= mysql_errno_to_sqlstate(sql_errno);
@ -470,6 +471,12 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
packet is "buffered" in the diagnostics area and sent to the client
in the end of statement.
@note This method defines a template, but delegates actual
sending of data to virtual Protocol::send_{ok,eof,error}. This
allows for implementation of protocols that "intercept" ok/eof/error
messages, and store them in memory, etc, instead of sending to
the client.
@pre The diagnostics area is assigned or disabled. It can not be empty
-- we assume that every SQL statement or COM_* command
generates OK, ERROR, or EOF status.
@ -484,47 +491,94 @@ static uchar *net_store_length_fast(uchar *packet, uint length)
Diagnostics_area::is_sent is set for debugging purposes only.
*/
void net_end_statement(THD *thd)
void Protocol::end_statement()
{
DBUG_ENTER("Protocol::end_statement");
DBUG_ASSERT(! thd->stmt_da->is_sent);
bool error= FALSE;
/* Can not be true, but do not take chances in production. */
if (thd->stmt_da->is_sent)
return;
bool error= FALSE;
DBUG_VOID_RETURN;
switch (thd->stmt_da->status()) {
case Diagnostics_area::DA_ERROR:
/* The query failed, send error to log and abort bootstrap. */
error= net_send_error(thd,
thd->stmt_da->sql_errno(),
thd->stmt_da->message(),
thd->stmt_da->get_sqlstate());
error= send_error(thd->stmt_da->sql_errno(),
thd->stmt_da->message(),
thd->stmt_da->get_sqlstate());
break;
case Diagnostics_area::DA_EOF:
error= net_send_eof(thd,
thd->stmt_da->server_status(),
thd->stmt_da->statement_warn_count());
error= send_eof(thd->stmt_da->server_status(),
thd->stmt_da->statement_warn_count());
break;
case Diagnostics_area::DA_OK:
error= net_send_ok(thd,
thd->stmt_da->server_status(),
thd->stmt_da->statement_warn_count(),
thd->stmt_da->affected_rows(),
thd->stmt_da->last_insert_id(),
thd->stmt_da->message());
error= send_ok(thd->stmt_da->server_status(),
thd->stmt_da->statement_warn_count(),
thd->stmt_da->affected_rows(),
thd->stmt_da->last_insert_id(),
thd->stmt_da->message());
break;
case Diagnostics_area::DA_DISABLED:
break;
case Diagnostics_area::DA_EMPTY:
default:
DBUG_ASSERT(0);
error= net_send_ok(thd, thd->server_status, 0, 0, 0, NULL);
error= send_ok(thd->server_status, 0, 0, 0, NULL);
break;
}
if (!error)
thd->stmt_da->is_sent= TRUE;
DBUG_VOID_RETURN;
}
/**
A default implementation of "OK" packet response to the client.
Currently this implementation is re-used by both network-oriented
protocols -- the binary and text one. They do not differ
in their OK packet format, which allows for a significant simplification
on client side.
*/
bool Protocol::send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
const char *message)
{
DBUG_ENTER("Protocol::send_ok");
DBUG_RETURN(net_send_ok(thd, server_status, statement_warn_count,
affected_rows, last_insert_id, message));
}
/**
A default implementation of "EOF" packet response to the client.
Binary and text protocol do not differ in their EOF packet format.
*/
bool Protocol::send_eof(uint server_status, uint statement_warn_count)
{
DBUG_ENTER("Protocol::send_eof");
DBUG_RETURN(net_send_eof(thd, server_status, statement_warn_count));
}
/**
A default implementation of "ERROR" packet response to the client.
Binary and text protocol do not differ in ERROR packet format.
*/
bool Protocol::send_error(uint sql_errno, const char *err_msg,
const char *sql_state)
{
DBUG_ENTER("Protocol::send_error");
DBUG_RETURN(net_send_error_packet(thd, sql_errno, err_msg, sql_state));
}
@ -581,9 +635,10 @@ void Protocol::init(THD *thd_arg)
for the error.
*/
void Protocol::end_partial_result_set(THD *thd)
void Protocol::end_partial_result_set(THD *thd_arg)
{
net_send_eof(thd, thd->server_status, 0 /* no warnings, we're inside SP */);
net_send_eof(thd_arg, thd_arg->server_status,
0 /* no warnings, we're inside SP */);
}
@ -616,16 +671,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
@ -770,7 +825,7 @@ bool Protocol::send_fields(List<Item> *list, uint flags)
write_eof_packet(thd, &thd->net, thd->server_status,
thd->warning_info->statement_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),
@ -788,6 +843,47 @@ bool Protocol::write()
#endif /* EMBEDDED_LIBRARY */
/**
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))
{
// If we're out of memory, reclaim some, to help us recover.
this->free();
DBUG_RETURN(TRUE);
}
/* Item::send() may generate an error. If so, abort the loop. */
if (thd->is_error())
DBUG_RETURN(TRUE);
/*
Reset str_buffer to its original state, as it may have been altered in
Item::send().
*/
str_buffer.set(buffer, sizeof(buffer), &my_charset_bin);
}
DBUG_RETURN(FALSE);
}
/**
Send \\0 end terminated string.
@ -834,7 +930,6 @@ bool Protocol::store(I_List<i_string>* str_list)
return store((char*) tmp.ptr(), len, tmp.charset());
}
/****************************************************************************
Functions to handle the simple (default) protocol where everything is
This protocol is the one that is used by default between the MySQL server
@ -1113,6 +1208,53 @@ bool Protocol_text::store_time(MYSQL_TIME *tm)
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
@ -1133,14 +1275,13 @@ bool Protocol_text::store_time(MYSQL_TIME *tm)
[..]..[[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;
}
@ -1328,3 +1469,80 @@ bool Protocol_binary::store_time(MYSQL_TIME *tm)
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;
}

View file

@ -21,6 +21,7 @@
class i_string;
class THD;
class Item_param;
typedef struct st_mysql_field MYSQL_FIELD;
typedef struct st_mysql_rows MYSQL_ROWS;
@ -47,6 +48,16 @@ protected:
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
bool store_string_aux(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
virtual bool send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
const char *message);
virtual bool send_eof(uint server_status, uint statement_warn_count);
virtual bool send_error(uint sql_errno, const char *err_msg,
const char *sql_state);
public:
Protocol() {}
Protocol(THD *thd_arg) { init(thd_arg); }
@ -54,7 +65,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);
@ -72,9 +84,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();
@ -96,6 +108,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() {}
@ -104,13 +118,15 @@ public:
#endif
enum enum_protocol_type
{
PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1
/*
before adding here or change the values, consider that it is cast to a
bit in sql_cache.cc.
Before adding a new type, please make sure
there is enough storage for it in Query_cache_query_flags.
*/
PROTOCOL_TEXT= 0, PROTOCOL_BINARY= 1, PROTOCOL_LOCAL= 2
};
virtual enum enum_protocol_type type()= 0;
void end_statement();
};
@ -137,6 +153,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
@ -151,7 +169,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();
@ -172,13 +190,15 @@ 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; };
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
bool net_send_error(THD *thd, uint sql_errno, const char *err,
const char* sqlstate);
void net_end_statement(THD *thd);
bool send_old_password_request(THD *thd);
uchar *net_store_data(uchar *to,const uchar *from, size_t length);
uchar *net_store_data(uchar *to,int32 from);

View file

@ -471,7 +471,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();
@ -675,7 +675,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);

View file

@ -1605,7 +1605,7 @@ bool show_master_info(THD* thd, Master_info* mi)
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);

View file

@ -14,6 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "mysql_priv.h"
#include "sql_prepare.h"
#include "probes_mysql.h"
#ifdef USE_PRAGMA_IMPLEMENTATION
#pragma implementation
@ -2043,6 +2044,16 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
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);
}
}
@ -2450,7 +2461,7 @@ sp_head::show_create_routine(THD *thd, int type)
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);
@ -2636,8 +2647,8 @@ sp_head::show_routine_code(THD *thd)
field_list.push_back(new Item_uint("Pos", 9));
// 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 |
max(buffer.length(), 1024)));
if (protocol->send_result_set_metadata(&field_list, Protocol::SEND_NUM_ROWS |
Protocol::SEND_EOF))
DBUG_RETURN(1);
@ -2869,7 +2880,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp)
res= m_lex_keeper.reset_lex_and_exec_core(thd, nextp, FALSE, this);
if (thd->stmt_da->is_eof())
net_end_statement(thd);
thd->protocol->end_statement();
query_cache_end_of_result(thd);

View file

@ -4532,7 +4532,7 @@ bool mysql_show_grants(THD *thd,LEX_USER *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))
{
VOID(pthread_mutex_unlock(&acl_cache->lock));

View file

@ -21,6 +21,7 @@
#include "sp_head.h"
#include "sp.h"
#include "sql_trigger.h"
#include "sql_prepare.h"
#include <m_ctype.h>
#include <my_dir.h>
#include <hash.h>

View file

@ -1155,7 +1155,9 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
protocol (COM_EXECUTE) cannot be served to statements asking for results
in the text protocol (COM_QUERY) and vice-versa.
*/
flags.result_in_binary_protocol= (unsigned int) thd->protocol->type();
flags.protocol_type= (unsigned int) thd->protocol->type();
/* PROTOCOL_LOCAL results are not cached. */
DBUG_ASSERT(flags.protocol_type != (unsigned int) Protocol::PROTOCOL_LOCAL);
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
@ -1184,7 +1186,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.result_in_binary_protocol,
(int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,
@ -1439,7 +1441,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
flags.client_long_flag= test(thd->client_capabilities & CLIENT_LONG_FLAG);
flags.client_protocol_41= test(thd->client_capabilities &
CLIENT_PROTOCOL_41);
flags.result_in_binary_protocol= (unsigned int)thd->protocol->type();
flags.protocol_type= (unsigned int) thd->protocol->type();
flags.more_results_exists= test(thd->server_status &
SERVER_MORE_RESULTS_EXISTS);
flags.in_trans= test(thd->server_status & SERVER_STATUS_IN_TRANS);
@ -1466,7 +1468,7 @@ sql mode: 0x%lx, sort len: %lu, conncat len: %lu, div_precision: %lu, \
def_week_frmt: %lu, in_trans: %d, autocommit: %d",
(int)flags.client_long_flag,
(int)flags.client_protocol_41,
(int)flags.result_in_binary_protocol,
(int)flags.protocol_type,
(int)flags.more_results_exists,
flags.pkt_nr,
flags.character_set_client_num,

View file

@ -1493,8 +1493,8 @@ int THD::send_explain_fields(select_result *result)
}
item->maybe_null= 1;
field_list.push_back(new Item_empty_string("Extra", 255, cs));
return (result->send_fields(field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
return (result->send_result_set_metadata(field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF));
}
#ifdef SIGNAL_WITH_VIO_CLOSE
@ -1618,10 +1618,10 @@ bool sql_exchange::escaped_given(void)
}
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;
}
@ -1662,10 +1662,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);
}
/*
@ -1675,36 +1678,18 @@ bool select_send::send_data(List<Item> &items)
*/
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))
{
protocol->free(); // Free used buffer
my_message(ER_OUT_OF_RESOURCES, ER(ER_OUT_OF_RESOURCES), MYF(0));
break;
}
/*
Reset buffer to its original state, as it may have been altered in
Item::send().
*/
buffer.set(buff, sizeof(buff), &my_charset_bin);
}
thd->sent_row_count++;
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);
}

View file

@ -23,52 +23,8 @@
#include "log.h"
#include "rpl_tblmap.h"
/**
An interface that is used to take an action when
the locking module notices that a table version has changed
since the last execution. "Table" here may refer to any kind of
table -- a base table, a temporary table, a view or an
information schema table.
When we open and lock tables for execution of a prepared
statement, we must verify that they did not change
since statement prepare. If some table did change, the statement
parse tree *may* be no longer valid, e.g. in case it contains
optimizations that depend on table metadata.
This class provides an interface (a method) that is
invoked when such a situation takes place.
The implementation of the method simply reports an error, but
the exact details depend on the nature of the SQL statement.
At most 1 instance of this class is active at a time, in which
case THD::m_reprepare_observer is not NULL.
@sa check_and_update_table_version() for details of the
version tracking algorithm
@sa Open_tables_state::m_reprepare_observer for the life cycle
of metadata observers.
*/
class Reprepare_observer
{
public:
Reprepare_observer() {}
/**
Check if a change of metadata is OK. In future
the signature of this method may be extended to accept the old
and the new versions, but since currently the check is very
simple, we only need the THD to report an error.
*/
bool report_error(THD *thd);
bool is_invalidated() const { return m_invalidated; }
void reset_reprepare_observer() { m_invalidated= FALSE; }
private:
bool m_invalidated;
};
class Reprepare_observer;
class Relay_log_info;
class Query_log_event;
@ -2441,7 +2397,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);
@ -2486,7 +2442,7 @@ class select_result_interceptor: public select_result
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; }
};
@ -2499,7 +2455,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; }

View file

@ -957,7 +957,7 @@ static bool login_connection(THD *thd)
my_net_set_write_timeout(net, connect_timeout);
error= check_connection(thd);
net_end_statement(thd);
thd->protocol->end_statement();
if (error)
{ // Wrong permissions

View file

@ -90,7 +90,7 @@ class Materialized_cursor: public Server_side_cursor
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);
@ -115,7 +115,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);
};
@ -376,12 +376,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;
@ -566,14 +566,14 @@ Materialized_cursor::Materialized_cursor(select_result *result_arg,
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;
@ -583,7 +583,7 @@ int Materialized_cursor::fill_item_list(THD *thd, List<Item> &send_fields)
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,
@ -623,17 +623,17 @@ int Materialized_cursor::open(JOIN *join __attribute__((unused)))
{
/*
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;
@ -717,7 +717,7 @@ Materialized_cursor::~Materialized_cursor()
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(),

View file

@ -672,7 +672,7 @@ bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
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);

View file

@ -548,7 +548,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
@ -670,18 +670,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++;

View file

@ -431,7 +431,7 @@ int send_answer_1(Protocol *protocol, String *s1, String *s2, String *s3)
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, String *s1, String *s2, String *s3)
+- -+
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, bool for_category)
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));
}

View file

@ -35,6 +35,7 @@ sys_var *trg_new_row_fake_var= (sys_var*) 0x01;
LEX_STRING constant for null-string to be used in parser and other places.
*/
const LEX_STRING null_lex_str= {NULL, 0};
const LEX_STRING empty_lex_str= { (char*) "", 0 };
/* Longest standard keyword name */
@ -140,6 +141,7 @@ Lex_input_stream::Lex_input_stream(THD *thd,
found_semicolon(NULL),
ignore_space(test(thd->variables.sql_mode & MODE_IGNORE_SPACE)),
stmt_prepare_mode(FALSE),
multi_statements(TRUE),
in_comment(NO_COMMENT),
m_underscore_cs(NULL)
{

View file

@ -952,6 +952,7 @@ enum xa_option_words {XA_NONE, XA_JOIN, XA_RESUME, XA_ONE_PHASE,
XA_SUSPEND, XA_FOR_MIGRATE};
extern const LEX_STRING null_lex_str;
extern const LEX_STRING empty_lex_str;
/*
@ -1489,9 +1490,13 @@ public:
/**
TRUE if we're parsing a prepared statement: in this mode
we should allow placeholders and disallow multi-statements.
we should allow placeholders.
*/
bool stmt_prepare_mode;
/**
TRUE if we should allow multi-statements.
*/
bool multi_statements;
/** State of the lexical analyser for comments. */
enum_comment_state in_comment;

View file

@ -18,7 +18,7 @@
#pragma implementation // gcc: Class implementation
#endif
#include "mysql_priv.h"
#include "sql_list.h"
list_node end_of_list;

View file

@ -15,11 +15,16 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "my_global.h"
#include "my_sys.h"
#ifdef USE_PRAGMA_INTERFACE
#pragma interface /* gcc class implementation */
#endif
void *sql_alloc(size_t);
/* mysql standard class memory allocator */
class Sql_alloc

View file

@ -27,6 +27,7 @@
#include "sp_cache.h"
#include "events.h"
#include "sql_trigger.h"
#include "sql_prepare.h"
#include "probes_mysql.h"
/**
@ -453,7 +454,7 @@ static void handle_bootstrap_impl(THD *thd)
/* purecov: begin tested */
if (net_realloc(&(thd->net), 2 * thd->net.max_packet))
{
net_end_statement(thd);
thd->protocol->end_statement();
bootstrap_error= 1;
break;
}
@ -494,7 +495,7 @@ static void handle_bootstrap_impl(THD *thd)
close_thread_tables(thd); // Free tables
bootstrap_error= thd->is_error();
net_end_statement(thd);
thd->protocol->end_statement();
#if defined(ENABLED_PROFILING)
thd->profiling.finish_current_query();
@ -819,7 +820,7 @@ bool do_command(THD *thd)
/* The error must be set. */
DBUG_ASSERT(thd->is_error());
net_end_statement(thd);
thd->protocol->end_statement();
if (net->error != 3)
{
@ -1236,7 +1237,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
char *beginning_of_next_stmt= (char*) end_of_stmt;
net_end_statement(thd);
thd->protocol->end_statement();
query_cache_end_of_result(thd);
/*
Multiple queries exits, execute them individually
@ -1609,7 +1610,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thd->transaction.stmt.reset();
net_end_statement(thd);
thd->protocol->end_statement();
query_cache_end_of_result(thd);
thd->proc_info= "closing tables";

View file

@ -84,6 +84,7 @@ When one supplies long data for a placeholder:
*/
#include "mysql_priv.h"
#include "sql_prepare.h"
#include "sql_select.h" // for JOIN
#include "sql_cursor.h"
#include "sp_head.h"
@ -106,7 +107,7 @@ class Select_fetch_protocol_binary: public select_send
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
@ -163,6 +164,7 @@ public:
bool execute_loop(String *expanded_query,
bool open_cursor,
uchar *packet_arg, uchar *packet_end_arg);
bool execute_server_runnable(Server_runnable *server_runnable);
/* Destroy this statement */
void deallocate();
private:
@ -183,6 +185,78 @@ private:
void swap_prepared_statement(Prepared_statement *copy);
};
/**
Execute one SQL statement in an isolated context.
*/
class Execute_sql_statement: public Server_runnable
{
public:
Execute_sql_statement(LEX_STRING sql_text);
virtual bool execute_server_code(THD *thd);
private:
LEX_STRING m_sql_text;
};
class Ed_connection;
/**
Protocol_local: a helper class to intercept the result
of the data written to the network.
*/
class Protocol_local :public Protocol
{
public:
Protocol_local(THD *thd, Ed_connection *ed_connection);
~Protocol_local() { free_root(&m_rset_root, MYF(0)); }
protected:
virtual void prepare_for_resend();
virtual bool write();
virtual bool store_null();
virtual bool store_tiny(longlong from);
virtual bool store_short(longlong from);
virtual bool store_long(longlong from);
virtual bool store_longlong(longlong from, bool unsigned_flag);
virtual bool store_decimal(const my_decimal *);
virtual bool store(const char *from, size_t length, CHARSET_INFO *cs);
virtual bool store(const char *from, size_t length,
CHARSET_INFO *fromcs, CHARSET_INFO *tocs);
virtual bool store(MYSQL_TIME *time);
virtual bool store_date(MYSQL_TIME *time);
virtual bool store_time(MYSQL_TIME *time);
virtual bool store(float value, uint32 decimals, String *buffer);
virtual bool store(double value, uint32 decimals, String *buffer);
virtual bool store(Field *field);
virtual bool send_result_set_metadata(List<Item> *list, uint flags);
virtual bool send_out_parameters(List<Item_param> *sp_params);
#ifdef EMBEDDED_LIBRARY
void remove_last_row();
#endif
virtual enum enum_protocol_type type() { return PROTOCOL_LOCAL; };
virtual bool send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
const char *message);
virtual bool send_eof(uint server_status, uint statement_warn_count);
virtual bool send_error(uint sql_errno, const char *err_msg, const char* sqlstate);
private:
bool store_string(const char *str, size_t length,
CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs);
bool store_column(const void *data, size_t length);
void opt_add_row_to_rset();
private:
Ed_connection *m_connection;
MEM_ROOT m_rset_root;
List<Ed_row> *m_rset;
size_t m_column_count;
Ed_column *m_current_row;
Ed_column *m_current_column;
};
/******************************************************************************
Implementation
@ -260,7 +334,7 @@ static bool send_prep_stmt(Prepared_statement *stmt, uint columns)
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);
}
@ -1374,7 +1448,7 @@ static int mysql_test_select(Prepared_statement *stmt,
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);
@ -1920,30 +1994,6 @@ static bool check_prepared_statement(Prepared_statement *stmt)
Note that we don't need to have cases in this list if they are
marked with CF_STATUS_COMMAND in sql_command_flags
*/
case SQLCOM_SHOW_PROCESSLIST:
case SQLCOM_SHOW_STORAGE_ENGINES:
case SQLCOM_SHOW_PRIVILEGES:
case SQLCOM_SHOW_COLUMN_TYPES:
case SQLCOM_SHOW_ENGINE_LOGS:
case SQLCOM_SHOW_ENGINE_STATUS:
case SQLCOM_SHOW_ENGINE_MUTEX:
case SQLCOM_SHOW_CREATE_DB:
case SQLCOM_SHOW_GRANTS:
case SQLCOM_SHOW_BINLOG_EVENTS:
case SQLCOM_SHOW_MASTER_STAT:
case SQLCOM_SHOW_SLAVE_STAT:
case SQLCOM_SHOW_CREATE_PROC:
case SQLCOM_SHOW_CREATE_FUNC:
case SQLCOM_SHOW_CREATE_EVENT:
case SQLCOM_SHOW_CREATE_TRIGGER:
case SQLCOM_SHOW_CREATE:
case SQLCOM_SHOW_PROC_CODE:
case SQLCOM_SHOW_FUNC_CODE:
case SQLCOM_SHOW_AUTHORS:
case SQLCOM_SHOW_CONTRIBUTORS:
case SQLCOM_SHOW_WARNS:
case SQLCOM_SHOW_ERRORS:
case SQLCOM_SHOW_BINLOGS:
case SQLCOM_DROP_TABLE:
case SQLCOM_RENAME_TABLE:
case SQLCOM_ALTER_TABLE:
@ -2066,7 +2116,6 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{
Protocol *save_protocol= thd->protocol;
Prepared_statement *stmt;
bool error;
DBUG_ENTER("mysqld_stmt_prepare");
DBUG_PRINT("prep_query", ("%s", packet));
@ -2091,15 +2140,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
thd->protocol= &thd->protocol_binary;
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
error= stmt->prepare(packet, packet_length);
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
if (error)
if (stmt->prepare(packet, packet_length))
{
/* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt);
@ -2478,7 +2519,6 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
DBUG_VOID_RETURN;
}
@ -2787,19 +2827,19 @@ Select_fetch_protocol_binary::Select_fetch_protocol_binary(THD *thd_arg)
: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;
@ -2848,6 +2888,70 @@ Reprepare_observer::report_error(THD *thd)
}
/*******************************************************************
* Server_runnable
*******************************************************************/
Server_runnable::~Server_runnable()
{
}
///////////////////////////////////////////////////////////////////////////
Execute_sql_statement::
Execute_sql_statement(LEX_STRING sql_text)
:m_sql_text(sql_text)
{}
/**
Parse and execute a statement. Does not prepare the query.
Allows to execute a statement from within another statement.
The main property of the implementation is that it does not
affect the environment -- i.e. you can run many
executions without having to cleanup/reset THD in between.
*/
bool
Execute_sql_statement::execute_server_code(THD *thd)
{
bool error;
if (alloc_query(thd, m_sql_text.str, m_sql_text.length))
return TRUE;
Parser_state parser_state(thd, thd->query, thd->query_length);
parser_state.m_lip.multi_statements= FALSE;
lex_start(thd);
error= parse_sql(thd, &parser_state, NULL) || thd->is_error();
if (error)
goto end;
thd->lex->set_trg_event_type_for_tables();
error= mysql_execute_command(thd);
if (thd->killed_errno())
{
if (! thd->stmt_da->is_set())
thd->send_kill_message();
}
/* report error issued during command execution */
if (error == 0 && thd->spcont == NULL)
general_log_write(thd, COM_STMT_EXECUTE,
thd->query, thd->query_length);
end:
lex_end(thd->lex);
return error;
}
/***************************************************************************
Prepared_statement
****************************************************************************/
@ -2945,7 +3049,8 @@ void Prepared_statement::cleanup_stmt()
DBUG_ENTER("Prepared_statement::cleanup_stmt");
DBUG_PRINT("enter",("stmt: 0x%lx", (long) this));
DBUG_ASSERT(lex->sphead == 0);
delete lex->sphead;
lex->sphead= 0;
/* The order is important */
lex->unit.cleanup();
cleanup_items(free_list);
@ -3058,6 +3163,7 @@ bool Prepared_statement::prepare(const char *packet, uint packet_len)
Parser_state parser_state(thd, thd->query, thd->query_length);
parser_state.m_lip.stmt_prepare_mode= TRUE;
parser_state.m_lip.multi_statements= FALSE;
lex_start(thd);
error= parse_sql(thd, & parser_state, NULL) ||
@ -3302,6 +3408,45 @@ reexecute:
}
bool
Prepared_statement::execute_server_runnable(Server_runnable *server_runnable)
{
Statement stmt_backup;
bool error;
Query_arena *save_stmt_arena= thd->stmt_arena;
Item_change_list save_change_list;
thd->change_list= save_change_list;
state= CONVENTIONAL_EXECUTION;
if (!(lex= new (mem_root) st_lex_local))
return TRUE;
thd->set_n_backup_statement(this, &stmt_backup);
thd->set_n_backup_active_arena(this, &stmt_backup);
thd->stmt_arena= this;
error= server_runnable->execute_server_code(thd);
delete lex->sphead;
lex->sphead= 0;
/* The order is important */
lex->unit.cleanup();
close_thread_tables(thd);
thd->cleanup_after_query();
thd->restore_active_arena(this, &stmt_backup);
thd->restore_backup_statement(this, &stmt_backup);
thd->stmt_arena= save_stmt_arena;
save_change_list= thd->change_list;
/* Items and memory will freed in destructor */
return error;
}
/**
Reprepare this prepared statement.
@ -3637,6 +3782,15 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (state == Query_arena::PREPARED)
state= Query_arena::EXECUTED;
if (this->lex->sql_command == SQLCOM_CALL)
{
if (is_sql_prepare())
thd->protocol_text.send_out_parameters(&this->lex->param_list);
else
thd->protocol->send_out_parameters(&this->lex->param_list);
}
/*
Log COM_EXECUTE to the general log. Note, that in case of SQL
prepared statements this causes two records to be output:
@ -3670,3 +3824,573 @@ void Prepared_statement::deallocate()
/* Statement map calls delete stmt on erase */
thd->stmt_map.erase(this);
}
/***************************************************************************
* Ed_result_set
***************************************************************************/
/**
Use operator delete to free memory of Ed_result_set.
Accessing members of a class after the class has been destroyed
is a violation of the C++ standard but is commonly used in the
server code.
*/
void Ed_result_set::operator delete(void *ptr, size_t size) throw ()
{
if (ptr)
{
/*
Make a stack copy, otherwise free_root() will attempt to
write to freed memory.
*/
MEM_ROOT own_root= ((Ed_result_set*) ptr)->m_mem_root;
free_root(&own_root, MYF(0));
}
}
/**
Initialize an instance of Ed_result_set.
Instances of the class, as well as all result set rows, are
always allocated in the memory root passed over as the second
argument. In the constructor, we take over ownership of the
memory root. It will be freed when the class is destroyed.
sic: Ed_result_est is not designed to be allocated on stack.
*/
Ed_result_set::Ed_result_set(List<Ed_row> *rows_arg,
size_t column_count_arg,
MEM_ROOT *mem_root_arg)
:m_mem_root(*mem_root_arg),
m_column_count(column_count_arg),
m_rows(rows_arg),
m_next_rset(NULL)
{
/* Take over responsibility for the memory */
clear_alloc_root(mem_root_arg);
}
/***************************************************************************
* Ed_result_set
***************************************************************************/
/**
Create a new "execute direct" connection.
*/
Ed_connection::Ed_connection(THD *thd)
:m_warning_info(thd->query_id),
m_thd(thd),
m_rsets(0),
m_current_rset(0)
{
}
/**
Free all result sets of the previous statement, if any,
and reset warnings and errors.
Called before execution of the next query.
*/
void
Ed_connection::free_old_result()
{
while (m_rsets)
{
Ed_result_set *rset= m_rsets->m_next_rset;
delete m_rsets;
m_rsets= rset;
}
m_current_rset= m_rsets;
m_diagnostics_area.reset_diagnostics_area();
m_warning_info.clear_warning_info(m_thd->query_id);
}
/**
A simple wrapper that uses a helper class to execute SQL statements.
*/
bool
Ed_connection::execute_direct(LEX_STRING sql_text)
{
Execute_sql_statement execute_sql_statement(sql_text);
DBUG_PRINT("ed_query", ("%s", sql_text.str));
return execute_direct(&execute_sql_statement);
}
/**
Execute a fragment of server functionality without an effect on
thd, and store results in memory.
Conventions:
- the code fragment must finish with OK, EOF or ERROR.
- the code fragment doesn't have to close thread tables,
free memory, commit statement transaction or do any other
cleanup that is normally done in the end of dispatch_command().
@param server_runnable A code fragment to execute.
*/
bool Ed_connection::execute_direct(Server_runnable *server_runnable)
{
bool rc= FALSE;
Protocol_local protocol_local(m_thd, this);
Prepared_statement stmt(m_thd);
Protocol *save_protocol= m_thd->protocol;
Diagnostics_area *save_diagnostics_area= m_thd->stmt_da;
Warning_info *save_warning_info= m_thd->warning_info;
DBUG_ENTER("Ed_connection::execute_direct");
free_old_result(); /* Delete all data from previous execution, if any */
m_thd->protocol= &protocol_local;
m_thd->stmt_da= &m_diagnostics_area;
m_thd->warning_info= &m_warning_info;
rc= stmt.execute_server_runnable(server_runnable);
m_thd->protocol->end_statement();
m_thd->protocol= save_protocol;
m_thd->stmt_da= save_diagnostics_area;
m_thd->warning_info= save_warning_info;
/*
Protocol_local makes use of m_current_rset to keep
track of the last result set, while adding result sets to the end.
Reset it to point to the first result set instead.
*/
m_current_rset= m_rsets;
DBUG_RETURN(rc);
}
/**
A helper method that is called only during execution.
Although Ed_connection doesn't support multi-statements,
a statement may generate many result sets. All subsequent
result sets are appended to the end.
@pre This is called only by Protocol_local.
*/
void
Ed_connection::add_result_set(Ed_result_set *ed_result_set)
{
if (m_rsets)
{
m_current_rset->m_next_rset= ed_result_set;
/* While appending, use m_current_rset as a pointer to the tail. */
m_current_rset= ed_result_set;
}
else
m_current_rset= m_rsets= ed_result_set;
}
/**
Release ownership of the current result set to the client.
Since we use a simple linked list for result sets,
this method uses a linear search of the previous result
set to exclude the released instance from the list.
@todo Use double-linked list, when this is really used.
XXX: This has never been tested with more than one result set!
@pre There must be a result set.
*/
Ed_result_set *
Ed_connection::store_result_set()
{
Ed_result_set *ed_result_set;
DBUG_ASSERT(m_current_rset);
if (m_current_rset == m_rsets)
{
/* Assign the return value */
ed_result_set= m_current_rset;
/* Exclude the return value from the list. */
m_current_rset= m_rsets= m_rsets->m_next_rset;
}
else
{
Ed_result_set *prev_rset= m_rsets;
/* Assign the return value. */
ed_result_set= m_current_rset;
/* Exclude the return value from the list */
while (prev_rset->m_next_rset != m_current_rset)
prev_rset= ed_result_set->m_next_rset;
m_current_rset= prev_rset->m_next_rset= m_current_rset->m_next_rset;
}
ed_result_set->m_next_rset= NULL; /* safety */
return ed_result_set;
}
/*************************************************************************
* Protocol_local
**************************************************************************/
Protocol_local::Protocol_local(THD *thd, Ed_connection *ed_connection)
:Protocol(thd),
m_connection(ed_connection),
m_rset(NULL),
m_column_count(0),
m_current_row(NULL),
m_current_column(NULL)
{
clear_alloc_root(&m_rset_root);
}
/**
Called between two result set rows.
Prepare structures to fill result set rows.
Unfortunately, we can't return an error here. If memory allocation
fails, we'll have to return an error later. And so is done
in methods such as @sa store_column().
*/
void Protocol_local::prepare_for_resend()
{
DBUG_ASSERT(alloc_root_inited(&m_rset_root));
opt_add_row_to_rset();
/* Start a new row. */
m_current_row= (Ed_column *) alloc_root(&m_rset_root,
sizeof(Ed_column) * m_column_count);
m_current_column= m_current_row;
}
/**
In "real" protocols this is called to finish a result set row.
Unused in the local implementation.
*/
bool Protocol_local::write()
{
return FALSE;
}
/**
A helper function to add the current row to the current result
set. Called in @sa prepare_for_resend(), when a new row is started,
and in send_eof(), when the result set is finished.
*/
void Protocol_local::opt_add_row_to_rset()
{
if (m_current_row)
{
/* Add the old row to the result set */
Ed_row *ed_row= new (&m_rset_root) Ed_row(m_current_row, m_column_count);
if (ed_row)
m_rset->push_back(ed_row, &m_rset_root);
}
}
/**
Add a NULL column to the current row.
*/
bool Protocol_local::store_null()
{
if (m_current_column == NULL)
return TRUE; /* prepare_for_resend() failed to allocate memory. */
bzero(m_current_column, sizeof(*m_current_column));
++m_current_column;
return FALSE;
}
/**
A helper method to add any column to the current row
in its binary form.
Allocates memory for the data in the result set memory root.
*/
bool Protocol_local::store_column(const void *data, size_t length)
{
if (m_current_column == NULL)
return TRUE; /* prepare_for_resend() failed to allocate memory. */
/*
alloc_root() automatically aligns memory, so we don't need to
do any extra alignment if we're pointing to, say, an integer.
*/
m_current_column->str= (char*) memdup_root(&m_rset_root,
data,
length + 1 /* Safety */);
if (! m_current_column->str)
return TRUE;
m_current_column->str[length]= '\0'; /* Safety */
m_current_column->length= length;
++m_current_column;
return FALSE;
}
/**
Store a string value in a result set column, optionally
having converted it to character_set_results.
*/
bool
Protocol_local::store_string(const char *str, size_t length,
CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
{
/* Store with conversion */
uint error_unused;
if (dst_cs && !my_charset_same(src_cs, dst_cs) &&
src_cs != &my_charset_bin &&
dst_cs != &my_charset_bin)
{
if (convert->copy(str, length, src_cs, dst_cs, &error_unused))
return TRUE;
str= convert->ptr();
length= convert->length();
}
return store_column(str, length);
}
/** Store a tiny int as is (1 byte) in a result set column. */
bool Protocol_local::store_tiny(longlong value)
{
char v= (char) value;
return store_column(&v, 1);
}
/** Store a short as is (2 bytes, host order) in a result set column. */
bool Protocol_local::store_short(longlong value)
{
int16 v= (int16) value;
return store_column(&v, 2);
}
/** Store a "long" as is (4 bytes, host order) in a result set column. */
bool Protocol_local::store_long(longlong value)
{
int32 v= (int32) value;
return store_column(&v, 4);
}
/** Store a "longlong" as is (8 bytes, host order) in a result set column. */
bool Protocol_local::store_longlong(longlong value, bool unsigned_flag)
{
int64 v= (int64) value;
return store_column(&v, 8);
}
/** Store a decimal in string format in a result set column */
bool Protocol_local::store_decimal(const my_decimal *value)
{
char buf[DECIMAL_MAX_STR_LENGTH];
String str(buf, sizeof (buf), &my_charset_bin);
int rc;
rc= my_decimal2string(E_DEC_FATAL_ERROR, value, 0, 0, 0, &str);
if (rc)
return TRUE;
return store_column(str.ptr(), str.length());
}
/** Convert to cs_results and store a string. */
bool Protocol_local::store(const char *str, size_t length,
CHARSET_INFO *src_cs)
{
CHARSET_INFO *dst_cs;
dst_cs= m_connection->m_thd->variables.character_set_results;
return store_string(str, length, src_cs, dst_cs);
}
/** Store a string. */
bool Protocol_local::store(const char *str, size_t length,
CHARSET_INFO *src_cs, CHARSET_INFO *dst_cs)
{
return store_string(str, length, src_cs, dst_cs);
}
/* Store MYSQL_TIME (in binary format) */
bool Protocol_local::store(MYSQL_TIME *time)
{
return store_column(time, sizeof(MYSQL_TIME));
}
/** Store MYSQL_TIME (in binary format) */
bool Protocol_local::store_date(MYSQL_TIME *time)
{
return store_column(time, sizeof(MYSQL_TIME));
}
/** Store MYSQL_TIME (in binary format) */
bool Protocol_local::store_time(MYSQL_TIME *time)
{
return store_column(time, sizeof(MYSQL_TIME));
}
/* Store a floating point number, as is. */
bool Protocol_local::store(float value, uint32 decimals, String *buffer)
{
return store_column(&value, sizeof(float));
}
/* Store a double precision number, as is. */
bool Protocol_local::store(double value, uint32 decimals, String *buffer)
{
return store_column(&value, sizeof (double));
}
/* Store a Field. */
bool Protocol_local::store(Field *field)
{
if (field->is_null())
return store_null();
return field->send_binary(this);
}
/** Called to start a new result set. */
bool Protocol_local::send_result_set_metadata(List<Item> *columns, uint)
{
DBUG_ASSERT(m_rset == 0 && !alloc_root_inited(&m_rset_root));
init_sql_alloc(&m_rset_root, MEM_ROOT_BLOCK_SIZE, 0);
if (! (m_rset= new (&m_rset_root) List<Ed_row>))
return TRUE;
m_column_count= columns->elements;
return FALSE;
}
/**
Normally this is a separate result set with OUT parameters
of stored procedures. Currently unsupported for the local
version.
*/
bool Protocol_local::send_out_parameters(List<Item_param> *sp_params)
{
return FALSE;
}
/** Called for statements that don't have a result set, at statement end. */
bool
Protocol_local::send_ok(uint server_status, uint statement_warn_count,
ulonglong affected_rows, ulonglong last_insert_id,
const char *message)
{
/*
Just make sure nothing is sent to the client, we have grabbed
the status information in the connection diagnostics area.
*/
return FALSE;
}
/**
Called at the end of a result set. Append a complete
result set to the list in Ed_connection.
Don't send anything to the client, but instead finish
building of the result set at hand.
*/
bool Protocol_local::send_eof(uint server_status, uint statement_warn_count)
{
Ed_result_set *ed_result_set;
DBUG_ASSERT(m_rset);
opt_add_row_to_rset();
m_current_row= 0;
ed_result_set= new (&m_rset_root) Ed_result_set(m_rset, m_column_count,
&m_rset_root);
m_rset= NULL;
if (! ed_result_set)
return TRUE;
/* In case of successful allocation memory ownership was transferred. */
DBUG_ASSERT(!alloc_root_inited(&m_rset_root));
/*
Link the created Ed_result_set instance into the list of connection
result sets. Never fails.
*/
m_connection->add_result_set(ed_result_set);
return FALSE;
}
/** Called to send an error to the client at the end of a statement. */
bool
Protocol_local::send_error(uint sql_errno, const char *err_msg, const char*)
{
/*
Just make sure that nothing is sent to the client (default
implementation).
*/
return FALSE;
}
#ifdef EMBEDDED_LIBRARY
void Protocol_local::remove_last_row()
{ }
#endif

367
sql/sql_prepare.h Normal file
View file

@ -0,0 +1,367 @@
#ifndef SQL_PREPARE_H
#define SQL_PREPARE_H
/* Copyright (C) 1995-2008 MySQL AB
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
#include "sql_error.h"
class THD;
struct LEX;
/**
An interface that is used to take an action when
the locking module notices that a table version has changed
since the last execution. "Table" here may refer to any kind of
table -- a base table, a temporary table, a view or an
information schema table.
When we open and lock tables for execution of a prepared
statement, we must verify that they did not change
since statement prepare. If some table did change, the statement
parse tree *may* be no longer valid, e.g. in case it contains
optimizations that depend on table metadata.
This class provides an interface (a method) that is
invoked when such a situation takes place.
The implementation of the method simply reports an error, but
the exact details depend on the nature of the SQL statement.
At most 1 instance of this class is active at a time, in which
case THD::m_reprepare_observer is not NULL.
@sa check_and_update_table_version() for details of the
version tracking algorithm
@sa Open_tables_state::m_reprepare_observer for the life cycle
of metadata observers.
*/
class Reprepare_observer
{
public:
/**
Check if a change of metadata is OK. In future
the signature of this method may be extended to accept the old
and the new versions, but since currently the check is very
simple, we only need the THD to report an error.
*/
bool report_error(THD *thd);
bool is_invalidated() const { return m_invalidated; }
void reset_reprepare_observer() { m_invalidated= FALSE; }
private:
bool m_invalidated;
};
void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length);
void mysqld_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysqld_stmt_close(THD *thd, char *packet);
void mysql_sql_stmt_prepare(THD *thd);
void mysql_sql_stmt_execute(THD *thd);
void mysql_sql_stmt_close(THD *thd);
void mysqld_stmt_fetch(THD *thd, char *packet, uint packet_length);
void mysqld_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
void reinit_stmt_before_use(THD *thd, LEX *lex);
/**
Execute a fragment of server code in an isolated context, so that
it doesn't leave any effect on THD. THD must have no open tables.
The code must not leave any open tables around.
The result of execution (if any) is stored in Ed_result.
*/
class Server_runnable
{
public:
virtual bool execute_server_code(THD *thd)= 0;
virtual ~Server_runnable();
};
/**
Execute direct interface.
@todo Implement support for prelocked mode.
*/
class Ed_row;
/**
Ed_result_set -- a container with result set rows.
@todo Implement support for result set metadata and
automatic type conversion.
*/
class Ed_result_set: public Sql_alloc
{
public:
operator List<Ed_row>&() { return *m_rows; }
unsigned int size() const { return m_rows->elements; }
Ed_result_set(List<Ed_row> *rows_arg, size_t column_count,
MEM_ROOT *mem_root_arg);
/** We don't call member destructors, they all are POD types. */
~Ed_result_set() {}
size_t get_field_count() const { return m_column_count; }
static void operator delete(void *ptr, size_t size) throw ();
private:
Ed_result_set(const Ed_result_set &); /* not implemented */
Ed_result_set &operator=(Ed_result_set &); /* not implemented */
private:
MEM_ROOT m_mem_root;
size_t m_column_count;
List<Ed_row> *m_rows;
Ed_result_set *m_next_rset;
friend class Ed_connection;
};
class Ed_connection
{
public:
/**
Construct a new "execute direct" connection.
The connection can be used to execute SQL statements.
If the connection failed to initialize, the error
will be returned on the attempt to execute a statement.
@pre thd must have no open tables
while the connection is used. However,
Ed_connection works okay in LOCK TABLES mode.
Other properties of THD, such as the current warning
information, errors, etc. do not matter and are
preserved by Ed_connection. One thread may have many
Ed_connections created for it.
*/
Ed_connection(THD *thd);
/**
Execute one SQL statement.
Until this method is executed, no other methods of
Ed_connection can be used. Life cycle of Ed_connection is:
Initialized -> a statement has been executed ->
look at result, move to next result ->
look at result, move to next result ->
...
moved beyond the last result == Initialized.
This method can be called repeatedly. Once it's invoked,
results of the previous execution are lost.
A result of execute_direct() can be either:
- success, no result set rows. In this case get_field_count()
returns 0. This happens after execution of INSERT, UPDATE,
DELETE, DROP and similar statements. Some other methods, such
as get_affected_rows() can be used to retrieve additional
result information.
- success, there are some result set rows (maybe 0). E.g.
happens after SELECT. In this case get_field_count() returns
the number of columns in a result set and store_result()
can be used to retrieve a result set..
- an error, methods to retrieve error information can
be used.
@return execution status
@retval FALSE success, use get_field_count()
to determine what to do next.
@retval TRUE error, use get_last_error()
to see the error number.
*/
bool execute_direct(LEX_STRING sql_text);
/**
Same as the previous, but takes an instance of Server_runnable
instead of SQL statement text.
@return execution status
@retval FALSE success, use get_field_count()
if your code fragment is supposed to
return a result set
@retval TRUE failure
*/
bool execute_direct(Server_runnable *server_runnable);
/**
Get the number of result set fields.
This method is valid only if we have a result:
execute_direct() has been called. Otherwise
the returned value is undefined.
@sa Documentation for C API function
mysql_field_count()
*/
ulong get_field_count() const
{
return m_current_rset ? m_current_rset->get_field_count() : 0;
}
/**
Get the number of affected (deleted, updated)
rows for the current statement. Can be
used for statements with get_field_count() == 0.
@sa Documentation for C API function
mysql_affected_rows().
*/
ulonglong get_affected_rows() const
{
return m_diagnostics_area.affected_rows();
}
/**
Get the last insert id, if any.
@sa Documentation for mysql_insert_id().
*/
ulonglong get_last_insert_id() const
{
return m_diagnostics_area.last_insert_id();
}
/**
Get the total number of warnings for the last executed
statement. Note, that there is only one warning list even
if a statement returns multiple results.
@sa Documentation for C API function
mysql_num_warnings().
*/
ulong get_warn_count() const
{
return m_warning_info.warn_count();
}
/**
Get the server warnings as a result set.
The result set has fixed metadata:
The first column is the level.
The second is a numeric code.
The third is warning text.
*/
List<MYSQL_ERROR> *get_warn_list() { return &m_warning_info.warn_list(); }
/**
The following members are only valid if execute_direct()
or move_to_next_result() returned an error.
They never fail, but if they are called when there is no
result, or no error, the result is not defined.
*/
const char *get_last_error() const { return m_diagnostics_area.message(); }
unsigned int get_last_errno() const { return m_diagnostics_area.sql_errno(); }
const char *get_last_sqlstate() const { return m_diagnostics_area.get_sqlstate(); }
/**
Provided get_field_count() is not 0, this never fails. You don't
need to free the result set, this is done automatically when
you advance to the next result set or destroy the connection.
Not returning const because of List iterator not accepting
Should be used when you would like Ed_connection to manage
result set memory for you.
*/
Ed_result_set *use_result_set() { return m_current_rset; }
/**
Provided get_field_count() is not 0, this never fails. You
must free the returned result set. This can be called only
once after execute_direct().
Should be used when you would like to get the results
and destroy the connection.
*/
Ed_result_set *store_result_set();
/**
If the query returns multiple results, this method
can be checked if there is another result beyond the next
one.
Never fails.
*/
bool has_next_result() const { return test(m_current_rset->m_next_rset); }
/**
Only valid to call if has_next_result() returned true.
Otherwise the result is undefined.
*/
bool move_to_next_result()
{
m_current_rset= m_current_rset->m_next_rset;
return test(m_current_rset);
}
~Ed_connection() { free_old_result(); }
private:
Diagnostics_area m_diagnostics_area;
Warning_info m_warning_info;
/**
Execute direct interface does not support multi-statements, only
multi-results. So we never have a situation when we have
a mix of result sets and OK or error packets. We either
have a single result set, a single error, or a single OK,
or we have a series of result sets, followed by an OK or error.
*/
THD *m_thd;
Ed_result_set *m_rsets;
Ed_result_set *m_current_rset;
friend class Protocol_local;
private:
void free_old_result();
void add_result_set(Ed_result_set *ed_result_set);
private:
Ed_connection(const Ed_connection &); /* not implemented */
Ed_connection &operator=(Ed_connection &); /* not implemented */
};
/** One result set column. */
struct Ed_column: public LEX_STRING
{
/** Implementation note: destructor for this class is never called. */
};
/** One result set record. */
class Ed_row: public Sql_alloc
{
public:
const Ed_column &operator[](const unsigned int column_index) const
{
return *get_column(column_index);
}
const Ed_column *get_column(const unsigned int column_index) const
{
DBUG_ASSERT(column_index < size());
return m_column_array + column_index;
}
size_t size() const { return m_column_count; }
Ed_row(Ed_column *column_array_arg, size_t column_count_arg)
:m_column_array(column_array_arg),
m_column_count(column_count_arg)
{}
private:
Ed_column *m_column_array;
size_t m_column_count; /* TODO: change to point to metadata */
};
#endif // SQL_PREPARE_H

View file

@ -415,7 +415,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);

View file

@ -1404,7 +1404,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);
@ -1563,7 +1563,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();
@ -1615,7 +1615,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);

View file

@ -1680,8 +1680,8 @@ JOIN::exec()
(zero_result_cause?zero_result_cause:"No tables used"));
else
{
if (result->send_fields(*columns_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
if (result->send_result_set_metadata(*columns_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
{
DBUG_VOID_RETURN;
}
@ -2081,7 +2081,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 &&
@ -2221,7 +2221,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);
@ -7214,7 +7214,7 @@ return_zero_rows(JOIN *join, select_result *result,TABLE_LIST *tables,
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)))
{
bool send_error= FALSE;
@ -9736,7 +9736,7 @@ void setup_tmp_table_column_bitmaps(TABLE *table, uchar *bitmaps)
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
@ -14988,7 +14988,7 @@ test_if_group_changed(List<Cached_item> &list)
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
@ -15228,7 +15228,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
@ -15238,7 +15238,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);
@ -15260,7 +15260,7 @@ bool JOIN::make_sum_func_list(List<Item> &field_list, List<Item> &send_fields,
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)

View file

@ -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);
@ -403,8 +403,8 @@ bool mysqld_show_column_types(THD *thd)
field_list.push_back(new Item_empty_string("Default",NAME_CHAR_LEN));
field_list.push_back(new Item_empty_string("Comment",NAME_CHAR_LEN));
if (protocol->send_fields(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
if (protocol->send_result_set_metadata(&field_list,
Protocol::SEND_NUM_ROWS | Protocol::SEND_EOF))
DBUG_RETURN(TRUE);
/* TODO: Change the loop to not use 'i' */
@ -655,7 +655,7 @@ mysqld_show_create(THD *thd, TABLE_LIST *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();
@ -740,7 +740,7 @@ bool mysqld_show_create_db(THD *thd, char *dbname,
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);
@ -808,7 +808,7 @@ mysqld_list_fields(THD *thd, TABLE_LIST *table_list, const char *wild)
}
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;
@ -1698,7 +1698,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
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;
@ -6905,7 +6905,7 @@ static bool show_create_trigger_impl(THD *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. */

View file

@ -120,6 +120,11 @@ public:
(void) realloc(str_length);
return Ptr;
}
LEX_STRING lex_string() const
{
LEX_STRING lex_string = { (char*) ptr(), length() };
return lex_string;
}
void set(String &str,uint32 offset,uint32 arg_length)
{

View file

@ -4524,7 +4524,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
item->maybe_null = 1;
field_list.push_back(item = new Item_empty_string("Msg_text", 255));
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);
@ -7819,7 +7819,7 @@ bool mysql_checksum_table(THD *thd, TABLE_LIST *tables,
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);

View file

@ -922,7 +922,6 @@ bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list,
if ((duplicate= unique_table(thd, table_list, table_list->next_global, 0)))
{
update_non_unique_table_error(table_list, "UPDATE", duplicate);
my_error(ER_UPDATE_TABLE_USED, MYF(0), table_list->table_name);
DBUG_RETURN(TRUE);
}
}

View file

@ -1385,7 +1385,7 @@ query:
Lex_input_stream *lip = YYLIP;
if ((YYTHD->client_capabilities & CLIENT_MULTI_QUERIES) &&
! lip->stmt_prepare_mode &&
lip->multi_statements &&
! lip->eof())
{
/*

View file

@ -1516,6 +1516,568 @@ static void test_prepare_simple()
myquery(rc);
}
/************************************************************************/
#define FILE_PATH_SIZE 4096
char mct_log_file_path[FILE_PATH_SIZE];
FILE *mct_log_file= NULL;
void mct_start_logging(const char *test_case_name)
{
const char *tmp_dir= getenv("MYSQL_TMP_DIR");
if (!tmp_dir)
{
printf("Warning: MYSQL_TMP_DIR is not set. Logging is disabled.\n");
return;
}
if (mct_log_file)
{
printf("Warning: can not start logging for test case '%s' "
"because log is already open\n",
(const char *) test_case_name);
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 | O_BINARY, 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);
}
}
void mct_close_log()
{
if (!mct_log_file)
return;
my_fclose(mct_log_file, MYF(0));
mct_log_file= NULL;
}
#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_metadata= mysql_stmt_result_metadata(stmt);
num_fields= mysql_stmt_field_count(stmt);
fields= mysql_fetch_fields(rs_metadata);
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);
free(rs_bind);
mysql_free_result(rs_metadata);
if (rc > 0)
{
printf("Error: %s (errno: %d)\n",
mysql_stmt_error(stmt), mysql_stmt_errno(stmt));
DIE(rc > 0);
}
if (rc)
break;
if (!mysql->field_count)
{
/* This is the last OK-packet. No more resultsets. */
break;
}
}
}
mysql_stmt_close(stmt);
mct_close_log();
rc= mysql_commit(mysql);
myquery(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);
}
}
static void test_wl4435_2()
{
MYSQL_STMT *stmt;
int i;
int rc;
char query[MAX_TEST_QUERY_LENGTH];
myheader("test_wl4435_2");
mct_start_logging("test_wl4435_2");
/*
Do a few iterations so that we catch any problem with incorrect
handling/flushing prepared statement results.
*/
for (i= 0; i < 10; ++i)
{
/*
Prepare a procedure. That can be moved out of the loop, but it was
left in the loop for the sake of having as many statements as
possible.
*/
rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
myquery(rc);
rc= mysql_query(mysql,
"CREATE PROCEDURE p1()"
"BEGIN "
" SELECT 1; "
" SELECT 2, 3 UNION SELECT 4, 5; "
" SELECT 6, 7, 8; "
"END");
myquery(rc);
/* Invoke a procedure, that returns several result sets. */
strmov(query, "CALL p1()");
stmt= mysql_simple_prepare(mysql, query);
check_stmt(stmt);
/* Execute! */
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
/* Flush all the results. */
mysql_stmt_close(stmt);
/* Clean up. */
rc= mysql_commit(mysql);
myquery(rc);
rc= mysql_query(mysql, "DROP PROCEDURE p1");
myquery(rc);
}
}
/* Test simple prepare field results */
@ -14476,9 +15038,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);
@ -15846,7 +16407,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);
@ -18565,6 +19126,8 @@ static struct my_tests_st my_tests[]= {
{ "test_wl4166_2", test_wl4166_2 },
{ "test_wl4166_3", test_wl4166_3 },
{ "test_wl4166_4", test_wl4166_4 },
{ "test_wl4435", test_wl4435 },
{ "test_wl4435_2", test_wl4435_2 },
{ "test_bug38486", test_bug38486 },
{ "test_bug33831", test_bug33831 },
{ "test_bug40365", test_bug40365 },