diff options
author | unknown <konstantin@mysql.com> | 2004-10-14 02:53:59 +0400 |
---|---|---|
committer | unknown <konstantin@mysql.com> | 2004-10-14 02:53:59 +0400 |
commit | 54b00f5453b49047142d59da21e19d607f4b208e (patch) | |
tree | 27ce02a5ecb8610d69b81dc15a3d96a62126e80a /sql | |
parent | d007600b9b83818b27e916457e166dccdf7c729c (diff) | |
download | mariadb-git-54b00f5453b49047142d59da21e19d607f4b208e.tar.gz |
A fix and test case for Bug#5985 ""prepare stmt from "select rand(?)"
crashes server." The fix makes Item_func_rand prepared-statements
aware plus it fixes the case when RAND is used in prepared
statements and replication is on (as well as several similar issues).
Until now we did not reset THD before every execution of a prepared
statement, so if some execution had set thd->time_zone_used
or thd->rand_used they would not be reset until next mysql_parse.
Some of post-review fixes done.
mysql-test/r/ps.result:
A test case for Bug#5985: test results fixed.
mysql-test/t/ps.test:
A test case for Bug#5985 "prepare stmt from "select rand(?)" crashes
server."
sql/item_func.cc:
Actual fix for Bug#5985: Item_func_rand rewritten to be
prepared statements aware.
sql/item_func.h:
Actual fix for Bug#5985: Item_func_rand rewritten to be
prepared statements aware.
sql/mysql_priv.h:
We need a separate call to reset THD state before every execute of
a prepared statement. Otherwise things like THD->user_var_events
are never cleaned up and bloat binary log (as the list of events
grows from execution to execution).
sql/sql_class.cc:
Statement::end_statement -> THD::end_statement()
(a leftover from some design change which is not to pushed now, but the
leftover is to be pushed).
sql/sql_class.h:
Statement::end_statement -> THD::end_statement()
(a leftover from some design change which is not to pushed now, but the
leftover is to be pushed).
sql/sql_lex.cc:
Move the part responsible for initializing LEX from mysql_init_query
to lex_start.
sql/sql_lex.h:
All lex-related initialization is now in lex_start.
Move thd->select_number to lex->select_number to be able to use it
easily in lex_start.
sql/sql_parse.cc:
Split mysql_init_query into two functions: mysql_reset_thd_for_next_query,
which is used in PS and conventional execution, and lex_start, used only
when we want to parse something.
Fix init_connect to use initialized THD.
sql/sql_prepare.cc:
Deploy mysql_reset_thd_for_next_query to reset THD state before
execution of a prepared statement.
Normally this should have been added to just one place, but
we have to reset thd before assigning placeholders from variables,
thus we can't do that in execute_stmt (yuck).
Diffstat (limited to 'sql')
-rw-r--r-- | sql/item_func.cc | 34 | ||||
-rw-r--r-- | sql/item_func.h | 6 | ||||
-rw-r--r-- | sql/mysql_priv.h | 1 | ||||
-rw-r--r-- | sql/sql_class.cc | 2 | ||||
-rw-r--r-- | sql/sql_class.h | 12 | ||||
-rw-r--r-- | sql/sql_lex.cc | 23 | ||||
-rw-r--r-- | sql/sql_lex.h | 4 | ||||
-rw-r--r-- | sql/sql_parse.cc | 57 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 7 |
9 files changed, 93 insertions, 53 deletions
diff --git a/sql/item_func.cc b/sql/item_func.cc index ccdde399e0a..f20d69bf2ad 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -1010,21 +1010,38 @@ double Item_func_round::val() } -void Item_func_rand::fix_length_and_dec() +bool Item_func_rand::fix_fields(THD *thd, struct st_table_list *tables, + Item **ref) { - decimals=NOT_FIXED_DEC; - max_length=float_length(decimals); + Item_real_func::fix_fields(thd, tables, ref); used_tables_cache|= RAND_TABLE_BIT; if (arg_count) { // Only use argument once in query - uint32 tmp= (uint32) (args[0]->val_int()); - if ((rand= (struct rand_struct*) sql_alloc(sizeof(*rand)))) - randominit(rand,(uint32) (tmp*0x10001L+55555555L), - (uint32) (tmp*0x10000001L)); + /* + Allocate rand structure once: we must use thd->current_arena + to create rand in proper mem_root if it's a prepared statement or + stored procedure. + */ + if (!rand && !(rand= (struct rand_struct*) + thd->current_arena->alloc(sizeof(*rand)))) + return TRUE; + /* + PARAM_ITEM is returned if we're in statement prepare and consequently + no placeholder value is set yet. + */ + if (args[0]->type() != PARAM_ITEM) + { + /* + TODO: do not do reinit 'rand' for every execute of PS/SP if + args[0] is a constant. + */ + uint32 tmp= (uint32) args[0]->val_int(); + randominit(rand, (uint32) (tmp*0x10001L+55555555L), + (uint32) (tmp*0x10000001L)); + } } else { - THD *thd= current_thd; /* No need to send a Rand log event if seed was given eg: RAND(seed), as it will be replicated in the query as such. @@ -1038,6 +1055,7 @@ void Item_func_rand::fix_length_and_dec() thd->rand_saved_seed2=thd->rand.seed2; rand= &thd->rand; } + return FALSE; } void Item_func_rand::update_used_tables() diff --git a/sql/item_func.h b/sql/item_func.h index 4889ca78a77..e61459cfbd8 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -512,13 +512,13 @@ class Item_func_rand :public Item_real_func { struct rand_struct *rand; public: - Item_func_rand(Item *a) :Item_real_func(a) {} - Item_func_rand() :Item_real_func() {} + Item_func_rand(Item *a) :Item_real_func(a), rand(0) {} + Item_func_rand() :Item_real_func() {} double val(); const char *func_name() const { return "rand"; } bool const_item() const { return 0; } void update_used_tables(); - void fix_length_and_dec(); + bool fix_fields(THD *thd, struct st_table_list *tables, Item **ref); }; diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index e152cd9ea49..1a0879c6347 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -447,6 +447,7 @@ bool is_update_query(enum enum_sql_command command); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); void mysql_init_query(THD *thd, uchar *buf, uint length); +void mysql_reset_thd_for_next_command(THD *thd); bool mysql_new_select(LEX *lex, bool move_down); void create_select_for_variable(const char *var_name); void mysql_init_multi_delete(LEX *lex); diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 4ce655c78f8..f1c75a3b365 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -1498,7 +1498,7 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } -void Statement::end_statement() +void THD::end_statement() { /* Cleanup SQL processing state to resuse this statement in next query. */ lex_end(lex); diff --git a/sql/sql_class.h b/sql/sql_class.h index aaa81fbe165..f25689aa512 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -565,12 +565,6 @@ public: void restore_backup_statement(Statement *stmt, Statement *backup); /* return class type */ virtual Type type() const; - - /* - Cleanup statement parse state (parse tree, lex) after execution of - a non-prepared SQL statement. - */ - void end_statement(); }; @@ -1064,6 +1058,12 @@ public: void nocheck_register_item_tree_change(Item **place, Item *old_value, MEM_ROOT *runtime_memroot); void rollback_item_tree_changes(); + + /* + Cleanup statement parse state (parse tree, lex) and execution + state after execution of a non-prepared SQL statement. + */ + void end_statement(); }; /* Flags for the THD::system_thread (bitmap) variable */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 3deecccb4e1..856742d13bc 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -117,7 +117,30 @@ void lex_free(void) void lex_start(THD *thd, uchar *buf,uint length) { LEX *lex= thd->lex; + lex->unit.init_query(); + lex->unit.init_select(); lex->thd= thd; + lex->unit.thd= thd; + lex->select_lex.init_query(); + lex->value_list.empty(); + lex->param_list.empty(); + lex->unit.next= lex->unit.master= + lex->unit.link_next= lex->unit.return_to= 0; + lex->unit.prev= lex->unit.link_prev= 0; + lex->unit.slave= lex->unit.global_parameters= lex->current_select= + lex->all_selects_list= &lex->select_lex; + lex->select_lex.master= &lex->unit; + lex->select_lex.prev= &lex->unit.slave; + lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; + lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); + lex->select_lex.options= 0; + lex->describe= 0; + lex->derived_tables= FALSE; + lex->lock_option= TL_READ; + lex->found_colon= 0; + lex->safe_to_cache_query= 1; + lex->time_zone_tables_used= 0; + lex->select_lex.select_number= 1; lex->next_state=MY_LEX_START; lex->end_of_query=(lex->ptr=buf)+length; lex->yylineno = 1; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index d198855a2d3..38cdde019ff 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -369,7 +369,7 @@ public: ulong init_prepare_fake_select_lex(THD *thd); int change_result(select_subselect *result, select_subselect *old_result); - friend void mysql_init_query(THD *thd, uchar *buf, uint length); + friend void lex_start(THD *thd, uchar *buf, uint length); friend int subselect_union_engine::exec(); private: bool create_total_list_n_last_return(THD *thd, st_lex *lex, @@ -508,7 +508,7 @@ public: bool test_limit(); - friend void mysql_init_query(THD *thd, uchar *buf, uint length); + friend void lex_start(THD *thd, uchar *buf, uint length); st_select_lex() {} void make_empty_select() { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index c5ca19ecba8..c9c4ed35dc7 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1001,16 +1001,15 @@ pthread_handler_decl(handle_one_connection,arg) net->compress=1; // Use compression thd->version= refresh_version; + thd->proc_info= 0; + thd->set_time(); + thd->init_for_queries(); if (sys_init_connect.value_length && !(thd->master_access & SUPER_ACL)) { execute_init_command(thd, &sys_init_connect, &LOCK_sys_init_connect); if (thd->query_error) thd->killed= 1; } - - thd->proc_info=0; - thd->set_time(); - thd->init_for_queries(); while (!net->error && net->vio != 0 && !thd->killed) { if (do_command(thd)) @@ -3854,7 +3853,6 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, ulong *yystacksize) return 0; } - /**************************************************************************** Initialize global thd variables needed for query ****************************************************************************/ @@ -3863,33 +3861,30 @@ void mysql_init_query(THD *thd, uchar *buf, uint length) { DBUG_ENTER("mysql_init_query"); - LEX *lex= thd->lex; - lex->unit.init_query(); - lex->unit.init_select(); - lex->unit.thd= thd; - lex->select_lex.init_query(); - lex->value_list.empty(); - lex->param_list.empty(); - lex->unit.next= lex->unit.master= - lex->unit.link_next= lex->unit.return_to=0; - lex->unit.prev= lex->unit.link_prev= 0; - lex->unit.slave= lex->unit.global_parameters= lex->current_select= - lex->all_selects_list= &lex->select_lex; - lex->select_lex.master= &lex->unit; - lex->select_lex.prev= &lex->unit.slave; - lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0; - lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list); - lex->select_lex.options=0; - lex->describe= 0; - lex->derived_tables= FALSE; - lex->lock_option= TL_READ; - lex->found_colon= 0; - lex->safe_to_cache_query= 1; - lex->time_zone_tables_used= 0; lex_start(thd, buf, length); - thd->select_number= lex->select_lex.select_number= 1; - thd->free_list= 0; - thd->total_warn_count=0; // Warnings for this query + mysql_reset_thd_for_next_command(thd); + DBUG_VOID_RETURN; +} + + +/* + Reset THD part responsible for command processing state. + + DESCRIPTION + This needs to be called before execution of every statement + (prepared or conventional). + + TODO + Make it a method of THD and align its name with the rest of + reset/end/start/init methods. + Call it after we use THD for queries, not before. +*/ + +void mysql_reset_thd_for_next_command(THD *thd) +{ + DBUG_ENTER("mysql_reset_thd_for_next_command"); + thd->select_number= 1; + thd->total_warn_count= 0; // Warnings for this query thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0; thd->sent_row_count= thd->examined_row_count= 0; thd->is_fatal_error= thd->rand_used= thd->time_zone_used= 0; diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index a638e74dc2f..27d98fdfeba 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1760,6 +1760,8 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) DBUG_VOID_RETURN; } + DBUG_ASSERT(thd->free_list == NULL); + mysql_reset_thd_for_next_command(thd); #ifndef EMBEDDED_LIBRARY if (stmt->param_count) { @@ -1778,7 +1780,6 @@ void mysql_stmt_execute(THD *thd, char *packet, uint packet_length) if (stmt->param_count && stmt->set_params_data(stmt, &expanded_query)) goto set_params_data_err; #endif - DBUG_ASSERT(thd->free_list == NULL); thd->protocol= &thd->protocol_prep; // Switch to binary protocol execute_stmt(thd, stmt, &expanded_query, true); thd->protocol= &thd->protocol_simple; // Use normal protocol @@ -1823,7 +1824,8 @@ void mysql_sql_stmt_execute(THD *thd, LEX_STRING *stmt_name) } DBUG_ASSERT(thd->free_list == NULL); - + /* Must go before setting variables, as it clears thd->user_var_events */ + mysql_reset_thd_for_next_command(thd); thd->set_n_backup_statement(stmt, &thd->stmt_backup); if (stmt->set_params_from_vars(stmt, thd->stmt_backup.lex->prepared_stmt_params, @@ -1932,6 +1934,7 @@ void mysql_stmt_reset(THD *thd, char *packet) */ reset_stmt_params(stmt); + mysql_reset_thd_for_next_command(thd); send_ok(thd); DBUG_VOID_RETURN; |