Commit a314cbef authored by sergefp@mysql.com's avatar sergefp@mysql.com

WL#1622 "SQL Syntax for Prepared Statements": post-review fixes:

  Moved PS name to Statement class, Statement_map now handles name-to-statement resolution.
  Both named and unnamed statements are now executed in one function (sql_prepare.cc:execute_stmt)
  Fixed a problem: Malformed sequence of commands from client could cause server to use previously deleted objects.
  Some code cleanup and small fixes
parent eef8fbcf
...@@ -613,11 +613,10 @@ int mysqld_show_column_types(THD *thd); ...@@ -613,11 +613,10 @@ int mysqld_show_column_types(THD *thd);
int mysqld_help (THD *thd, const char *text); int mysqld_help (THD *thd, const char *text);
/* sql_prepare.cc */ /* sql_prepare.cc */
class Prepared_statement; int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, LEX_STRING *name=NULL);
uint packet_length, bool text_protocol=false);
void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); void mysql_stmt_execute(THD *thd, char *packet, uint packet_length);
void mysql_sql_stmt_execute(THD *thd, Prepared_statement *stmt); void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name);
void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_free(THD *thd, char *packet);
void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet);
void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length);
......
...@@ -78,24 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry) ...@@ -78,24 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry)
my_free((char*) entry,MYF(0)); my_free((char*) entry,MYF(0));
} }
/****************************************************************************
** SQL syntax names for Prepared Statements
****************************************************************************/
extern "C" byte *get_stmt_key(SQL_PREP_STMT_ENTRY *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->name.length;
return (byte*) entry->name.str;
}
extern "C" void free_sql_stmt(SQL_PREP_STMT_ENTRY *entry)
{
char *pos= (char*) entry+ALIGN_SIZE(sizeof(*entry));
my_free((char*) entry,MYF(0));
}
/**************************************************************************** /****************************************************************************
** Thread specific functions ** Thread specific functions
****************************************************************************/ ****************************************************************************/
...@@ -178,9 +160,6 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), ...@@ -178,9 +160,6 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0),
else else
bzero((char*) &user_var_events, sizeof(user_var_events)); bzero((char*) &user_var_events, sizeof(user_var_events));
hash_init(&sql_prepared_stmts, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_stmt_key,
(hash_free_key) free_sql_stmt,0);
/* Protocol */ /* Protocol */
protocol= &protocol_simple; // Default protocol protocol= &protocol_simple; // Default protocol
protocol_simple.init(this); protocol_simple.init(this);
...@@ -299,7 +278,6 @@ void THD::cleanup(void) ...@@ -299,7 +278,6 @@ void THD::cleanup(void)
my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR)); my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR));
delete_dynamic(&user_var_events); delete_dynamic(&user_var_events);
hash_free(&user_vars); hash_free(&user_vars);
hash_free(&sql_prepared_stmts);
if (global_read_lock) if (global_read_lock)
unlock_global_read_lock(this); unlock_global_read_lock(this);
if (ull) if (ull)
...@@ -1220,6 +1198,7 @@ Statement::Statement(THD *thd) ...@@ -1220,6 +1198,7 @@ Statement::Statement(THD *thd)
query_length(0), query_length(0),
free_list(0) free_list(0)
{ {
name.str= NULL;
init_sql_alloc(&mem_root, init_sql_alloc(&mem_root,
thd->variables.query_alloc_block_size, thd->variables.query_alloc_block_size,
thd->variables.query_prealloc_size); thd->variables.query_prealloc_size);
...@@ -1303,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key) ...@@ -1303,17 +1282,52 @@ static void delete_statement_as_hash_key(void *key)
delete (Statement *) key; delete (Statement *) key;
} }
byte *get_stmt_name_hash_key(Statement *entry, uint *length,
my_bool not_used __attribute__((unused)))
{
*length=(uint) entry->name.length;
return (byte*) entry->name.str;
}
C_MODE_END C_MODE_END
Statement_map::Statement_map() : Statement_map::Statement_map() :
last_found_statement(0) last_found_statement(0)
{ {
enum { START_HASH_SIZE = 16 }; enum
hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0, {
START_STMT_HASH_SIZE = 16,
START_NAME_HASH_SIZE = 16
};
hash_init(&st_hash, default_charset_info, START_STMT_HASH_SIZE, 0, 0,
get_statement_id_as_hash_key, get_statement_id_as_hash_key,
delete_statement_as_hash_key, MYF(0)); delete_statement_as_hash_key, MYF(0));
hash_init(&names_hash, &my_charset_bin, START_NAME_HASH_SIZE, 0, 0,
(hash_get_key) get_stmt_name_hash_key,
NULL,MYF(0));
} }
int Statement_map::insert(Statement *statement)
{
int rc= my_hash_insert(&st_hash, (byte *) statement);
if (rc == 0)
last_found_statement= statement;
if (statement->name.str)
{
/*
If there is a statement with the same name, remove it. It is ok to
remove old and fail to insert new one at the same time.
*/
Statement *old_stmt;
if ((old_stmt= find_by_name(&statement->name)))
erase(old_stmt);
if ((rc= my_hash_insert(&names_hash, (byte*)statement)))
hash_delete(&st_hash, (byte*)statement);
}
return rc;
}
bool select_dumpvar::send_data(List<Item> &items) bool select_dumpvar::send_data(List<Item> &items)
{ {
List_iterator_fast<Item_func_set_user_var> li(vars); List_iterator_fast<Item_func_set_user_var> li(vars);
......
...@@ -456,6 +456,7 @@ class Statement ...@@ -456,6 +456,7 @@ class Statement
*/ */
bool allow_sum_func; bool allow_sum_func;
LEX_STRING name; /* name for named prepared statements */
LEX *lex; // parse tree descriptor LEX *lex; // parse tree descriptor
/* /*
Points to the query associated with this statement. It's const, but Points to the query associated with this statement. It's const, but
...@@ -522,8 +523,14 @@ class Statement ...@@ -522,8 +523,14 @@ class Statement
/* /*
Used to seek all existing statements in the connection Container for all statements created/used in a connection.
Deletes all statements in destructor. Statements in Statement_map have unique Statement::id (guaranteed by id
assignment in Statement::Statement)
Non-empty statement names are unique too: attempt to insert a new statement
with duplicate name causes older statement to be deleted
Statements are auto-deleted when they are removed from the map and when the
map is deleted.
*/ */
class Statement_map class Statement_map
...@@ -531,12 +538,14 @@ class Statement_map ...@@ -531,12 +538,14 @@ class Statement_map
public: public:
Statement_map(); Statement_map();
int insert(Statement *statement) int insert(Statement *statement);
Statement *find_by_name(LEX_STRING *name)
{ {
int rc= my_hash_insert(&st_hash, (byte *) statement); Statement *stmt;
if (rc == 0) stmt= (Statement*)hash_search(&names_hash, (byte*)name->str,
last_found_statement= statement; name->length);
return rc; return stmt;
} }
Statement *find(ulong id) Statement *find(ulong id)
...@@ -550,15 +559,21 @@ class Statement_map ...@@ -550,15 +559,21 @@ class Statement_map
{ {
if (statement == last_found_statement) if (statement == last_found_statement)
last_found_statement= 0; last_found_statement= 0;
if (statement->name.str)
{
hash_delete(&names_hash, (byte *) statement);
}
hash_delete(&st_hash, (byte *) statement); hash_delete(&st_hash, (byte *) statement);
} }
~Statement_map() ~Statement_map()
{ {
hash_free(&st_hash); hash_free(&st_hash);
hash_free(&names_hash);
} }
private: private:
HASH st_hash; HASH st_hash;
HASH names_hash;
Statement *last_found_statement; Statement *last_found_statement;
}; };
...@@ -594,12 +609,6 @@ class THD :public ilink, ...@@ -594,12 +609,6 @@ class THD :public ilink,
struct system_variables variables; // Changeable local variables struct system_variables variables; // Changeable local variables
pthread_mutex_t LOCK_delete; // Locked before thd is deleted pthread_mutex_t LOCK_delete; // Locked before thd is deleted
/*
statement_name -> (Statement*) map of statements prepared using SQL syntax.
Hash element is SQL_PREP_STMT_ENTRY.
*/
HASH sql_prepared_stmts;
/* all prepared statements and cursors of this connection */ /* all prepared statements and cursors of this connection */
Statement_map stmt_map; Statement_map stmt_map;
/* /*
...@@ -1276,14 +1285,6 @@ class user_var_entry ...@@ -1276,14 +1285,6 @@ class user_var_entry
DTCollation collation; DTCollation collation;
}; };
class Prepared_statement;
/* Needed by THD::sql_prepared_stmts */
typedef struct st_sql_prep_stmt_entry
{
public:
LEX_STRING name;
Prepared_statement *stmt;
}SQL_PREP_STMT_ENTRY;
/* Class for unique (removing of duplicates) */ /* Class for unique (removing of duplicates) */
......
...@@ -1961,87 +1961,40 @@ mysql_execute_command(THD *thd) ...@@ -1961,87 +1961,40 @@ mysql_execute_command(THD *thd)
} }
case SQLCOM_PREPARE: case SQLCOM_PREPARE:
{ {
char *stmt_name= lex->prepared_stmt_name.str; DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n",
uint name_len= lex->prepared_stmt_name.length; lex->prepared_stmt_name.length,
Prepared_statement *stmt; lex->prepared_stmt_name.str,
SQL_PREP_STMT_ENTRY *entry;
DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n", name_len, stmt_name,
lex->prepared_stmt_code.length, lex->prepared_stmt_code.length,
lex->prepared_stmt_code.str)); lex->prepared_stmt_code.str));
if ((entry=(SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts,
(byte*)stmt_name, name_len)))
{
/* Free the statement with the same name and reuse hash entry */
thd->stmt_map.erase((Statement*)entry->stmt);
}
else
{
uint size=ALIGN_SIZE(sizeof(SQL_PREP_STMT_ENTRY))+name_len+1;
if (!hash_inited(&thd->sql_prepared_stmts) ||
!(entry= (SQL_PREP_STMT_ENTRY*)my_malloc(size,MYF(MY_WME))))
{
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
entry->name.str= (char*)entry + ALIGN_SIZE(sizeof(SQL_PREP_STMT_ENTRY));
entry->name.length= name_len;
memcpy(entry->name.str, stmt_name, name_len+1);
if (my_hash_insert(&thd->sql_prepared_stmts, (byte*)entry))
{
my_free((char*)entry,MYF(0));
send_error(thd, ER_OUT_OF_RESOURCES);
break;
}
}
/* Pretend this is a COM_PREPARE query so parser allows placeholders etc*/
thd->command= COM_PREPARE; thd->command= COM_PREPARE;
/* 'length+1' is for alloc_query that strips the last character */ if (!mysql_stmt_prepare(thd, lex->prepared_stmt_code.str,
stmt= mysql_stmt_prepare(thd, lex->prepared_stmt_code.str, lex->prepared_stmt_code.length + 1,
lex->prepared_stmt_code.length + 1, true); &lex->prepared_stmt_name))
if (stmt)
{
entry->stmt= stmt;
send_ok(thd, 0L, 0L, "Statement prepared"); send_ok(thd, 0L, 0L, "Statement prepared");
}
else
hash_delete(&thd->sql_prepared_stmts, (byte*)entry);
break; break;
} }
case SQLCOM_EXECUTE: case SQLCOM_EXECUTE:
{ {
char *stmt_name= lex->prepared_stmt_name.str; DBUG_PRINT("info", ("EXECUTE: %.*s\n",
uint name_len= lex->prepared_stmt_name.length; lex->prepared_stmt_name.length,
SQL_PREP_STMT_ENTRY *entry; lex->prepared_stmt_name.str));
DBUG_PRINT("info", ("EXECUTE: %.*s\n", name_len, stmt_name)); mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name);
if (!(entry= (SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts,
(byte*)stmt_name,
name_len)))
{
send_error(thd, ER_UNKNOWN_STMT_HANDLER, "Undefined prepared statement");
lex->prepared_stmt_params.empty();
break;
}
mysql_sql_stmt_execute(thd, entry->stmt);
lex->prepared_stmt_params.empty(); lex->prepared_stmt_params.empty();
break; break;
} }
case SQLCOM_DEALLOCATE_PREPARE: case SQLCOM_DEALLOCATE_PREPARE:
{ {
char *stmt_name= lex->prepared_stmt_name.str; Statement* stmt;
uint name_len= lex->prepared_stmt_name.length; DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n",
SQL_PREP_STMT_ENTRY *entry; lex->prepared_stmt_name.length,
DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", name_len, stmt_name)); lex->prepared_stmt_name.str));
if (!(entry= (SQL_PREP_STMT_ENTRY*)hash_search(&thd->sql_prepared_stmts, if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name)))
(byte*)stmt_name,
name_len)))
{ {
send_error(thd, ER_UNKNOWN_STMT_HANDLER, "Undefined prepared statement"); thd->stmt_map.erase(stmt);
break;
}
thd->stmt_map.erase((Statement*)entry->stmt);
hash_delete(&thd->sql_prepared_stmts, (byte*)entry);
send_ok(thd); send_ok(thd);
}
else
send_error(thd,ER_UNKNOWN_STMT_HANDLER,"Undefined prepared statement");
break; break;
} }
case SQLCOM_DO: case SQLCOM_DO:
......
...@@ -107,6 +107,7 @@ class Prepared_statement: public Statement ...@@ -107,6 +107,7 @@ class Prepared_statement: public Statement
virtual Statement::Type type() const; virtual Statement::Type type() const;
}; };
static void execute_stmt(THD *thd, Prepared_statement *stmt);
/****************************************************************************** /******************************************************************************
Implementation Implementation
...@@ -636,7 +637,6 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) ...@@ -636,7 +637,6 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt)
/* /*
Set prepared statement parameters from user variables. Set prepared statement parameters from user variables.
Also replace '?' marks with values in thd->query if binary logging is on.
SYNOPSIS SYNOPSIS
insert_params_from_vars() insert_params_from_vars()
stmt Statement stmt Statement
...@@ -682,11 +682,7 @@ static bool insert_params_from_vars(Prepared_statement *stmt, ...@@ -682,11 +682,7 @@ static bool insert_params_from_vars(Prepared_statement *stmt,
} }
} }
else else
{ param->maybe_null= param->null_value= param->value_is_set= 1;
param->item_result_type= INT_RESULT;
param->maybe_null= param->null_value= 1;
param->value_is_set= 0;
}
} }
DBUG_RETURN(0); DBUG_RETURN(0);
} }
...@@ -704,6 +700,8 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -704,6 +700,8 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
String str, query; String str, query;
const String *res; const String *res;
uint32 length= 0; uint32 length= 0;
if (query.copy(stmt->query, stmt->query_length, default_charset_info))
DBUG_RETURN(1);
for (Item_param **it= begin; it < end; ++it) for (Item_param **it= begin; it < end; ++it)
{ {
...@@ -734,9 +732,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt, ...@@ -734,9 +732,7 @@ static bool insert_params_from_vars_with_log(Prepared_statement *stmt,
} }
else else
{ {
param->item_result_type= INT_RESULT; param->maybe_null= param->null_value= param->value_is_set= 1;
param->maybe_null= param->null_value= 1;
param->value_is_set= 0;
res= &my_null_string; res= &my_null_string;
} }
...@@ -1089,6 +1085,14 @@ static bool init_param_array(Prepared_statement *stmt) ...@@ -1089,6 +1085,14 @@ static bool init_param_array(Prepared_statement *stmt)
/* /*
SYNOPSIS
mysql_stmt_prepare()
packet Prepared query
packet_length query length, with ignored trailing NULL or quote char.
name NULL or statement name. For unnamed statements binary PS
protocol is used, for named statmenents text protocol is
used.
Parse the query and send the total number of parameters Parse the query and send the total number of parameters
and resultset metadata information back to client (if any), and resultset metadata information back to client (if any),
without executing the query i.e. without any log/disk without executing the query i.e. without any log/disk
...@@ -1103,8 +1107,8 @@ static bool init_param_array(Prepared_statement *stmt) ...@@ -1103,8 +1107,8 @@ static bool init_param_array(Prepared_statement *stmt)
*/ */
Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length,
uint packet_length, bool text_protocol) LEX_STRING *name)
{ {
LEX *lex; LEX *lex;
Prepared_statement *stmt= new Prepared_statement(thd); Prepared_statement *stmt= new Prepared_statement(thd);
...@@ -1116,14 +1120,26 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, ...@@ -1116,14 +1120,26 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
if (stmt == 0) if (stmt == 0)
{ {
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_RETURN(NULL); DBUG_RETURN(1);
}
if (name)
{
stmt->name.length= name->length;
if (!(stmt->name.str= my_memdup((byte*)name->str, name->length,
MYF(MY_WME))))
{
delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_RETURN(1);
}
} }
if (thd->stmt_map.insert(stmt)) if (thd->stmt_map.insert(stmt))
{ {
delete stmt; delete stmt;
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_RETURN(NULL); DBUG_RETURN(1);
} }
thd->stmt_backup.set_statement(thd); thd->stmt_backup.set_statement(thd);
...@@ -1140,7 +1156,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, ...@@ -1140,7 +1156,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
/* Statement map deletes statement on erase */ /* Statement map deletes statement on erase */
thd->stmt_map.erase(stmt); thd->stmt_map.erase(stmt);
send_error(thd, ER_OUT_OF_RESOURCES); send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_RETURN(NULL); DBUG_RETURN(1);
} }
mysql_log.write(thd, COM_PREPARE, "%s", packet); mysql_log.write(thd, COM_PREPARE, "%s", packet);
...@@ -1152,7 +1168,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, ...@@ -1152,7 +1168,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
error= yyparse((void *)thd) || thd->is_fatal_error || error= yyparse((void *)thd) || thd->is_fatal_error ||
init_param_array(stmt) || init_param_array(stmt) ||
send_prepare_results(stmt, text_protocol); send_prepare_results(stmt, test(name));
/* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
...@@ -1183,7 +1199,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet, ...@@ -1183,7 +1199,7 @@ Prepared_statement *mysql_stmt_prepare(THD *thd, char *packet,
sl->prep_where= sl->where; sl->prep_where= sl->where;
} }
} }
DBUG_RETURN(stmt); DBUG_RETURN(!stmt);
} }
/* Reinit statement before execution */ /* Reinit statement before execution */
...@@ -1236,6 +1252,7 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) ...@@ -1236,6 +1252,7 @@ static void reset_stmt_for_execute(Prepared_statement *stmt)
} }
} }
/* /*
Executes previously prepared query. Executes previously prepared query.
If there is any parameters, then replace markers with the data supplied If there is any parameters, then replace markers with the data supplied
...@@ -1267,11 +1284,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1267,11 +1284,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
reset_stmt_for_execute(stmt);
#ifndef EMBEDDED_LIBRARY #ifndef EMBEDDED_LIBRARY
if (stmt->param_count) if (stmt->param_count)
{ {
...@@ -1289,30 +1301,12 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1289,30 +1301,12 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
if (stmt->param_count && stmt->set_params_data(stmt)) if (stmt->param_count && stmt->set_params_data(stmt))
goto set_params_data_err; goto set_params_data_err;
#endif #endif
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR);
/*
TODO:
Also, have checks on basic executions such as mysql_insert(),
mysql_delete(), mysql_update() and mysql_select() to not to
have re-check on setup_* and other things ..
*/
thd->protocol= &thd->protocol_prep; // Switch to binary protocol thd->protocol= &thd->protocol_prep; // Switch to binary protocol
mysql_execute_command(thd); execute_stmt(thd, stmt);
thd->protocol= &thd->protocol_simple; // Use normal protocol thd->protocol= &thd->protocol_simple; // Use normal protocol
if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
cleanup_items(stmt->free_list);
close_thread_tables(thd); // to close derived tables
thd->set_statement(&thd->stmt_backup);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
set_params_data_err: set_params_data_err:
thd->set_statement(&thd->stmt_backup);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
send_error(thd); send_error(thd);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
...@@ -1324,28 +1318,48 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) ...@@ -1324,28 +1318,48 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length)
lex->prepared_stmt_params and send result to the client using text protocol. lex->prepared_stmt_params and send result to the client using text protocol.
*/ */
void mysql_sql_stmt_execute(THD *thd, Prepared_statement *stmt) void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name)
{ {
Prepared_statement *stmt;
DBUG_ENTER("mysql_stmt_execute"); DBUG_ENTER("mysql_stmt_execute");
if (!(stmt= (Prepared_statement*)thd->stmt_map.find_by_name(stmt_name)))
{
send_error(thd, ER_UNKNOWN_STMT_HANDLER,
"Undefined prepared statement");
DBUG_VOID_RETURN;
}
if (stmt->param_count != thd->lex->prepared_stmt_params.elements) if (stmt->param_count != thd->lex->prepared_stmt_params.elements)
{ {
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
send_error(thd); send_error(thd);
DBUG_VOID_RETURN; DBUG_VOID_RETURN;
} }
thd->stmt_backup.set_statement(thd); /* Item_param allows setting parameters in COM_EXECUTE only */
thd->set_statement(stmt);
reset_stmt_for_execute(stmt);
thd->command= COM_EXECUTE; thd->command= COM_EXECUTE;
if (stmt->set_params_from_vars(stmt, thd->stmt_backup.lex-> if (stmt->set_params_from_vars(stmt, thd->lex->prepared_stmt_params))
prepared_stmt_params))
{ {
thd->set_statement(&thd->stmt_backup);
my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute");
send_error(thd); send_error(thd);
} }
execute_stmt(thd, stmt);
DBUG_VOID_RETURN;
}
/*
Execute prepared statement.
Caller must set parameter values and thd::protocol.
*/
static void execute_stmt(THD *thd, Prepared_statement *stmt)
{
DBUG_ENTER("execute_stmt");
thd->stmt_backup.set_statement(thd);
thd->set_statement(stmt);
reset_stmt_for_execute(stmt);
if (!(specialflag & SPECIAL_NO_PRIOR)) if (!(specialflag & SPECIAL_NO_PRIOR))
my_pthread_setprio(pthread_self(),QUERY_PRIOR); my_pthread_setprio(pthread_self(),QUERY_PRIOR);
mysql_execute_command(thd); mysql_execute_command(thd);
...@@ -1359,6 +1373,7 @@ void mysql_sql_stmt_execute(THD *thd, Prepared_statement *stmt) ...@@ -1359,6 +1373,7 @@ void mysql_sql_stmt_execute(THD *thd, Prepared_statement *stmt)
} }
/* /*
Reset a prepared statement, in case there was an error in send_longdata. Reset a prepared statement, in case there was an error in send_longdata.
Note: we don't send any reply to that command. Note: we don't send any reply to that command.
...@@ -1522,6 +1537,8 @@ Prepared_statement::Prepared_statement(THD *thd_arg) ...@@ -1522,6 +1537,8 @@ Prepared_statement::Prepared_statement(THD *thd_arg)
Prepared_statement::~Prepared_statement() Prepared_statement::~Prepared_statement()
{ {
free_items(free_list); free_items(free_list);
if (name.str)
my_free(name.str, MYF(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