Commit 0830a3be authored by Davi Arnaut's avatar Davi Arnaut

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

Merge Konstantin's patch and add a test case.
parents bb4f24bf e601194c
...@@ -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 @@ public: ...@@ -148,7 +148,7 @@ public:
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 @@ public: ...@@ -156,7 +156,8 @@ public:
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));
......
...@@ -17940,6 +17940,57 @@ static void test_bug41078(void) ...@@ -17940,6 +17940,57 @@ static void test_bug41078(void)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
/**
Bug#44495: Prepared Statement:
CALL p(<x>) - `thd->protocol == &thd->protocol_text' failed
*/
static void test_bug44495()
{
int rc;
MYSQL con;
MYSQL_STMT *stmt;
DBUG_ENTER("test_bug44495");
myheader("test_44495");
rc= mysql_query(mysql, "DROP PROCEDURE IF EXISTS p1");
myquery(rc);
rc= mysql_query(mysql, "CREATE PROCEDURE p1(IN arg VARCHAR(25))"
" BEGIN SET @stmt = CONCAT('SELECT \"', arg, '\"');"
" PREPARE ps1 FROM @stmt;"
" EXECUTE ps1;"
" DROP PREPARE ps1;"
"END;");
myquery(rc);
DIE_UNLESS(mysql_init(&con));
DIE_UNLESS(mysql_real_connect(&con, opt_host, opt_user, opt_password,
current_db, opt_port, opt_unix_socket,
CLIENT_MULTI_RESULTS));
stmt= mysql_simple_prepare(&con, "CALL p1('abc')");
check_stmt(stmt);
rc= mysql_stmt_execute(stmt);
check_execute(stmt, rc);
rc= my_process_stmt_result(stmt);
DIE_UNLESS(rc == 1);
mysql_stmt_close(stmt);
mysql_close(&con);
rc= mysql_query(mysql, "DROP PROCEDURE p1");
myquery(rc);
DBUG_VOID_RETURN;
}
/* /*
Read and parse arguments and MySQL options from my.cnf Read and parse arguments and MySQL options from my.cnf
*/ */
...@@ -18255,6 +18306,7 @@ static struct my_tests_st my_tests[]= { ...@@ -18255,6 +18306,7 @@ static struct my_tests_st my_tests[]= {
{ "test_bug36326", test_bug36326 }, { "test_bug36326", test_bug36326 },
#endif #endif
{ "test_bug41078", test_bug41078 }, { "test_bug41078", test_bug41078 },
{ "test_bug44495", test_bug44495 },
{ 0, 0 } { 0, 0 }
}; };
......
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