summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
authorunknown <konstantin@mysql.com>2004-08-24 20:17:11 +0400
committerunknown <konstantin@mysql.com>2004-08-24 20:17:11 +0400
commit49bd559eb8f041de97e4ef55f280f3806d1b6c42 (patch)
treeabcc02d1c616c2795f2ca78dd7032b01d865d0ce /sql
parent83e3d3f9a3ab6888292b8bcb15e0f77f00249289 (diff)
downloadmariadb-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.cc84
-rw-r--r--sql/sql_class.h22
-rw-r--r--sql/sql_insert.cc7
-rw-r--r--sql/sql_lex.cc5
-rw-r--r--sql/sql_lex.h2
-rw-r--r--sql/sql_parse.cc59
-rw-r--r--sql/sql_prepare.cc8
-rw-r--r--sql/sql_select.cc10
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);
}