diff options
author | unknown <konstantin@mysql.com> | 2004-08-24 20:17:11 +0400 |
---|---|---|
committer | unknown <konstantin@mysql.com> | 2004-08-24 20:17:11 +0400 |
commit | 49bd559eb8f041de97e4ef55f280f3806d1b6c42 (patch) | |
tree | abcc02d1c616c2795f2ca78dd7032b01d865d0ce /sql | |
parent | 83e3d3f9a3ab6888292b8bcb15e0f77f00249289 (diff) | |
download | mariadb-git-49bd559eb8f041de97e4ef55f280f3806d1b6c42.tar.gz |
Fix for Bug#5034 "prepared "select 1 into @arg15", second
execute crashes server": we were deleting lex->result
after each execute, but prepared statements assumed that
it's left intact.
The fix adds cleanup() method to select_result hierarchy,
so that result objects can be reused.
Plus we now need to delete result objects more wisely.
mysql-test/r/ps.result:
Test results fixed: test case for bug#5034
mysql-test/t/ps.test:
A test case for bug#5034, few followups
sql/sql_class.cc:
- fix warning in THD::THD
- implementation of cleanup() for select_result hierarchy
- select_export::send_eof was identical to
select_dump::send_eof: moved to the base class select_to_file.
- Statement::end_statement() to end lex, free items, and
delete possible select_result
sql/sql_class.h:
- select_result::cleanup() declaration
-
sql/sql_insert.cc:
- implementation of select_insert::cleanup(): currently
we always create a new instance of select_insert/
select_create on each execute.
sql/sql_lex.cc:
- with more complicated logic of freeing lex->result it's
easier to have it non-zero only if it points to a valid
result.
sql/sql_lex.h:
Now st_lex::st_lex is not empty.
sql/sql_parse.cc:
mysql_execute_command():
- delete select_result *result only if it was created in
this function.
- use end_statement() to cleanup lex and thd in the end of
each statement.
- no need to save THD::lock if this is explain. This save
apparently left from times when derived tables were
materialized here, not in open_and_lock_tables.
sql/sql_prepare.cc:
- call result->cleanup() in reset_stmt_for_execute
- now Statement is responsible for freeing its lex->result.
sql/sql_select.cc:
handle_select():
- don't delete result, it might be needed
for next executions
- result is never null
Diffstat (limited to 'sql')
-rw-r--r-- | sql/sql_class.cc | 84 | ||||
-rw-r--r-- | sql/sql_class.h | 22 | ||||
-rw-r--r-- | sql/sql_insert.cc | 7 | ||||
-rw-r--r-- | sql/sql_lex.cc | 5 | ||||
-rw-r--r-- | sql/sql_lex.h | 2 | ||||
-rw-r--r-- | sql/sql_parse.cc | 59 | ||||
-rw-r--r-- | sql/sql_prepare.cc | 8 | ||||
-rw-r--r-- | sql/sql_select.cc | 10 |
8 files changed, 123 insertions, 74 deletions
diff --git a/sql/sql_class.cc b/sql/sql_class.cc index 23fef44c964..07df83b78e7 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -155,10 +155,10 @@ bool foreign_key_prefix(Key *a, Key *b) ** Thread specific functions ****************************************************************************/ -THD::THD():user_time(0), current_arena(this), is_fatal_error(0), - last_insert_id_used(0), +THD::THD():user_time(0), current_arena(this), global_read_lock(0), + is_fatal_error(0), last_insert_id_used(0), insert_id_used(0), rand_used(0), time_zone_used(0), - in_lock_tables(0), global_read_lock(0), bootstrap(0) + in_lock_tables(0), bootstrap(0) { host= user= priv_user= db= ip=0; host_or_ip= "connecting host"; @@ -703,6 +703,12 @@ void select_result::send_error(uint errcode,const char *err) ::send_error(thd, errcode, err); } + +void select_result::cleanup() +{ + /* do nothing */ +} + static String default_line_term("\n",default_charset_info); static String default_escaped("\\",default_charset_info); static String default_field_term("\t",default_charset_info); @@ -808,6 +814,32 @@ void select_to_file::send_error(uint errcode,const char *err) } +bool select_to_file::send_eof() +{ + int error= test(end_io_cache(&cache)); + if (my_close(file,MYF(MY_WME))) + error= 1; + if (!error) + ::send_ok(thd,row_count); + file= -1; + return error; +} + + +void select_to_file::cleanup() +{ + /* In case of error send_eof() may be not called: close the file here. */ + if (file >= 0) + { + (void) end_io_cache(&cache); + (void) my_close(file,MYF(0)); + file= -1; + } + path[0]= '\0'; + row_count= 0; +} + + select_to_file::~select_to_file() { if (file >= 0) @@ -1058,18 +1090,6 @@ err: } -bool select_export::send_eof() -{ - int error=test(end_io_cache(&cache)); - if (my_close(file,MYF(MY_WME))) - error=1; - if (!error) - ::send_ok(thd,row_count); - file= -1; - return error; -} - - /*************************************************************************** ** Dump of select to a binary file ***************************************************************************/ @@ -1123,18 +1143,6 @@ err: } -bool select_dump::send_eof() -{ - int error=test(end_io_cache(&cache)); - if (my_close(file,MYF(MY_WME))) - error=1; - if (!error) - ::send_ok(thd,row_count); - file= -1; - return error; -} - - select_subselect::select_subselect(Item_subselect *item_arg) { item= item_arg; @@ -1301,6 +1309,13 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u) } +void select_dumpvar::cleanup() +{ + vars.empty(); + row_count=0; +} + + Item_arena::Item_arena(THD* thd) :free_list(0), state(INITIALIZED) @@ -1405,6 +1420,21 @@ void Statement::restore_backup_statement(Statement *stmt, Statement *backup) } +void Statement::end_statement() +{ + /* Cleanup SQL processing state to resuse this statement in next query. */ + lex_end(lex); + delete lex->result; + lex->result= 0; + free_items(free_list); + free_list= 0; + /* + Don't free mem_root, as mem_root is freed in the end of dispatch_command + (once for any command). + */ +} + + void Item_arena::set_n_backup_item_arena(Item_arena *set, Item_arena *backup) { backup->set_item_arena(this); diff --git a/sql/sql_class.h b/sql/sql_class.h index 83fdb4c7d76..c18bf969ab9 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -547,6 +547,12 @@ 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(); }; @@ -1029,10 +1035,13 @@ public: ~Disable_binlog(); }; + /* Used to hold information about file and file structure in exchainge via non-DB file (...INTO OUTFILE..., ...LOAD DATA...) + XXX: We never call destructor for objects of this class. */ + class sql_exchange :public Sql_alloc { public: @@ -1042,7 +1051,6 @@ public: bool dumpfile; ulong skip_lines; sql_exchange(char *name,bool dumpfile_flag); - ~sql_exchange() {} }; #include "log_event.h" @@ -1073,6 +1081,11 @@ public: virtual void send_error(uint errcode,const char *err); virtual bool send_eof()=0; virtual void abort() {} + /* + Cleanup instance of this class for next execution of a prepared + statement/stored procedure. + */ + virtual void cleanup(); }; @@ -1099,6 +1112,8 @@ public: ~select_to_file(); bool send_fields(List<Item> &list, uint flag) { return 0; } void send_error(uint errcode,const char *err); + bool send_eof(); + void cleanup(); }; @@ -1111,7 +1126,6 @@ public: ~select_export(); int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); - bool send_eof(); }; @@ -1120,7 +1134,6 @@ public: select_dump(sql_exchange *ex) :select_to_file(ex) {} int prepare(List<Item> &list, SELECT_LEX_UNIT *u); bool send_data(List<Item> &items); - bool send_eof(); }; @@ -1145,6 +1158,8 @@ class select_insert :public select_result { bool send_data(List<Item> &items); void send_error(uint errcode,const char *err); bool send_eof(); + /* not implemented: select_insert is never re-used in prepared statements */ + void cleanup(); }; @@ -1445,4 +1460,5 @@ public: bool send_fields(List<Item> &list, uint flag) {return 0;} bool send_data(List<Item> &items); bool send_eof(); + void cleanup(); }; diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 406bff6d273..4cbd11c6a15 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -1465,6 +1465,13 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) DBUG_RETURN(0); } + +void select_insert::cleanup() +{ + /* select_insert/select_create are never re-used in prepared statement */ + DBUG_ASSERT(0); +} + select_insert::~select_insert() { if (table) diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index be1b7c3377e..d83c5ba5778 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1653,6 +1653,11 @@ void st_select_lex::print_limit(THD *thd, String *str) } +st_lex::st_lex() + :result(0) +{} + + /* Unlink first table from global table list and first table from outer select list (lex->select_lex) diff --git a/sql/sql_lex.h b/sql/sql_lex.h index 053c85166f6..9c7918a400f 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -639,7 +639,7 @@ typedef struct st_lex list of those tables after they are opened. */ TABLE_LIST *time_zone_tables_used; - st_lex() {} + st_lex(); inline void uncacheable(uint8 cause) { safe_to_cache_query= 0; diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index 66eebba74c9..f9e979d1fdf 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -1967,8 +1967,6 @@ mysql_execute_command(THD *thd) else thd->send_explain_fields(result); res= mysql_explain_union(thd, &thd->lex->unit, result); - MYSQL_LOCK *save_lock= thd->lock; - thd->lock= (MYSQL_LOCK *)0; if (lex->describe & DESCRIBE_EXTENDED) { char buff[1024]; @@ -1980,20 +1978,19 @@ mysql_execute_command(THD *thd) ER_YES, str.ptr()); } result->send_eof(); - thd->lock= save_lock; + delete result; } else { - if (!result) + if (!result && !(result= new select_send())) { - if (!(result=new select_send())) - { - res= -1; - break; - } + res= -1; + break; } query_cache_store_query(thd, tables); - res=handle_select(thd, lex, result); + res= handle_select(thd, lex, result); + if (result != lex->result) + delete result; } } break; @@ -2708,23 +2705,24 @@ unsent_create_error: } - if (!(res=open_and_lock_tables(thd, tables))) + if (!(res= open_and_lock_tables(thd, tables)) && + (result= new select_insert(tables->table, &lex->field_list, + lex->duplicates))) { - if ((result=new select_insert(tables->table,&lex->field_list, - lex->duplicates))) - /* Skip first table, which is the table we are inserting in */ - lex->select_lex.table_list.first= (byte*) first_local_table->next; - /* - insert/replace from SELECT give its SELECT_LEX for SELECT, - and item_list belong to SELECT - */ - lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; - res=handle_select(thd,lex,result); - /* revert changes for SP */ - lex->select_lex.table_list.first= (byte*) first_local_table; - lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; + /* Skip first table, which is the table we are inserting in */ + lex->select_lex.table_list.first= (byte*) first_local_table->next; + /* + insert/replace from SELECT give its SELECT_LEX for SELECT, + and item_list belong to SELECT + */ + lex->select_lex.resolve_mode= SELECT_LEX::SELECT_MODE; + res= handle_select(thd, lex, result); + /* revert changes for SP */ + lex->select_lex.table_list.first= (byte*) first_local_table; + lex->select_lex.resolve_mode= SELECT_LEX::INSERT_MODE; + delete result; if (thd->net.report_error) - res= -1; + res= -1; } else res= -1; @@ -3904,8 +3902,8 @@ mysql_init_select(LEX *lex) select_lex->select_limit= HA_POS_ERROR; if (select_lex == &lex->select_lex) { + DBUG_ASSERT(lex->result == 0); lex->exchange= 0; - lex->result= 0; lex->proc_list.first= 0; } } @@ -4047,9 +4045,7 @@ void mysql_parse(THD *thd, char *inBuf, uint length) query_cache_abort(&thd->net); } thd->proc_info="freeing items"; - free_items(thd->free_list); /* Free strings used by items */ - thd->free_list= 0; - lex_end(lex); + thd->end_statement(); } DBUG_VOID_RETURN; } @@ -4074,10 +4070,7 @@ bool mysql_test_parse_for_slave(THD *thd, char *inBuf, uint length) if (!yyparse((void*) thd) && ! thd->is_fatal_error && all_tables_not_ok(thd,(TABLE_LIST*) lex->select_lex.table_list.first)) error= 1; /* Ignore question */ - free_items(thd->free_list); /* Free strings used by items */ - thd->free_list= 0; - lex_end(lex); - + thd->end_statement(); return error; } #endif diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 850d41a030b..6d494b83535 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -1630,7 +1630,8 @@ int mysql_stmt_prepare(THD *thd, char *packet, uint packet_length, static void reset_stmt_for_execute(Prepared_statement *stmt) { THD *thd= stmt->thd; - SELECT_LEX *sl= stmt->lex->all_selects_list; + LEX *lex= stmt->lex; + SELECT_LEX *sl= lex->all_selects_list; for (; sl; sl= sl->next_select_in_list()) { @@ -1678,7 +1679,9 @@ static void reset_stmt_for_execute(Prepared_statement *stmt) unit->reinit_exec_mechanism(); } } - stmt->lex->current_select= &stmt->lex->select_lex; + lex->current_select= &lex->select_lex; + if (lex->result) + lex->result->cleanup(); } @@ -2053,6 +2056,7 @@ void Prepared_statement::setup_set_params() Prepared_statement::~Prepared_statement() { free_items(free_list); + delete lex->result; } diff --git a/sql/sql_select.cc b/sql/sql_select.cc index fdea963b3ca..34ee7139d71 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -199,16 +199,10 @@ int handle_select(THD *thd, LEX *lex, select_result *result) res= 1; if (res) { - if (result) - { - result->send_error(0, NullS); - result->abort(); - } - else - send_error(thd, 0, NullS); + result->send_error(0, NullS); + result->abort(); res= 1; // Error sent to client } - delete result; DBUG_RETURN(res); } |