diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index 29200a439756d97d169a755ad936e90b93b1844b..c08d252cfe30cdc50d7152b5fa8cedbe7c171a21 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -95,6 +95,23 @@ delete from t1; drop procedure zip; drop procedure zap; drop procedure bar; +create procedure c1(x int) +call c2("c", x); +create procedure c2(s char(16), x int) +call c3(x, s); +create procedure c3(x int, s char(16)) +call c4("level", x, s); +create procedure c4(l char(8), x int, s char(16)) +insert into t1 values (concat(l,s), x); +call c1(42); +select * from t1; +id data +levelc 42 +delete from t1; +drop procedure c1; +drop procedure c2; +drop procedure c3; +drop procedure c4; create procedure iotest(x1 char(16), x2 char(16), y int) begin call inc2(x2, y); diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index c501501b82fc7da8843c3ed13801751800211419..61519c529290d4699eb56c83f72541689eb47184 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -126,6 +126,24 @@ drop procedure zap| drop procedure bar| +# "Deep" calls... +create procedure c1(x int) + call c2("c", x)| +create procedure c2(s char(16), x int) + call c3(x, s)| +create procedure c3(x int, s char(16)) + call c4("level", x, s)| +create procedure c4(l char(8), x int, s char(16)) + insert into t1 values (concat(l,s), x)| + +call c1(42)| +select * from t1| +delete from t1| +drop procedure c1| +drop procedure c2| +drop procedure c3| +drop procedure c4| + # INOUT test create procedure iotest(x1 char(16), x2 char(16), y int) begin diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 64b84858282a6b34260fea89c0c94ee13cbc4b2d..57a7fd0f55351e061140f8bf4f6526cb64927abc 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -353,7 +353,7 @@ bool is_update_query(enum enum_sql_command command); void free_items(Item *item); bool alloc_query(THD *thd, char *packet, ulong packet_length); void mysql_init_select(LEX *lex); -void mysql_init_query(THD *thd); +void mysql_init_query(THD *thd, bool lexonly=0); 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/sp.cc b/sql/sp.cc index b1b73f308119cbbf27503cc7069555f553ad716a..5c87488bcda1eca9f7ed9b41115d4850a0796c80 100644 --- a/sql/sp.cc +++ b/sql/sp.cc @@ -88,7 +88,6 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) DBUG_ENTER("db_find_routine"); DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name)); extern int yyparse(void *thd); - LEX *tmplex; TABLE *table; const char *defstr; int ret; @@ -146,15 +145,29 @@ db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp) table= NULL; } - tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr)); - if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL) - ret= SP_PARSE_ERROR; - else { - *sphp= tmplex->sphead; - (*sphp)->sp_set_info((char *) creator, (uint) strlen(creator), - created, modified, suid, - ptr, length); + LEX *oldlex= thd->lex; + enum enum_sql_command oldcmd= thd->lex->sql_command; + + lex_start(thd, (uchar*)defstr, strlen(defstr)); + if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL) + { + if (thd->lex->sphead) + { + if (oldlex != thd->lex) + thd->lex->sphead->restore_lex(thd); + thd->lex->sphead->destroy(); + } + ret= SP_PARSE_ERROR; + } + else + { + *sphp= thd->lex->sphead; + (*sphp)->sp_set_info((char *) creator, (uint) strlen(creator), + created, modified, suid, + ptr, length); + } + thd->lex->sql_command= oldcmd; } done: diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 15a85173f8f1e7720a2360a048fdcdb2a652e8c5..96624430307b4562c8638b0ccc961ec91aa28a33 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -365,54 +365,44 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) void sp_head::reset_lex(THD *thd) { - memcpy(&m_lex, thd->lex, sizeof(LEX)); // Save old one + DBUG_ENTER("sp_head::reset_lex"); + LEX *sublex; + + m_lex= thd->lex; + thd->lex= sublex= new st_lex; + sublex->yylineno= m_lex->yylineno; /* Reset most stuff. The length arguments doesn't matter here. */ - lex_start(thd, m_lex.buf, m_lex.end_of_query - m_lex.ptr); + lex_start(thd, m_lex->buf, m_lex->end_of_query - m_lex->ptr); /* We must reset ptr and end_of_query again */ - thd->lex->ptr= m_lex.ptr; - thd->lex->end_of_query= m_lex.end_of_query; + sublex->ptr= m_lex->ptr; + sublex->end_of_query= m_lex->end_of_query; /* And keep the SP stuff too */ - thd->lex->sphead = m_lex.sphead; - thd->lex->spcont = m_lex.spcont; - /* Clear all lists. (QQ Why isn't this reset by lex_start()?). - We may be overdoing this, but we know for sure that value_list must - be cleared at least. */ - thd->lex->col_list.empty(); - thd->lex->ref_list.empty(); - thd->lex->drop_list.empty(); - thd->lex->alter_list.empty(); - thd->lex->interval_list.empty(); - thd->lex->users_list.empty(); - thd->lex->columns.empty(); - thd->lex->key_list.empty(); - thd->lex->create_list.empty(); - thd->lex->insert_list= NULL; - thd->lex->field_list.empty(); - thd->lex->value_list.empty(); - thd->lex->many_values.empty(); - thd->lex->var_list.empty(); - thd->lex->param_list.empty(); - thd->lex->proc_list.empty(); - thd->lex->auxilliary_table_list.empty(); + sublex->sphead= m_lex->sphead; + sublex->spcont= m_lex->spcont; + mysql_init_query(thd, true); // Only init lex + DBUG_VOID_RETURN; } // Restore lex during parsing, after we have parsed a sub statement. void sp_head::restore_lex(THD *thd) { + DBUG_ENTER("sp_head::restore_lex"); + LEX *sublex= thd->lex; + // Update some state in the old one first - m_lex.ptr= thd->lex->ptr; - m_lex.next_state= thd->lex->next_state; + m_lex->ptr= sublex->ptr; + m_lex->next_state= sublex->next_state; // Collect some data from the sub statement lex. - sp_merge_funs(&m_lex, thd->lex); + sp_merge_funs(m_lex, sublex); #if 0 // QQ We're not using this at the moment. - if (thd->lex.sql_command == SQLCOM_CALL) + if (sublex.sql_command == SQLCOM_CALL) { // It would be slightly faster to keep the list sorted, but we need // an "insert before" method to do that. - char *proc= thd->lex.udf.name.str; + char *proc= sublex.udf.name.str; List_iterator_fast<char *> li(m_calls); char **it; @@ -428,7 +418,7 @@ sp_head::restore_lex(THD *thd) // QQ ...or just open tables in thd->open_tables? // This is not entirerly clear at the moment, but for now, we collect // tables here. - for (SELECT_LEX *sl= thd->lex.all_selects_list ; + for (SELECT_LEX *sl= sublex.all_selects_list ; sl ; sl= sl->next_select()) { @@ -448,7 +438,8 @@ sp_head::restore_lex(THD *thd) } #endif - memcpy(thd->lex, &m_lex, sizeof(LEX)); // Restore lex + thd->lex= m_lex; + DBUG_VOID_RETURN; } void @@ -490,14 +481,14 @@ int sp_instr_stmt::execute(THD *thd, uint *nextp) { DBUG_ENTER("sp_instr_stmt::execute"); - DBUG_PRINT("info", ("command: %d", m_lex.sql_command)); - LEX olex; // The other lex + DBUG_PRINT("info", ("command: %d", m_lex->sql_command)); + LEX *olex; // The other lex int res; - memcpy(&olex, thd->lex, sizeof(LEX)); // Save the other lex - - memcpy(thd->lex, &m_lex, sizeof(LEX)); // Use my own lex - thd->lex->thd = thd; + olex= thd->lex; // Save the other lex + thd->lex= m_lex; // Use my own lex + thd->lex->thd = thd; // QQ Not reentrant! + thd->lex->unit.thd= thd; // QQ Not reentrant res= mysql_execute_command(thd); if (thd->lock || thd->open_tables || thd->derived_tables) @@ -506,7 +497,7 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) close_thread_tables(thd); /* Free tables */ } - memcpy(thd->lex, &olex, sizeof(LEX)); // Restore the other lex + thd->lex= olex; // Restore the other lex *nextp = m_ip+1; DBUG_RETURN(res); diff --git a/sql/sp_head.h b/sql/sp_head.h index d5bf7138785580ac914d43fea779729a5e824ec2..f25e141cd1850afdce29207a97714800b59be2b3 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -136,7 +136,7 @@ class sp_head : public Sql_alloc bool m_suid; sp_pcontext *m_pcont; // Parse context - LEX m_lex; // Temp. store for the other lex + LEX *m_lex; // Temp. store for the other lex DYNAMIC_ARRAY m_instr; // The "instructions" typedef struct { @@ -222,18 +222,18 @@ class sp_instr_stmt : public sp_instr inline void set_lex(LEX *lex) { - memcpy(&m_lex, lex, sizeof(LEX)); + m_lex= lex; } inline LEX * get_lex() { - return &m_lex; + return m_lex; } private: - LEX m_lex; // My own lex + LEX *m_lex; // My own lex }; // class sp_instr_stmt : public sp_instr diff --git a/sql/sql_lex.h b/sql/sql_lex.h index ecf04c1575c033da1d2a854b141894e3dc9ed289..09cbe107ffaaf75e1d949807f48d734b471ca100 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -320,7 +320,7 @@ class st_select_lex_unit: public st_select_lex_node { int exec(); int cleanup(); - friend void mysql_init_query(THD *thd); + friend void mysql_init_query(THD *thd, bool lexonly); friend int subselect_union_engine::exec(); private: bool create_total_list_n_last_return(THD *thd, st_lex *lex, @@ -408,7 +408,7 @@ class st_select_lex: public st_select_lex_node order_list.next= (byte**) &order_list.first; } - friend void mysql_init_query(THD *thd); + friend void mysql_init_query(THD *thd, bool lexonly); st_select_lex(struct st_lex *lex); st_select_lex() {} void make_empty_select(st_select_lex *last_select) diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index d1b9fecee3518f3175c5871db5342fb3c62cadb4..e434524957e138896228cf34de2ccec9de96a497 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -3433,7 +3433,7 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize) ****************************************************************************/ void -mysql_init_query(THD *thd) +mysql_init_query(THD *thd, bool lexonly) { DBUG_ENTER("mysql_init_query"); LEX *lex=thd->lex; @@ -3457,17 +3457,20 @@ mysql_init_query(THD *thd) lex->lock_option= TL_READ; lex->found_colon= 0; lex->safe_to_cache_query= 1; - thd->select_number= lex->select_lex.select_number= 1; - thd->free_list= 0; - 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= 0; - thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; - thd->tmp_table_used= 0; - if (opt_bin_log) - reset_dynamic(&thd->user_var_events); - thd->clear_error(); + if (! lexonly) + { + thd->select_number= lex->select_lex.select_number= 1; + thd->free_list= 0; + 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= 0; + thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS; + thd->tmp_table_used= 0; + if (opt_bin_log) + reset_dynamic(&thd->user_var_events); + thd->clear_error(); + } DBUG_VOID_RETURN; } @@ -3582,7 +3585,11 @@ mysql_parse(THD *thd, char *inBuf, uint length) else { if (thd->net.report_error) + { send_error(thd, 0, NullS); + if (thd->lex->sphead) + thd->lex->sphead->destroy(); + } else { mysql_execute_command(thd); @@ -3598,8 +3605,12 @@ mysql_parse(THD *thd, char *inBuf, uint length) thd->is_fatal_error)); #ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/ query_cache_abort(&thd->net); + if (thd->lex->sphead) + thd->lex->sphead->destroy(); #endif } + if (thd->lex->sphead && lex != thd->lex) + thd->lex->sphead->restore_lex(thd); thd->proc_info="freeing items"; free_items(thd->free_list); /* Free strings used by items */ lex_end(lex); diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc index 43376012d4c369a9a05970029eef3f2bcb965ba5..bd0a3a094222a9a586b07f6a4e905b339edececc 100644 --- a/sql/sql_prepare.cc +++ b/sql/sql_prepare.cc @@ -71,6 +71,7 @@ Long data handling: #include "sql_acl.h" #include "sql_select.h" // for JOIN #include <m_ctype.h> // for isspace() +#include "sp_head.h" #define IS_PARAM_NULL(pos, param_no) pos[param_no/8] & (1 << param_no & 7) @@ -761,6 +762,8 @@ static bool parse_prepare_query(PREP_STMT *stmt, thd->lex->param_count= 0; if (!yyparse((void *)thd) && !thd->is_fatal_error) error= send_prepare_results(stmt); + if (thd->lex->sphead && lex != thd->lex) + thd->lex->sphead->restore_lex(thd); lex_end(lex); DBUG_RETURN(error); }