Commit c6b8dced authored by Konstantin Osipov's avatar Konstantin Osipov

A fix for Bug#44495 "Prepared Statement: CALL p(<x>) - `thd->protocol == &thd->protocol_text'

failed"

Do not assume that SQL prepared statements always run in text protocol.
When invoked from a stored procedure, which is itself invoked
by means of prepared CALL statement, the protocol may be binary.
Juggle with the protocol only when we want to change it
to binary in COM_STMT_EXECUTE, COM_STMT_PREPARE.

This is a backport from 5.4/6.0, where the bug was fixed
as part of WL#4264 "Backup: Stabilize Service Interface" 
parent 03793f4c
...@@ -127,12 +127,12 @@ class Prepared_statement: public Statement ...@@ -127,12 +127,12 @@ class Prepared_statement: public Statement
public: public:
enum flag_values enum flag_values
{ {
IS_IN_USE= 1 IS_IN_USE= 1,
IS_SQL_PREPARE= 2
}; };
THD *thd; THD *thd;
Select_fetch_protocol_binary result; Select_fetch_protocol_binary result;
Protocol *protocol;
Item_param **param_array; Item_param **param_array;
uint param_count; uint param_count;
uint last_errno; uint last_errno;
...@@ -148,7 +148,7 @@ class Prepared_statement: public Statement ...@@ -148,7 +148,7 @@ class Prepared_statement: public Statement
List<LEX_STRING>& varnames, List<LEX_STRING>& varnames,
String *expanded_query); String *expanded_query);
public: public:
Prepared_statement(THD *thd_arg, Protocol *protocol_arg); Prepared_statement(THD *thd_arg);
virtual ~Prepared_statement(); virtual ~Prepared_statement();
void setup_set_params(); void setup_set_params();
virtual Query_arena::Type type() const; virtual Query_arena::Type type() const;
...@@ -156,7 +156,8 @@ class Prepared_statement: public Statement ...@@ -156,7 +156,8 @@ class Prepared_statement: public Statement
bool set_name(LEX_STRING *name); bool set_name(LEX_STRING *name);
inline void close_cursor() { delete cursor; cursor= 0; } inline void close_cursor() { delete cursor; cursor= 0; }
inline bool is_in_use() { return flags & (uint) IS_IN_USE; } inline bool is_in_use() { return flags & (uint) IS_IN_USE; }
inline bool is_protocol_text() const { return protocol == &thd->protocol_text; } inline bool is_sql_prepare() const { return flags & (uint) IS_SQL_PREPARE; }
void set_sql_prepare() { flags|= (uint) IS_SQL_PREPARE; }
bool prepare(const char *packet, uint packet_length); bool prepare(const char *packet, uint packet_length);
bool execute_loop(String *expanded_query, bool execute_loop(String *expanded_query,
bool open_cursor, bool open_cursor,
...@@ -1358,7 +1359,7 @@ static int mysql_test_select(Prepared_statement *stmt, ...@@ -1358,7 +1359,7 @@ static int mysql_test_select(Prepared_statement *stmt,
*/ */
if (unit->prepare(thd, 0, 0)) if (unit->prepare(thd, 0, 0))
goto error; goto error;
if (!lex->describe && !stmt->is_protocol_text()) if (!lex->describe && !stmt->is_sql_prepare())
{ {
/* Make copy of item list, as change_columns may change it */ /* Make copy of item list, as change_columns may change it */
List<Item> fields(lex->select_lex.item_list); List<Item> fields(lex->select_lex.item_list);
...@@ -1988,7 +1989,7 @@ static bool check_prepared_statement(Prepared_statement *stmt) ...@@ -1988,7 +1989,7 @@ static bool check_prepared_statement(Prepared_statement *stmt)
break; break;
} }
if (res == 0) if (res == 0)
DBUG_RETURN(stmt->is_protocol_text() ? DBUG_RETURN(stmt->is_sql_prepare() ?
FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush())); FALSE : (send_prep_stmt(stmt, 0) || thd->protocol->flush()));
error: error:
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
...@@ -2058,6 +2059,7 @@ static bool init_param_array(Prepared_statement *stmt) ...@@ -2058,6 +2059,7 @@ static bool init_param_array(Prepared_statement *stmt)
void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
{ {
Protocol *save_protocol= thd->protocol;
Prepared_statement *stmt; Prepared_statement *stmt;
bool error; bool error;
DBUG_ENTER("mysqld_stmt_prepare"); DBUG_ENTER("mysqld_stmt_prepare");
...@@ -2067,7 +2069,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -2067,7 +2069,7 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* First of all clear possible warnings from the previous command */ /* First of all clear possible warnings from the previous command */
mysql_reset_thd_for_next_command(thd); mysql_reset_thd_for_next_command(thd);
if (! (stmt= new Prepared_statement(thd, &thd->protocol_binary))) if (! (stmt= new Prepared_statement(thd)))
DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */ DBUG_VOID_RETURN; /* out of memory: error is set in Sql_alloc */
if (thd->stmt_map.insert(thd, stmt)) if (thd->stmt_map.insert(thd, stmt))
...@@ -2084,6 +2086,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -2084,6 +2086,8 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
sp_cache_flush_obsolete(&thd->sp_proc_cache); sp_cache_flush_obsolete(&thd->sp_proc_cache);
sp_cache_flush_obsolete(&thd->sp_func_cache); sp_cache_flush_obsolete(&thd->sp_func_cache);
thd->protocol= &thd->protocol_binary;
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR); my_pthread_setprio(pthread_self(),QUERY_PRIOR);
...@@ -2097,6 +2101,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length) ...@@ -2097,6 +2101,9 @@ void mysqld_stmt_prepare(THD *thd, const char *packet, uint packet_length)
/* Statement map deletes statement on erase */ /* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
} }
thd->protocol= save_protocol;
/* check_prepared_statemnt sends the metadata packet in case of success */ /* check_prepared_statemnt sends the metadata packet in case of success */
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
...@@ -2229,7 +2236,6 @@ void mysql_sql_stmt_prepare(THD *thd) ...@@ -2229,7 +2236,6 @@ void mysql_sql_stmt_prepare(THD *thd)
const char *query; const char *query;
uint query_len= 0; uint query_len= 0;
DBUG_ENTER("mysql_sql_stmt_prepare"); DBUG_ENTER("mysql_sql_stmt_prepare");
DBUG_ASSERT(thd->protocol == &thd->protocol_text);
if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name))) if ((stmt= (Prepared_statement*) thd->stmt_map.find_by_name(name)))
{ {
...@@ -2247,11 +2253,13 @@ void mysql_sql_stmt_prepare(THD *thd) ...@@ -2247,11 +2253,13 @@ void mysql_sql_stmt_prepare(THD *thd)
} }
if (! (query= get_dynamic_sql_string(lex, &query_len)) || if (! (query= get_dynamic_sql_string(lex, &query_len)) ||
! (stmt= new Prepared_statement(thd, &thd->protocol_text))) ! (stmt= new Prepared_statement(thd)))
{ {
DBUG_VOID_RETURN; /* out of memory */ DBUG_VOID_RETURN; /* out of memory */
} }
stmt->set_sql_prepare();
/* Set the name first, insert should know that this statement has a name */ /* Set the name first, insert should know that this statement has a name */
if (stmt->set_name(name)) if (stmt->set_name(name))
{ {
...@@ -2431,6 +2439,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) ...@@ -2431,6 +2439,7 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
String expanded_query; String expanded_query;
uchar *packet_end= packet + packet_length; uchar *packet_end= packet + packet_length;
Prepared_statement *stmt; Prepared_statement *stmt;
Protocol *save_protocol= thd->protocol;
bool open_cursor; bool open_cursor;
DBUG_ENTER("mysqld_stmt_execute"); DBUG_ENTER("mysqld_stmt_execute");
...@@ -2458,7 +2467,9 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length) ...@@ -2458,7 +2467,9 @@ void mysqld_stmt_execute(THD *thd, char *packet_arg, uint packet_length)
open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY); open_cursor= test(flags & (ulong) CURSOR_TYPE_READ_ONLY);
thd->protocol= &thd->protocol_binary;
stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end); stmt->execute_loop(&expanded_query, open_cursor, packet, packet_end);
thd->protocol= save_protocol;
/* Close connection socket; for use with client testing (Bug#43560). */ /* Close connection socket; for use with client testing (Bug#43560). */
DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio);); DBUG_EXECUTE_IF("close_conn_after_stmt_execute", vio_close(thd->net.vio););
...@@ -2814,12 +2825,11 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields) ...@@ -2814,12 +2825,11 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields)
Prepared_statement Prepared_statement
****************************************************************************/ ****************************************************************************/
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg) Prepared_statement::Prepared_statement(THD *thd_arg)
:Statement(NULL, &main_mem_root, :Statement(NULL, &main_mem_root,
INITIALIZED, ++thd_arg->statement_id_counter), INITIALIZED, ++thd_arg->statement_id_counter),
thd(thd_arg), thd(thd_arg),
result(thd_arg), result(thd_arg),
protocol(protocol_arg),
param_array(0), param_array(0),
param_count(0), param_count(0),
last_errno(0), last_errno(0),
...@@ -3288,7 +3298,9 @@ Prepared_statement::reprepare() ...@@ -3288,7 +3298,9 @@ Prepared_statement::reprepare()
bool cur_db_changed; bool cur_db_changed;
bool error; bool error;
Prepared_statement copy(thd, &thd->protocol_text); Prepared_statement copy(thd);
copy.set_sql_prepare(); /* To suppress sending metadata to the client. */
status_var_increment(thd->status_var.com_stmt_reprepare); status_var_increment(thd->status_var.com_stmt_reprepare);
...@@ -3346,7 +3358,7 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy) ...@@ -3346,7 +3358,7 @@ bool Prepared_statement::validate_metadata(Prepared_statement *copy)
return FALSE -- the metadata of the original SELECT, return FALSE -- the metadata of the original SELECT,
if any, has not been sent to the client. if any, has not been sent to the client.
*/ */
if (is_protocol_text() || lex->describe) if (is_sql_prepare() || lex->describe)
return FALSE; return FALSE;
if (lex->select_lex.item_list.elements != if (lex->select_lex.item_list.elements !=
...@@ -3409,7 +3421,6 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy) ...@@ -3409,7 +3421,6 @@ Prepared_statement::swap_prepared_statement(Prepared_statement *copy)
DBUG_ASSERT(thd == copy->thd); DBUG_ASSERT(thd == copy->thd);
last_error[0]= '\0'; last_error[0]= '\0';
last_errno= 0; last_errno= 0;
/* Do not swap protocols, the copy always has protocol_text */
} }
...@@ -3550,8 +3561,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) ...@@ -3550,8 +3561,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
thd->stmt_arena= this; thd->stmt_arena= this;
reinit_stmt_before_use(thd, lex); reinit_stmt_before_use(thd, lex);
thd->protocol= protocol; /* activate stmt protocol */
/* Go! */ /* Go! */
if (open_cursor) if (open_cursor)
...@@ -3582,8 +3591,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor) ...@@ -3582,8 +3591,6 @@ bool Prepared_statement::execute(String *expanded_query, bool open_cursor)
if (cur_db_changed) if (cur_db_changed)
mysql_change_db(thd, &saved_cur_db_name, TRUE); mysql_change_db(thd, &saved_cur_db_name, TRUE);
thd->protocol= &thd->protocol_text; /* use normal protocol */
/* Assert that if an error, no cursor is open */ /* Assert that if an error, no cursor is open */
DBUG_ASSERT(! (error && cursor)); DBUG_ASSERT(! (error && cursor));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment