mirror of
https://github.com/MariaDB/server.git
synced 2025-01-31 02:51:44 +01:00
Automerge from mysql-next-mr-runtime.
This commit is contained in:
commit
d18d2dc8dc
47 changed files with 2899 additions and 328 deletions
|
@ -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);
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
||||
###########################################################################
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
168
sql/item.cc
168
sql/item.cc
|
@ -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 */
|
||||
|
|
30
sql/item.h
30
sql/item.h
|
@ -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.
|
||||
*/
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
@ -1430,19 +1430,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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
280
sql/protocol.cc
280
sql/protocol.cc
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2451,7 +2462,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);
|
||||
|
@ -2637,8 +2648,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);
|
||||
|
||||
|
@ -2870,7 +2881,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);
|
||||
|
||||
|
|
|
@ -4641,7 +4641,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));
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#pragma implementation // gcc: Class implementation
|
||||
#endif
|
||||
|
||||
#include "mysql_priv.h"
|
||||
#include "sql_list.h"
|
||||
|
||||
list_node end_of_list;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "sp_cache.h"
|
||||
#include "events.h"
|
||||
#include "sql_trigger.h"
|
||||
#include "sql_prepare.h"
|
||||
#include "probes_mysql.h"
|
||||
|
||||
/**
|
||||
|
@ -452,7 +453,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;
|
||||
}
|
||||
|
@ -493,7 +494,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();
|
||||
|
@ -818,7 +819,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)
|
||||
{
|
||||
|
@ -1235,7 +1236,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
|
||||
|
@ -1608,7 +1609,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";
|
||||
|
|
|
@ -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);
|
||||
|
@ -1923,30 +1997,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:
|
||||
|
@ -2069,7 +2119,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));
|
||||
|
@ -2094,15 +2143,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);
|
||||
|
@ -2481,7 +2522,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;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -2790,19 +2830,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;
|
||||
|
@ -2851,6 +2891,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
|
||||
****************************************************************************/
|
||||
|
@ -2948,7 +3052,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);
|
||||
|
@ -3061,6 +3166,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) ||
|
||||
|
@ -3305,6 +3411,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.
|
||||
|
||||
|
@ -3640,6 +3785,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:
|
||||
|
@ -3673,3 +3827,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
367
sql/sql_prepare.h
Normal 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
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -6907,7 +6907,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. */
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -4530,7 +4530,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);
|
||||
|
||||
|
@ -7825,7 +7825,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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
{
|
||||
/*
|
||||
|
|
|
@ -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 },
|
||||
|
|
Loading…
Add table
Reference in a new issue