diff options
author | unknown <sergefp@mysql.com> | 2004-05-15 00:06:16 +0400 |
---|---|---|
committer | unknown <sergefp@mysql.com> | 2004-05-15 00:06:16 +0400 |
commit | 201412da7571f5078dae6cdb2067f64a3de5bef8 (patch) | |
tree | 2b847992c4d6ac40460d6727558f05df45a64c69 /sql | |
parent | dd4be0244d57da44351d6788dc9e29d2bcb60ea3 (diff) | |
parent | d55809d8911f0a2c479d99770624deb07a71a974 (diff) | |
download | mariadb-git-201412da7571f5078dae6cdb2067f64a3de5bef8.tar.gz |
Trivial manual merge
sql/sql_class.h:
Auto merged
sql/sql_lex.h:
Auto merged
sql/sql_parse.cc:
Auto merged
sql/sql_yacc.yy:
Auto merged
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item.cc | 11 | ||||
-rw-r--r-- | sql/item.h | 1 | ||||
-rw-r--r-- | sql/lex.h | 2 | ||||
-rw-r--r-- | sql/mysql_priv.h | 4 | ||||
-rw-r--r-- | sql/mysqld.cc | 3 | ||||
-rw-r--r-- | sql/sql_class.cc | 43 | ||||
-rw-r--r-- | sql/sql_class.h | 29 | ||||
-rw-r--r-- | sql/sql_lex.h | 6 | ||||
-rw-r--r-- | sql/sql_parse.cc | 40 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 261 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 73 |
11 files changed, 417 insertions, 56 deletions
diff --git a/sql/item.cc b/sql/item.cc index 11a9e88bdd6..b6b99a9f717 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -667,10 +667,10 @@ void Item_param::set_double(double value) } -void Item_param::set_value(const char *str, uint length) +void Item_param::set_value(const char *str, uint length, CHARSET_INFO *ci) { DBUG_ENTER("Item_param::set_value"); - str_value.copy(str,length,default_charset()); + str_value.copy(str,length,ci); item_type= STRING_ITEM; value_is_set= 1; maybe_null= 0; @@ -678,6 +678,11 @@ void Item_param::set_value(const char *str, uint length) DBUG_VOID_RETURN; } +void Item_param::set_value(const char *str, uint length) +{ + set_value(str, length, default_charset()); +} + void Item_param::set_time(TIME *tm, timestamp_type type) { @@ -1558,7 +1563,7 @@ bool Item::send(Protocol *protocol, String *buffer) } case MYSQL_TYPE_TINY: { - longlong nr; + longlong nr; nr= val_int(); if (!null_value) result= protocol->store_tiny(nr); diff --git a/sql/item.h b/sql/item.h index 99a0516e439..471d502a6e1 100644 --- a/sql/item.h +++ b/sql/item.h @@ -414,6 +414,7 @@ public: void set_int(longlong i); void set_double(double i); void set_value(const char *str, uint length); + void set_value(const char *str, uint length, CHARSET_INFO *ci); void set_long_str(const char *str, ulong length); void set_long_binary(const char *str, ulong length); void set_longdata(const char *str, ulong length); diff --git a/sql/lex.h b/sql/lex.h index e5bc537c213..64cc6b57464 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -133,6 +133,7 @@ static SYMBOL symbols[] = { { "DAY_MICROSECOND", SYM(DAY_MICROSECOND_SYM)}, { "DAY_MINUTE", SYM(DAY_MINUTE_SYM)}, { "DAY_SECOND", SYM(DAY_SECOND_SYM)}, + { "DEALLOCATE", SYM(DEALLOCATE_SYM)}, { "DEC", SYM(DECIMAL_SYM)}, { "DECIMAL", SYM(DECIMAL_SYM)}, { "DEFAULT", SYM(DEFAULT)}, @@ -324,6 +325,7 @@ static SYMBOL symbols[] = { { "POINT", SYM(POINT_SYM)}, { "POLYGON", SYM(POLYGON)}, { "PRECISION", SYM(PRECISION)}, + { "PREPARE", SYM(PREPARE_SYM)}, { "PREV", SYM(PREV_SYM)}, { "PRIMARY", SYM(PRIMARY_SYM)}, { "PRIVILEGES", SYM(PRIVILEGES)}, diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 8cae95e5070..dd8134840dd 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -634,8 +634,10 @@ int mysqld_show_column_types(THD *thd); int mysqld_help (THD *thd, const char *text); /* sql_prepare.cc */ -void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length); +int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, + LEX_STRING *name=NULL); void mysql_stmt_execute(THD *thd, char *packet, uint packet_length); +void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name); void mysql_stmt_free(THD *thd, char *packet); void mysql_stmt_reset(THD *thd, char *packet); void mysql_stmt_get_longdata(THD *thd, char *pos, ulong packet_length); diff --git a/sql/mysqld.cc b/sql/mysqld.cc index 2d2514b0017..e810393af60 100644 --- a/sql/mysqld.cc +++ b/sql/mysqld.cc @@ -4842,6 +4842,9 @@ struct show_var_st status_vars[]= { {"Com_unlock_tables", (char*) (com_stat+(uint) SQLCOM_UNLOCK_TABLES),SHOW_LONG}, {"Com_update", (char*) (com_stat+(uint) SQLCOM_UPDATE),SHOW_LONG}, {"Com_update_multi", (char*) (com_stat+(uint) SQLCOM_UPDATE_MULTI),SHOW_LONG}, + {"Com_prepare_sql", (char*) (com_stat+(uint) SQLCOM_PREPARE), SHOW_LONG}, + {"Com_execute_sql", (char*) (com_stat+(uint) SQLCOM_EXECUTE), SHOW_LONG}, + {"Com_dealloc_sql", (char*) (com_stat+(uint) SQLCOM_DEALLOCATE_PREPARE), SHOW_LONG}, {"Connections", (char*) &thread_id, SHOW_LONG_CONST}, {"Created_tmp_disk_tables", (char*) &created_tmp_disk_tables,SHOW_LONG}, {"Created_tmp_files", (char*) &my_tmp_file_created, SHOW_LONG}, diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 03d67c4f300..d50631e29c3 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -78,7 +78,6 @@ extern "C" void free_user_var(user_var_entry *entry) my_free((char*) entry,MYF(0)); } - bool key_part_spec::operator==(const key_part_spec& other) const { return length == other.length && !strcmp(field_name, other.field_name); @@ -233,7 +232,7 @@ THD::THD():user_time(0), current_statement(0), is_fatal_error(0), 16); else bzero((char*) &user_var_events, sizeof(user_var_events)); - + /* Protocol */ protocol= &protocol_simple; // Default protocol protocol_simple.init(this); @@ -1272,6 +1271,7 @@ Statement::Statement(THD *thd) query_length(0), free_list(0) { + name.str= NULL; init_sql_alloc(&mem_root, thd->variables.query_alloc_block_size, thd->variables.query_prealloc_size); @@ -1355,17 +1355,52 @@ static void delete_statement_as_hash_key(void *key) delete (Statement *) key; } +static 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 Statement_map::Statement_map() : last_found_statement(0) { - enum { START_HASH_SIZE = 16 }; - hash_init(&st_hash, default_charset_info, START_HASH_SIZE, 0, 0, + enum + { + 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, 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) { List_iterator_fast<Item_func_set_user_var> li(vars); diff --git a/sql/sql_class.h b/sql/sql_class.h index e602b7d6d5f..f208a3f4d73 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -461,6 +461,7 @@ public: */ bool allow_sum_func; + LEX_STRING name; /* name for named prepared statements */ LEX *lex; // parse tree descriptor /* Points to the query associated with this statement. It's const, but @@ -527,8 +528,14 @@ public: /* - Used to seek all existing statements in the connection - Deletes all statements in destructor. + Container for all statements created/used in a connection. + 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 @@ -536,12 +543,14 @@ class Statement_map public: 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); - if (rc == 0) - last_found_statement= statement; - return rc; + Statement *stmt; + stmt= (Statement*)hash_search(&names_hash, (byte*)name->str, + name->length); + return stmt; } Statement *find(ulong id) @@ -555,15 +564,21 @@ public: { if (statement == last_found_statement) last_found_statement= 0; + if (statement->name.str) + { + hash_delete(&names_hash, (byte *) statement); + } hash_delete(&st_hash, (byte *) statement); } ~Statement_map() { hash_free(&st_hash); + hash_free(&names_hash); } private: HASH st_hash; + HASH names_hash; Statement *last_found_statement; }; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 557c037c799..a525bc485c1 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -76,6 +76,7 @@ enum enum_sql_command { SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_STORAGE_ENGINES, SQLCOM_SHOW_PRIVILEGES, SQLCOM_HELP, SQLCOM_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM, + SQLCOM_PREPARE, SQLCOM_EXECUTE, SQLCOM_DEALLOCATE_PREPARE, /* This should be the last !!! */ SQLCOM_END }; @@ -605,6 +606,11 @@ typedef struct st_lex bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; bool derived_tables; bool safe_to_cache_query; + /* Prepared statements SQL syntax:*/ + LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */ + LEX_STRING prepared_stmt_code; /* Statement query (in PREPARE )*/ + /* Names of user variables holding parameters (in EXECUTE) */ + List<LEX_STRING> prepared_stmt_params; st_lex() {} inline void uncacheable(uint8 cause) { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index e949d40625d..65d538e3ac4 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1424,7 +1424,6 @@ bool dispatch_command(enum enum_server_command command, THD *thd, } case COM_EXECUTE: { - thd->free_list= NULL; mysql_stmt_execute(thd, packet, packet_length); break; } @@ -1971,7 +1970,44 @@ mysql_execute_command(THD *thd) } break; } - + case SQLCOM_PREPARE: + { + DBUG_PRINT("info", ("PREPARE: %.*s FROM '%.*s' \n", + lex->prepared_stmt_name.length, + lex->prepared_stmt_name.str, + lex->prepared_stmt_code.length, + lex->prepared_stmt_code.str)); + thd->command= COM_PREPARE; + if (!mysql_stmt_prepare(thd, lex->prepared_stmt_code.str, + lex->prepared_stmt_code.length + 1, + &lex->prepared_stmt_name)) + send_ok(thd, 0L, 0L, "Statement prepared"); + break; + } + case SQLCOM_EXECUTE: + { + DBUG_PRINT("info", ("EXECUTE: %.*s\n", + lex->prepared_stmt_name.length, + lex->prepared_stmt_name.str)); + mysql_sql_stmt_execute(thd, &lex->prepared_stmt_name); + lex->prepared_stmt_params.empty(); + break; + } + case SQLCOM_DEALLOCATE_PREPARE: + { + Statement* stmt; + DBUG_PRINT("info", ("DEALLOCATE PREPARE: %.*s\n", + lex->prepared_stmt_name.length, + lex->prepared_stmt_name.str)); + if ((stmt= thd->stmt_map.find_by_name(&lex->prepared_stmt_name))) + { + thd->stmt_map.erase(stmt); + send_ok(thd); + } + else + send_error(thd,ER_UNKNOWN_STMT_HANDLER,"Undefined prepared statement"); + break; + } case SQLCOM_DO: if (tables && ((res= check_table_access(thd, SELECT_ACL, tables,0)) || (res= open_and_lock_tables(thd,tables)))) diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 61e5b778b64..1612432cd67 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -98,12 +98,15 @@ public: #else bool (*set_params_data)(Prepared_statement *st); #endif + bool (*set_params_from_vars)(Prepared_statement *stmt, + List<LEX_STRING>& varnames); public: Prepared_statement(THD *thd_arg); virtual ~Prepared_statement(); virtual Statement::Type type() const; }; +static void execute_stmt(THD *thd, Prepared_statement *stmt); /****************************************************************************** Implementation @@ -639,6 +642,116 @@ static bool emb_insert_params_withlog(Prepared_statement *stmt) #endif /*!EMBEDDED_LIBRARY*/ +/* + Set prepared statement parameters from user variables. + SYNOPSIS + insert_params_from_vars() + stmt Statement + varnames List of variables. Caller must ensure that number of variables + in the list is equal to number of statement parameters + +*/ + +static bool insert_params_from_vars(Prepared_statement *stmt, + List<LEX_STRING>& varnames) +{ + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + user_var_entry *entry; + LEX_STRING *varname; + DBUG_ENTER("insert_params_from_vars"); + + List_iterator<LEX_STRING> var_it(varnames); + for (Item_param **it= begin; it < end; ++it) + { + Item_param *param= *it; + varname= var_it++; + if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars, + (byte*) varname->str, + varname->length)) + && entry->value) + { + param->item_result_type= entry->type; + switch (entry->type) + { + case REAL_RESULT: + param->set_double(*(double*)entry->value); + break; + case INT_RESULT: + param->set_int(*(longlong*)entry->value); + break; + case STRING_RESULT: + param->set_value(entry->value, entry->length, + entry->collation.collation); + break; + default: + DBUG_ASSERT(0); + } + } + else + param->maybe_null= param->null_value= param->value_is_set= 1; + } + DBUG_RETURN(0); +} + +static bool insert_params_from_vars_with_log(Prepared_statement *stmt, + List<LEX_STRING>& varnames) +{ + Item_param **begin= stmt->param_array; + Item_param **end= begin + stmt->param_count; + user_var_entry *entry; + LEX_STRING *varname; + DBUG_ENTER("insert_params_from_vars"); + + List_iterator<LEX_STRING> var_it(varnames); + String str, query; + const String *res; + 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) + { + Item_param *param= *it; + varname= var_it++; + if ((entry= (user_var_entry*)hash_search(&stmt->thd->user_vars, + (byte*) varname->str, + varname->length)) + && entry->value) + { + param->item_result_type= entry->type; + switch (entry->type) + { + case REAL_RESULT: + param->set_double(*(double*)entry->value); + break; + case INT_RESULT: + param->set_int(*(longlong*)entry->value); + break; + case STRING_RESULT: + param->set_value(entry->value, entry->length, + entry->collation.collation); + break; + default: + DBUG_ASSERT(0); + } + res= param->query_val_str(&str); + } + else + { + param->maybe_null= param->null_value= param->value_is_set= 1; + res= &my_null_string; + } + + if (query.replace(param->pos_in_query+length, 1, *res)) + DBUG_RETURN(1); + length+= res->length()-1; + } + if (alloc_query(stmt->thd, (char *) query.ptr(), query.length()+1)) + DBUG_RETURN(1); + DBUG_RETURN(0); +} + /* Validate INSERT statement: @@ -835,7 +948,7 @@ static int mysql_test_delete(Prepared_statement *stmt, */ static int mysql_test_select(Prepared_statement *stmt, - TABLE_LIST *tables) + TABLE_LIST *tables, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -867,7 +980,7 @@ static int mysql_test_select(Prepared_statement *stmt, if (lex->describe) { - if (send_prep_stmt(stmt, 0)) + if (!text_protocol && send_prep_stmt(stmt, 0)) goto err; } else @@ -881,14 +994,16 @@ static int mysql_test_select(Prepared_statement *stmt, goto err_prep; } - if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || + if (!text_protocol) + { + if (send_prep_stmt(stmt, lex->select_lex.item_list.elements) || thd->protocol_simple.send_fields(&lex->select_lex.item_list, 0) #ifndef EMBEDDED_LIBRARY - || net_flush(&thd->net) + || net_flush(&thd->net) #endif - ) - goto err_prep; - + ) + goto err_prep; + } unit->cleanup(); } thd->free_temporary_memory_pool_for_ps_preparing(); @@ -1166,7 +1281,7 @@ static int mysql_test_insert_select(Prepared_statement *stmt, 0 success 1 error, sent to client */ -static int send_prepare_results(Prepared_statement *stmt) +static int send_prepare_results(Prepared_statement *stmt, bool text_protocol) { THD *thd= stmt->thd; LEX *lex= stmt->lex; @@ -1203,7 +1318,7 @@ static int send_prepare_results(Prepared_statement *stmt) break; case SQLCOM_SELECT: - if ((res= mysql_test_select(stmt, tables))) + if ((res= mysql_test_select(stmt, tables, text_protocol))) goto error; /* Statement and field info has already been sent */ DBUG_RETURN(0); @@ -1261,7 +1376,7 @@ static int send_prepare_results(Prepared_statement *stmt) goto error; } if (res == 0) - DBUG_RETURN(send_prep_stmt(stmt, 0)); + DBUG_RETURN(text_protocol?0:send_prep_stmt(stmt, 0)); error: if (res < 0) send_error(thd, thd->killed ? ER_SERVER_SHUTDOWN : 0); @@ -1302,6 +1417,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 and resultset metadata information back to client (if any), without executing the query i.e. without any log/disk @@ -1313,9 +1436,11 @@ static bool init_param_array(Prepared_statement *stmt) list in lex->param_array, so that a fast and direct retrieval can be made without going through all field items. + */ -void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) +int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, + LEX_STRING *name) { LEX *lex; Prepared_statement *stmt= new Prepared_statement(thd); @@ -1327,14 +1452,26 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) if (stmt == 0) { send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); + } + + if (name) + { + stmt->name.length= name->length; + if (!(stmt->name.str= memdup_root(&stmt->mem_root, (byte*)name->str, + name->length))) + { + delete stmt; + send_error(thd, ER_OUT_OF_RESOURCES); + DBUG_RETURN(1); + } } if (thd->stmt_map.insert(stmt)) { delete stmt; send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); } thd->stmt_backup.set_statement(thd); @@ -1351,7 +1488,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); send_error(thd, ER_OUT_OF_RESOURCES); - DBUG_VOID_RETURN; + DBUG_RETURN(1); } mysql_log.write(thd, COM_PREPARE, "%s", packet); @@ -1363,7 +1500,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) error= yyparse((void *)thd) || thd->is_fatal_error || init_param_array(stmt) || - send_prepare_results(stmt); + send_prepare_results(stmt, test(name)); /* restore to WAIT_PRIOR: QUERY_PRIOR is set inside alloc_query */ if (!(specialflag & SPECIAL_NO_PRIOR)) @@ -1379,6 +1516,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) { /* Statement map deletes statement on erase */ thd->stmt_map.erase(stmt); + stmt= NULL; /* error is sent inside yyparse/send_prepare_results */ } else @@ -1393,7 +1531,7 @@ void mysql_stmt_prepare(THD *thd, char *packet, uint packet_length) sl->prep_where= sl->where; } } - DBUG_VOID_RETURN; + DBUG_RETURN(!stmt); } /* Reinit statement before execution */ @@ -1472,7 +1610,6 @@ static void reset_stmt_params(Prepared_statement *stmt) mysql_stmt_execute() */ - void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) { ulong stmt_id= uint4korr(packet); @@ -1496,9 +1633,7 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } - thd->stmt_backup.set_statement(thd); - thd->set_statement(stmt); - reset_stmt_for_execute(stmt); + #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { @@ -1516,44 +1651,88 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt)) goto set_params_data_err; #endif + thd->protocol= &thd->protocol_prep; // Switch to binary protocol + execute_stmt(thd, stmt); + thd->protocol= &thd->protocol_simple; // Use normal protocol + DBUG_VOID_RETURN; + +set_params_data_err: + reset_stmt_params(stmt); + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + send_error(thd); + DBUG_VOID_RETURN; +} + + +/* + Execute prepared statement using parameter values from + lex->prepared_stmt_params and send result to the client using text protocol. +*/ + +void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) +{ + Prepared_statement *stmt; + 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) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + send_error(thd); + DBUG_VOID_RETURN; + } + /* Item_param allows setting parameters in COM_EXECUTE only */ + thd->command= COM_EXECUTE; + + if (stmt->set_params_from_vars(stmt, thd->lex->prepared_stmt_params)) + { + my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); + send_error(thd); + } + execute_stmt(thd, stmt); + DBUG_VOID_RETURN; +} + +/* + Execute prepared statement. + Caller must set parameter values and thd::protocol. + thd->free_list is assumed to be garbage. +*/ +static void execute_stmt(THD *thd, Prepared_statement *stmt) +{ + DBUG_ENTER("execute_stmt"); + thd->free_list= NULL; + thd->stmt_backup.set_statement(thd); + thd->set_statement(stmt); + reset_stmt_for_execute(stmt); 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 mysql_execute_command(thd); thd->lex->unit.cleanup(); - 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); - reset_stmt_params(stmt); - close_thread_tables(thd); // to close derived tables - thd->set_statement(&thd->stmt_backup); /* Free Items that were created during this execution of the PS by query optimizer. */ free_items(thd->free_list); - DBUG_VOID_RETURN; - -set_params_data_err: + cleanup_items(stmt->free_list); reset_stmt_params(stmt); + close_thread_tables(thd); // to close derived tables thd->set_statement(&thd->stmt_backup); - my_error(ER_WRONG_ARGUMENTS, MYF(0), "mysql_execute"); - send_error(thd); DBUG_VOID_RETURN; } + /* Reset a prepared statement in case there was a recoverable error. SYNOPSIS @@ -1694,6 +1873,7 @@ Prepared_statement::Prepared_statement(THD *thd_arg) if (mysql_bin_log.is_open()) { log_full_query= 1; + set_params_from_vars= insert_params_from_vars_with_log; #ifndef EMBEDDED_LIBRARY set_params= insert_params_withlog; #else @@ -1701,11 +1881,14 @@ Prepared_statement::Prepared_statement(THD *thd_arg) #endif } else + { + set_params_from_vars= insert_params_from_vars; #ifndef EMBEDDED_LIBRARY set_params= insert_params; #else set_params_data= emb_insert_params; #endif + } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 0f29289ac25..6a683a26b01 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -432,6 +432,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MEDIUMTEXT %token NUMERIC_SYM %token PRECISION +%token PREPARE_SYM +%token DEALLOCATE_SYM %token QUICK %token REAL %token SIGNED_SYM @@ -724,6 +726,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); precision subselect_start opt_and charset subselect_end select_var_list select_var_list_init help opt_len opt_extended_describe + prepare execute deallocate END_OF_INPUT %type <NONE> @@ -760,10 +763,12 @@ verb_clause: | checksum | commit | create + | deallocate | delete | describe | do | drop + | execute | flush | grant | handler @@ -775,6 +780,7 @@ verb_clause: | optimize | keycache | preload + | prepare | purge | rename | repair @@ -795,6 +801,71 @@ verb_clause: | use ; +deallocate: + DEALLOCATE_SYM PREPARE_SYM ident + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (thd->command == COM_PREPARE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_DEALLOCATE_PREPARE; + lex->prepared_stmt_name= $3; + }; + +prepare: + PREPARE_SYM ident FROM TEXT_STRING_sys + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (thd->command == COM_PREPARE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_PREPARE; + lex->prepared_stmt_name= $2; + lex->prepared_stmt_code= $4; + }; + +execute: + EXECUTE_SYM ident + { + THD *thd=YYTHD; + LEX *lex= thd->lex; + if (thd->command == COM_PREPARE) + { + yyerror(ER(ER_SYNTAX_ERROR)); + YYABORT; + } + lex->sql_command= SQLCOM_EXECUTE; + lex->prepared_stmt_name= $2; + } + execute_using + {} + ; + +execute_using: + /* nothing */ + | USING execute_var_list + ; + +execute_var_list: + execute_var_list ',' execute_var_ident + | execute_var_ident + ; + +execute_var_ident: '@' ident_or_text + { + LEX *lex=Lex; + LEX_STRING *lexstr= (LEX_STRING*)sql_memdup(&$2, sizeof(LEX_STRING)); + if (!lexstr || lex->prepared_stmt_params.push_back(lexstr)) + YYABORT; + } + ; + /* help */ help: @@ -4924,6 +4995,7 @@ keyword: | DATETIME {} | DATE_SYM {} | DAY_SYM {} + | DEALLOCATE_SYM {} | DELAY_KEY_WRITE_SYM {} | DES_KEY_FILE {} | DIRECTORY_SYM {} @@ -5022,6 +5094,7 @@ keyword: | PASSWORD {} | POINT_SYM {} | POLYGON {} + | PREPARE_SYM {} | PREV_SYM {} | PROCESS {} | PROCESSLIST_SYM {} |