From b1b62274856c81a5f1f7a49384fc93810fd2a2bd Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 12 Dec 2002 13:14:23 +0100 Subject: [PATCH] Moved create/find/drop functions to a separate files (sp.cc,sp.h). Fixed backpatching of forward jumps. Implemented LOOP, WHILE, REPEAT (temporarily known as SPREPEAT). Known bug: Expression evaluation still not quite ok (e.g. "x > 0"), which is why IF and CASE is not yet implemented. sql/Makefile.am: Added new sp.h/sp.cc file. sql/item.h: New deferred result_type() method in Item_splocal. sql/lex.h: Temporary fix until REPEAT conflict is solved. Use SPREPEAT for now. sql/sp_head.cc: Moved create/find/drop functions to sp.cc. Fixed the backpatch stuff. (Also removed some dead code and updated comments.) sql/sp_head.h: Moved create/find/drop declarations to sp.h. Fixed the backpatch stuff. sql/sp_pcontext.h: New method: last_label(). sql/sql_parse.cc: Include sp.h. sql/sql_yacc.yy: Fixed backpatching of forward jumps. Implemented LOOP, WHILE, and REPEAT. (Note: SPREPEAT for now.) --- sql/Makefile.am | 4 +- sql/item.h | 5 ++ sql/lex.h | 5 +- sql/sp.cc | 119 +++++++++++++++++++++++++++++++++++ sql/sp.h | 33 ++++++++++ sql/sp_head.cc | 155 +++++----------------------------------------- sql/sp_head.h | 24 ++----- sql/sp_pcontext.h | 6 ++ sql/sql_parse.cc | 1 + sql/sql_yacc.yy | 65 ++++++++++++++----- 10 files changed, 239 insertions(+), 178 deletions(-) create mode 100644 sql/sp.cc create mode 100644 sql/sp.h diff --git a/sql/Makefile.am b/sql/Makefile.am index 1cdfc05ffdc..116c094aa04 100644 --- a/sql/Makefile.am +++ b/sql/Makefile.am @@ -58,7 +58,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \ log_event.h mini_client.h sql_repl.h slave.h \ stacktrace.h sql_sort.h sql_cache.h set_var.h \ spatial.h gstream.h sp_head.h sp_pcontext.h \ - sp_rcontext.h + sp_rcontext.h sp.h mysqld_SOURCES = sql_lex.cc sql_handler.cc \ item.cc item_sum.cc item_buff.cc item_func.cc \ item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \ @@ -87,7 +87,7 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \ mini_client.cc mini_client_errors.c \ stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\ gstream.cc spatial.cc sql_help.cc \ - sp_head.cc sp_pcontext.cc + sp_head.cc sp_pcontext.cc sp.cc gen_lex_hash_SOURCES = gen_lex_hash.cc gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS) diff --git a/sql/item.h b/sql/item.h index bffb9212e3d..366eb8f2e32 100644 --- a/sql/item.h +++ b/sql/item.h @@ -126,6 +126,11 @@ public: return m_offset; } + virtual Item_result result_type() const + { + return this_const_item()->result_type(); + } + // Abstract methods inherited from Item. Just defer the call to // the item in the frame inline enum Type type() const diff --git a/sql/lex.h b/sql/lex.h index 829c16a3b74..606bb0eaf6c 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -317,10 +317,10 @@ static SYMBOL symbols[] = { { "REPAIR", SYM(REPAIR),0,0}, { "REPLACE", SYM(REPLACE),0,0}, { "REPLICATION", SYM(REPLICATION),0,0}, + { "SPREPEAT", SYM(SPREPEAT_SYM),0,0}, /* QQ Temp. until conflict solved */ { "REPEATABLE", SYM(REPEATABLE_SYM),0,0}, { "REQUIRE", SYM(REQUIRE_SYM),0,0}, { "RESET", SYM(RESET_SYM),0,0}, - { "UNTIL", SYM(UNTIL_SYM),0,0}, { "USER_RESOURCES", SYM(RESOURCES),0,0}, { "RESTORE", SYM(RESTORE_SYM),0,0}, { "RESTRICT", SYM(RESTRICT),0,0}, @@ -351,7 +351,7 @@ static SYMBOL symbols[] = { { "SONAME", SYM(UDF_SONAME_SYM),0,0}, { "SPATIAL", SYM(SPATIAL_SYM),0,0}, { "SPECIFIC", SYM(SPECIFIC_SYM),0,0}, - { "SPSET", SYM(SPSET_SYM),0,0}, + { "SPSET", SYM(SPSET_SYM),0,0}, /* Temp. until SET parsing solved. */ { "SQL_BIG_RESULT", SYM(SQL_BIG_RESULT),0,0}, { "SQL_BUFFER_RESULT", SYM(SQL_BUFFER_RESULT),0,0}, { "SQL_CACHE", SYM(SQL_CACHE_SYM), 0, 0}, @@ -392,6 +392,7 @@ static SYMBOL symbols[] = { { "UNIQUE", SYM(UNIQUE_SYM),0,0}, { "UNLOCK", SYM(UNLOCK_SYM),0,0}, { "UNSIGNED", SYM(UNSIGNED),0,0}, + { "UNTIL", SYM(UNTIL_SYM),0,0}, { "USE", SYM(USE_SYM),0,0}, { "USE_FRM", SYM(USE_FRM),0,0}, { "USING", SYM(USING),0,0}, diff --git a/sql/sp.cc b/sql/sp.cc new file mode 100644 index 00000000000..caeb01200ff --- /dev/null +++ b/sql/sp.cc @@ -0,0 +1,119 @@ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + + +#include "mysql_priv.h" +#include "sp.h" +#include "sp_head.h" + +// Finds the SP 'name'. Currently this always reads from the database +// and prepares (parse) it, but in the future it will first look in +// the in-memory cache for SPs. (And store newly prepared SPs there of +// course.) +sp_head * +sp_find(THD *thd, Item_string *iname) +{ + extern int yyparse(void *thd); + LEX *tmplex; + TABLE *table; + TABLE_LIST tables; + const char *defstr; + String *name; + sp_head *sp = NULL; + + name = iname->const_string(); + memset(&tables, 0, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.real_name= tables.alias= (char*)"proc"; + if (! (table= open_ltable(thd, &tables, TL_READ))) + return NULL; + + if (table->file->index_read_idx(table->record[0], 0, + (byte*)name->c_ptr(), name->length(), + HA_READ_KEY_EXACT)) + goto done; + + if ((defstr= get_field(&thd->mem_root, table, 1)) == NULL) + goto done; + + // QQ Set up our own mem_root here??? + tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr)); + if (yyparse(thd) || thd->fatal_error || tmplex->sphead == NULL) + goto done; // Error + else + sp = tmplex->sphead; + + done: + if (table) + close_thread_tables(thd); + return sp; +} + +int +sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen) +{ + int ret= 0; + TABLE *table; + TABLE_LIST tables; + + memset(&tables, 0, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.real_name= tables.alias= (char*)"proc"; + /* Allow creation of procedures even if we can't open proc table */ + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + { + ret= -1; + goto done; + } + + restore_record(table, 2); // Get default values for fields + + table->field[0]->store(name, namelen, default_charset_info); + table->field[1]->store(def, deflen, default_charset_info); + + ret= table->file->write_row(table->record[0]); + + done: + close_thread_tables(thd); + return ret; +} + +int +sp_drop(THD *thd, char *name, uint namelen) +{ + TABLE *table; + TABLE_LIST tables; + + tables.db= (char *)"mysql"; + tables.real_name= tables.alias= (char *)"proc"; + if (! (table= open_ltable(thd, &tables, TL_WRITE))) + goto err; + if (! table->file->index_read_idx(table->record[0], 0, + (byte *)name, namelen, + HA_READ_KEY_EXACT)) + { + int error; + + if ((error= table->file->delete_row(table->record[0]))) + table->file->print_error(error, MYF(0)); + } + close_thread_tables(thd); + return 0; + + err: + close_thread_tables(thd); + return -1; +} diff --git a/sql/sp.h b/sql/sp.h new file mode 100644 index 00000000000..cb3343a4e92 --- /dev/null +++ b/sql/sp.h @@ -0,0 +1,33 @@ +/* -*- C++ -*- */ +/* Copyright (C) 2002 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef _SP_H_ +#define _SP_H_ + +// +// Finds a stored procedure given its name. Returns NULL if not found. +// +sp_head * +sp_find(THD *thd, Item_string *name); + +int +sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen); + +int +sp_drop(THD *thd, char *name, uint namelen); + +#endif /* _SP_H_ */ diff --git a/sql/sp_head.cc b/sql/sp_head.cc index ed93f005db3..8025fe3ef4c 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -20,6 +20,7 @@ #include "mysql_priv.h" #include "sp_head.h" +#include "sp.h" #include "sp_pcontext.h" #include "sp_rcontext.h" @@ -31,9 +32,7 @@ eval_func_item(Item *it, enum enum_field_types type) { it= it->this_item(); - /* QQ Which way do we do this? Or is there some even better way? */ -#if 1 - /* QQ Obey the declared type of the variable */ + /* QQ How do we do this? Is there some better way? */ switch (type) { case MYSQL_TYPE_TINY: @@ -77,30 +76,7 @@ eval_func_item(Item *it, enum enum_field_types type) /* QQ Don't know what to do with the rest. */ break; } -#else - /* QQ This looks simpler, but is wrong? It disregards the variable's type. */ - switch (it->result_type()) - { - case REAL_RESULT: - it= new Item_real(it->val()); - break; - case INT_RESULT: - it= new Item_int(it->val_int()); - break; - case STRING_RESULT: - { - char buffer[MAX_FIELD_WIDTH]; - String tmp(buffer, sizeof(buffer), default_charset_info); - (void)it->val_str(&tmp); - it= new Item_string(buffer, sizeof(buffer), default_charset_info); - break; - } - default: - /* QQ Don't know what to do with the rest. */ - break; - } -#endif return it; } @@ -113,6 +89,7 @@ sp_head::sp_head(LEX_STRING *name, LEX *lex) m_defstr= new Item_string(dstr, lex->end_of_query - lex->buf, default_charset_info); my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8); + m_backpatch.empty(); } int @@ -144,7 +121,7 @@ sp_head::execute(THD *thd) Item *it = li++; // Skip first one, it's the procedure name nctx = new sp_rcontext(csize); - // QQ: No error checking whatsoever right now + // QQ: No error checking whatsoever right now. Should do type checking? for (i = 0 ; (it= li++) && i < params ; i++) { sp_pvar_t *pvar = pctx->find_pvar(i); @@ -168,7 +145,7 @@ sp_head::execute(THD *thd) // The rest of the frame are local variables which are all IN. // QQ We haven't found any hint of what the value is when unassigned, // so we set it to NULL for now. It's an error to refer to an - // unassigned variable (which should be detected by the parser). + // unassigned variable anyway (which should be detected by the parser). for (; i < csize ; i++) nctx->push_item(NULL); thd->spcont= nctx; @@ -212,6 +189,7 @@ sp_head::execute(THD *thd) } +// Reset lex during parsing, before we parse a sub statement. void sp_head::reset_lex(THD *thd) { @@ -244,6 +222,7 @@ sp_head::reset_lex(THD *thd) thd->lex.auxilliary_table_list.empty(); } +// Restore lex during parsing, after we have parsed a sub statement. void sp_head::restore_lex(THD *thd) { @@ -255,123 +234,25 @@ sp_head::restore_lex(THD *thd) } void -sp_head::push_backpatch(uint ip) +sp_head::push_backpatch(sp_instr *i) { - (void)m_backpatch.push_front(&ip); + (void)m_backpatch.push_front(i); } void -sp_head::backpatch(uint dest) +sp_head::backpatch() { - while (! m_backpatch.is_empty()) + sp_instr *ip; + uint dest= instructions(); + List_iterator_fast li(m_backpatch); + + while ((ip= li++)) { - uint *ip= m_backpatch.pop(); - sp_instr_jump *i= static_cast(get_instr(*ip)); + sp_instr_jump *i= static_cast(ip); i->set_destination(dest); } -} - - -// ------------------------------------------------------------------ - -// Finds the SP 'name'. Currently this always reads from the database -// and prepares (parse) it, but in the future it will first look in -// the in-memory cache for SPs. (And store newly prepared SPs there of -// course.) -sp_head * -sp_find(THD *thd, Item_string *iname) -{ - extern int yyparse(void *thd); - LEX *tmplex; - TABLE *table; - TABLE_LIST tables; - const char *defstr; - String *name; - sp_head *sp = NULL; - - name = iname->const_string(); - memset(&tables, 0, sizeof(tables)); - tables.db= (char*)"mysql"; - tables.real_name= tables.alias= (char*)"proc"; - if (! (table= open_ltable(thd, &tables, TL_READ))) - return NULL; - - if (table->file->index_read_idx(table->record[0], 0, - (byte*)name->c_ptr(), name->length(), - HA_READ_KEY_EXACT)) - goto done; - - if ((defstr= get_field(&thd->mem_root, table, 1)) == NULL) - goto done; - - // QQ Set up our own mem_root here??? - tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr)); - if (yyparse(thd) || thd->fatal_error || tmplex->sphead == NULL) - goto done; // Error - else - sp = tmplex->sphead; - - done: - if (table) - close_thread_tables(thd); - return sp; -} - -int -sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen) -{ - int ret= 0; - TABLE *table; - TABLE_LIST tables; - - memset(&tables, 0, sizeof(tables)); - tables.db= (char*)"mysql"; - tables.real_name= tables.alias= (char*)"proc"; - /* Allow creation of procedures even if we can't open proc table */ - if (! (table= open_ltable(thd, &tables, TL_WRITE))) - { - ret= -1; - goto done; - } - - restore_record(table, 2); // Get default values for fields - - table->field[0]->store(name, namelen, default_charset_info); - table->field[1]->store(def, deflen, default_charset_info); - - ret= table->file->write_row(table->record[0]); - - done: - close_thread_tables(thd); - return ret; -} - -int -sp_drop(THD *thd, char *name, uint namelen) -{ - TABLE *table; - TABLE_LIST tables; - - tables.db= (char *)"mysql"; - tables.real_name= tables.alias= (char *)"proc"; - if (! (table= open_ltable(thd, &tables, TL_WRITE))) - goto err; - if (! table->file->index_read_idx(table->record[0], 0, - (byte *)name, namelen, - HA_READ_KEY_EXACT)) - { - int error; - - if ((error= table->file->delete_row(table->record[0]))) - table->file->print_error(error, MYF(0)); - } - close_thread_tables(thd); - return 0; - - err: - close_thread_tables(thd); - return -1; + m_backpatch.empty(); } @@ -412,7 +293,6 @@ sp_instr_set::execute(THD *thd, uint *nextp) // // sp_instr_jump_if // - int sp_instr_jump_if::execute(THD *thd, uint *nextp) { @@ -428,7 +308,6 @@ sp_instr_jump_if::execute(THD *thd, uint *nextp) // // sp_instr_jump_if_not // - int sp_instr_jump_if_not::execute(THD *thd, uint *nextp) { diff --git a/sql/sp_head.h b/sql/sp_head.h index 53130bffdf1..87f2b78b9fd 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -73,10 +73,10 @@ public: restore_lex(THD *thd); void - push_backpatch(uint ip); + push_backpatch(sp_instr *i); void - backpatch(uint dest); + backpatch(); private: @@ -85,7 +85,7 @@ private: LEX *m_mylex; // My own lex LEX m_lex; // Temp. store for the other lex DYNAMIC_ARRAY m_instr; // The "instructions" - List m_backpatch; // Instructions needing backpaching + List m_backpatch; // Instructions needing backpaching inline sp_instr * get_instr(uint i) @@ -99,20 +99,6 @@ private: }; // class sp_head : public Sql_alloc -// -// Find a stored procedure given its name. Returns NULL if not -// found. -// -sp_head * -sp_find(THD *thd, Item_string *name); - -int -sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen); - -int -sp_drop(THD *thd, char *name, uint namelen); - - // // "Instructions"... // @@ -243,7 +229,7 @@ public: m_dest= dest; } -private: +protected: int m_dest; // Where we will go @@ -272,7 +258,6 @@ public: private: - int m_dest; // Where we will go Item *m_expr; // The condition }; // class sp_instr_jump_if : public sp_instr_jump @@ -300,7 +285,6 @@ public: private: - int m_dest; // Where we will go Item *m_expr; // The condition }; // class sp_instr_jump_if_not : public sp_instr_jump diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 8a37adcf427..7c8e2ba0c43 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -125,6 +125,12 @@ class sp_pcontext : public Sql_alloc sp_label_t * find_label(char *name); + inline sp_label_t * + last_label() + { + return m_label.head(); + } + inline void pop_label() { diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc index fba726c95b6..ac18b3369ac 100644 --- a/sql/sql_parse.cc +++ b/sql/sql_parse.cc @@ -28,6 +28,7 @@ #endif #include "sp_head.h" +#include "sp.h" #ifdef HAVE_OPENSSL /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index bc19560ea77..30f9fe97ec5 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -531,6 +531,8 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token ITERATE_SYM %token LEAVE_SYM %token LOOP_SYM +/* QQ This is temporary, until the REPEAT conflict is solved. */ +%token SPREPEAT_SYM %token UNTIL_SYM %token WHILE_SYM %token ASENSITIVE_SYM @@ -1053,8 +1055,10 @@ sp_proc_stmt: } sp_unlabeled_control { - /* QQ backpatch here */ - Lex->spcont->pop_label(); + LEX *lex= Lex; + + lex->spcont->pop_label(); + lex->sphead->backpatch(); } | LEAVE_SYM IDENT { @@ -1068,10 +1072,9 @@ sp_proc_stmt: } else { - uint ip= lex->sphead->instructions(); - sp_instr_jump *i= new sp_instr_jump(ip, 0); + sp_instr_jump *i= new sp_instr_jump(lex->sphead->instructions()); - lex->sphead->push_backpatch(ip); + lex->sphead->push_backpatch(i); /* Jumping forward */ lex->sphead->add_instr(i); } } @@ -1088,7 +1091,7 @@ sp_proc_stmt: else { uint ip= lex->sphead->instructions(); - sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); + sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); /* Jump back */ lex->sphead->add_instr(i); } @@ -1148,29 +1151,59 @@ sp_labeled_control: } else { - /* QQ backpatch here */ lex->spcont->pop_label(); + lex->sphead->backpatch(); } } ; sp_unlabeled_control: - begin + BEGIN_SYM sp_decls sp_proc_stmts END - { + { /* QQ This is just a dummy for grouping declarations and statements + together. No [[NOT] ATOMIC] yet, and we need to figure out how + make it coexist with the existing BEGIN COMMIT/ROLLBACK. */ Lex->spcont->pop($2); } | LOOP_SYM - sp_proc_stmts - END LOOP_SYM + sp_proc_stmts END LOOP_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lab->ip); + + lex->sphead->add_instr(i); + } | WHILE_SYM expr DO_SYM - sp_proc_stmts - END WHILE_SYM - | FUNC_ARG2 /* "REPEAT" actually... */ - sp_proc_stmts - UNTIL_SYM expr END FUNC_ARG2 + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $2); + + lex->sphead->push_backpatch(i); /* Jumping forward */ + lex->sphead->add_instr(i); + } + sp_proc_stmts END WHILE_SYM + { + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump *i = new sp_instr_jump(ip, lab->ip); + + lex->sphead->add_instr(i); + } + | SPREPEAT_SYM sp_proc_stmts UNTIL_SYM expr END SPREPEAT_SYM + { /* ^^ QQ temp. until conflict solved ^^ */ + LEX *lex= Lex; + uint ip= lex->sphead->instructions(); + sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */ + sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $4, lab->ip); + + lex->sphead->add_instr(i); + } ; create2: