Commit 77b671f5 authored by pem@mysql.com's avatar pem@mysql.com

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.
parent f0137bcd
......@@ -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)
......
......@@ -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
......
......@@ -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},
......
/* 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;
}
/* -*- 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_ */
......@@ -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)
{
while (! m_backpatch.is_empty())
{
uint *ip= m_backpatch.pop();
sp_instr_jump *i= static_cast<sp_instr_jump *>(get_instr(*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)
sp_head::backpatch()
{
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;
}
sp_instr *ip;
uint dest= instructions();
List_iterator_fast<sp_instr> li(m_backpatch);
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)))
while ((ip= li++))
{
ret= -1;
goto done;
}
restore_record(table, 2); // Get default values for fields
sp_instr_jump *i= static_cast<sp_instr_jump *>(ip);
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));
i->set_destination(dest);
}
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)
{
......
......@@ -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<uint> m_backpatch; // Instructions needing backpaching
List<sp_instr> 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
......
......@@ -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()
{
......
......@@ -28,6 +28,7 @@
#endif
#include "sp_head.h"
#include "sp.h"
#ifdef HAVE_OPENSSL
/*
......
......@@ -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:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment