summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/Makefile.am6
-rw-r--r--sql/item.cc19
-rw-r--r--sql/item.h53
-rw-r--r--sql/lex.h6
-rw-r--r--sql/sp_head.cc384
-rw-r--r--sql/sp_head.h200
-rw-r--r--sql/sp_pcontext.cc91
-rw-r--r--sql/sp_pcontext.h127
-rw-r--r--sql/sp_rcontext.h82
-rw-r--r--sql/sql_class.cc2
-rw-r--r--sql/sql_class.h2
-rw-r--r--sql/sql_lex.cc3
-rw-r--r--sql/sql_lex.h8
-rw-r--r--sql/sql_parse.cc80
-rw-r--r--sql/sql_yacc.yy249
15 files changed, 1302 insertions, 10 deletions
diff --git a/sql/Makefile.am b/sql/Makefile.am
index c5af51e8397..1cdfc05ffdc 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -57,7 +57,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
lex.h lex_symbol.h sql_acl.h sql_crypt.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
+ spatial.h gstream.h sp_head.h sp_pcontext.h \
+ sp_rcontext.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 \
@@ -85,7 +86,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.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
+ gstream.cc spatial.cc sql_help.cc \
+ sp_head.cc sp_pcontext.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
diff --git a/sql/item.cc b/sql/item.cc
index f21cd0fc0f3..867b7f2174e 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -22,6 +22,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
+#include "sp_rcontext.h"
/*****************************************************************************
** Item functions
@@ -147,6 +148,24 @@ CHARSET_INFO * Item::thd_charset() const
return current_thd->thd_charset;
}
+
+Item *
+Item_splocal::this_item()
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item *
+Item_splocal::this_const_item() const
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+
Item_field::Item_field(Field *f) :Item_ident(NullS,f->table_name,f->field_name)
{
set_field(f);
diff --git a/sql/item.h b/sql/item.h
index a189789ba24..bffb9212e3d 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -94,6 +94,8 @@ public:
CHARSET_INFO *thd_charset() const;
CHARSET_INFO *charset() const { return str_value.charset(); };
void set_charset(CHARSET_INFO *cs) { str_value.set_charset(cs); }
+ virtual Item *this_item() { return this; } /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
// Row emulation
virtual uint cols() { return 1; }
@@ -103,6 +105,57 @@ public:
};
+// A local SP variable (incl. parameters), used in runtime
+class Item_splocal : public Item
+{
+private:
+
+ uint m_offset;
+
+public:
+
+ Item_splocal(uint offset)
+ : m_offset(offset)
+ {}
+
+ Item *this_item();
+ Item *this_const_item() const;
+
+ inline uint get_offset()
+ {
+ return m_offset;
+ }
+
+ // Abstract methods inherited from Item. Just defer the call to
+ // the item in the frame
+ inline enum Type type() const
+ {
+ return this_const_item()->type();
+ }
+
+ inline double val()
+ {
+ return this_item()->val();
+ }
+
+ inline longlong val_int()
+ {
+ return this_item()->val_int();
+ }
+
+ inline String *val_str(String *sp)
+ {
+ return this_item()->val_str(sp);
+ }
+
+ inline void make_field(Send_field *field)
+ {
+ this_item()->make_field(field);
+ }
+
+};
+
+
/*
Wrapper base class
*/
diff --git a/sql/lex.h b/sql/lex.h
index eb03c0b36ec..bce68540b55 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -79,6 +79,7 @@ static SYMBOL symbols[] = {
{ "BY", SYM(BY),0,0},
{ "BYTE", SYM(BYTE_SYM), 0, 0},
{ "CACHE", SYM(CACHE_SYM),0,0},
+ { "CALL", SYM(CALL_SYM),0,0},
{ "CASCADE", SYM(CASCADE),0,0},
{ "CASE", SYM(CASE_SYM),0,0},
{ "CHAR", SYM(CHAR_SYM),0,0},
@@ -117,6 +118,7 @@ static SYMBOL symbols[] = {
{ "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0},
{ "DEC", SYM(DECIMAL_SYM),0,0},
{ "DECIMAL", SYM(DECIMAL_SYM),0,0},
+ { "DECLARE", SYM(DECLARE_SYM),0,0},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0},
{ "DEFAULT", SYM(DEFAULT),0,0},
{ "DELAYED", SYM(DELAYED_SYM),0,0},
@@ -193,6 +195,7 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM),0,0},
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
+ { "INOUT", SYM(INOUT_SYM),0,0},
{ "INSERT", SYM(INSERT),0,0},
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
{ "INT", SYM(INT_SYM),0,0},
@@ -279,6 +282,7 @@ static SYMBOL symbols[] = {
{ "OPTIONALLY", SYM(OPTIONALLY),0,0},
{ "OR", SYM(OR),0,0},
{ "ORDER", SYM(ORDER_SYM),0,0},
+ { "OUT", SYM(OUT_SYM),0,0},
{ "OUTER", SYM(OUTER),0,0},
{ "OUTFILE", SYM(OUTFILE),0,0},
{ "PACK_KEYS", SYM(PACK_KEYS_SYM),0,0},
@@ -337,6 +341,8 @@ static SYMBOL symbols[] = {
{ "SOME", SYM(ANY_SYM),0,0},
{ "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},
{ "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},
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
new file mode 100644
index 00000000000..fcf3652095d
--- /dev/null
+++ b/sql/sp_head.cc
@@ -0,0 +1,384 @@
+/* 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 */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+
+/* Evaluate a (presumed) func item. Always returns an item, the parameter
+** if nothing else.
+*/
+static Item *
+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 */
+ switch (type)
+ {
+ case MYSQL_TYPE_TINY:
+ case MYSQL_TYPE_SHORT:
+ case MYSQL_TYPE_LONG:
+ case MYSQL_TYPE_LONGLONG:
+ case MYSQL_TYPE_INT24:
+ it= new Item_int(it->val_int());
+ break;
+ case MYSQL_TYPE_DECIMAL:
+ case MYSQL_TYPE_FLOAT:
+ case MYSQL_TYPE_DOUBLE:
+ it= new Item_real(it->val());
+ break;
+ case MYSQL_TYPE_VAR_STRING:
+ case MYSQL_TYPE_STRING:
+ case MYSQL_TYPE_TIMESTAMP:
+ case MYSQL_TYPE_DATE:
+ case MYSQL_TYPE_TIME:
+ case MYSQL_TYPE_DATETIME:
+ case MYSQL_TYPE_YEAR:
+ case MYSQL_TYPE_NEWDATE:
+ {
+ 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;
+ }
+ case MYSQL_TYPE_NULL:
+ it= new Item_null(); // A NULL is a NULL is a NULL...
+ break;
+ case MYSQL_TYPE_ENUM:
+ case MYSQL_TYPE_SET:
+ case MYSQL_TYPE_TINY_BLOB:
+ case MYSQL_TYPE_MEDIUM_BLOB:
+ case MYSQL_TYPE_LONG_BLOB:
+ case MYSQL_TYPE_BLOB:
+ case MYSQL_TYPE_GEOMETRY:
+ /* 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;
+}
+
+sp_head::sp_head(LEX_STRING *name, LEX *lex)
+{
+ const char *dstr = (const char*)lex->buf;
+
+ m_mylex= lex;
+ m_name= new Item_string(name->str, name->length, default_charset_info);
+ 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);
+}
+
+int
+sp_head::create(THD *thd)
+{
+ String *name= m_name->const_string();
+ String *def= m_defstr->const_string();
+
+ return sp_create_procedure(thd,
+ name->c_ptr(), name->length(),
+ def->c_ptr(), def->length());
+}
+
+int
+sp_head::execute(THD *thd)
+{
+ int ret= 0;
+ sp_instr *p;
+ sp_pcontext *pctx = m_mylex->spcont;
+ uint csize = pctx->max_framesize();
+ uint params = pctx->params();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+
+ if (csize > 0)
+ {
+ uint i;
+ List_iterator_fast<Item> li(m_mylex->value_list);
+ Item *it = li++; // Skip first one, it's the procedure name
+
+ nctx = new sp_rcontext(csize);
+ // QQ: No error checking whatsoever right now
+ for (i = 0 ; (it= li++) && i < params ; i++)
+ {
+ sp_pvar_t *pvar = pctx->find_pvar(i);
+
+ // QQ Passing an argument is, in a sense, a "SET". We have to evaluate
+ // any expression at this point.
+ nctx->push_item(it->this_item());
+ // Note: If it's OUT or INOUT, it must be a variable.
+ // QQ: Need to handle "global" user/host variables too!!!
+ if (!pvar || pvar->mode == sp_param_in)
+ nctx->set_oindex(i, -1);
+ else
+ nctx->set_oindex(i, static_cast<Item_splocal *>(it)->get_offset());
+ }
+ // 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).
+ for (; i < csize ; i++)
+ nctx->push_item(NULL);
+ thd->spcont= nctx;
+ }
+
+ { // Execute instructions...
+ uint ip= 0;
+ my_bool nsok= thd->net.no_send_ok;
+
+ thd->net.no_send_ok= TRUE; // Don't send_ok() during execution
+
+ while (ret == 0)
+ {
+ int offset;
+ sp_instr *i;
+
+ i = get_instr(ip); // Returns NULL when we're done.
+ if (i == NULL)
+ break;
+ ret= i->execute(thd, &offset);
+ ip += offset;
+ }
+
+ thd->net.no_send_ok= nsok;
+ }
+
+ // Don't copy back OUT values if we got an error
+ if (ret == 0 && csize > 0)
+ {
+ // Copy back all OUT or INOUT values to the previous frame
+ for (uint i = 0 ; i < params ; i++)
+ {
+ int oi = nctx->get_oindex(i);
+
+ if (oi >= 0)
+ octx->set_item(nctx->get_oindex(i), nctx->get_item(i));
+ }
+
+ thd->spcont= octx;
+ }
+
+ return ret;
+}
+
+
+void
+sp_head::reset_lex(THD *thd)
+{
+ memcpy(&m_lex, &thd->lex, sizeof(LEX)); // Save old one
+ /* Reset most stuff. The length arguments doesn't matter here. */
+ 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;
+ /* And keep the SP stuff too */
+ thd->lex.sphead = m_lex.sphead;
+ thd->lex.spcont = m_lex.spcont;
+ /* QQ Why isn't this reset by lex_start() ??? */
+ 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();
+}
+
+void
+sp_head::restore_lex(THD *thd)
+{
+ // Update some state in the old one first
+ m_lex.ptr= thd->lex.ptr;
+ m_lex.next_state= thd->lex.next_state;
+ // QQ Append tables, fields, etc. from the current lex to mine
+ memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
+}
+
+// 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;
+}
+
+
+
+//
+// sp_instr_stmt
+//
+int
+sp_instr_stmt::execute(THD *thd, int *offsetp)
+{
+ LEX olex; // The other lex
+
+ 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;
+
+ mysql_execute_command(thd);
+
+ memcpy(&thd->lex, &olex, sizeof(LEX)); // Restore the other lex
+
+ *offsetp = 1;
+ return 0;
+}
+
+//
+// sp_instr_set
+//
+int
+sp_instr_set::execute(THD *thd, int *offsetp)
+{
+ thd->spcont->set_item(m_offset, eval_func_item(m_value, m_type));
+ *offsetp = 1;
+ return 0;
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000000..72a5c90b011
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,200 @@
+/* -*- 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_HEAD_H_
+#define _SP_HEAD_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+#include <stddef.h>
+
+class sp_instr;
+
+class sp_head : public Sql_alloc
+{
+ sp_head(const sp_head &); /* Prevent use of these */
+ void operator=(sp_head &);
+
+public:
+
+ static void *operator new(size_t size)
+ {
+ return (void*) sql_alloc((uint) size);
+ }
+
+ static void operator delete(void *ptr, size_t size)
+ {
+ /* Empty */
+ }
+
+ sp_head(LEX_STRING *name, LEX* lex);
+
+ int
+ create(THD *thd);
+
+ int
+ execute(THD *thd);
+
+ inline void
+ add_instr(sp_instr *i)
+ {
+ insert_dynamic(&m_instr, (gptr)&i);
+ }
+
+ inline uint
+ instructions()
+ {
+ return m_instr.elements;
+ }
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void
+ reset_lex(THD *thd);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void
+ restore_lex(THD *thd);
+
+private:
+
+ Item_string *m_name;
+ Item_string *m_defstr;
+ LEX *m_mylex; // My own lex
+ LEX m_lex; // Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; // The "instructions"
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *in= NULL;
+
+ get_dynamic(&m_instr, (gptr)&in, i);
+ return in;
+ }
+
+}; // 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);
+
+
+class sp_instr : public Sql_alloc
+{
+ sp_instr(const sp_instr &); /* Prevent use of these */
+ void operator=(sp_instr &);
+
+public:
+
+ // Should give each a name or type code for debugging purposes?
+ sp_instr()
+ : Sql_alloc()
+ {}
+
+ virtual ~sp_instr()
+ {}
+
+ // Execute this instrution. '*offsetp' will be set to an offset to the
+ // next instruction to execute. (For most instruction this will be 1,
+ // i.e. the following instruction.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int
+ execute(THD *thd, int *offsetp)
+ { // Default is a no-op.
+ *offsetp = 1; // Next instruction
+ return 0;
+ }
+
+}; // class sp_instr : public Sql_alloc
+
+
+//
+// Call out to some prepared SQL statement.
+//
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+
+ sp_instr_stmt()
+ : sp_instr()
+ {}
+
+ virtual ~sp_instr_stmt()
+ {}
+
+ virtual int execute(THD *thd, int *offsetp);
+
+ inline void
+ set_lex(LEX *lex)
+ {
+ memcpy(&m_lex, lex, sizeof(LEX));
+ }
+
+ inline LEX *
+ get_lex()
+ {
+ return &m_lex;
+ }
+
+private:
+
+ LEX m_lex; // My own lex
+
+}; // class sp_instr_stmt : public sp_instr
+
+
+class sp_instr_set : public sp_instr
+{
+ sp_instr_set(const sp_instr_set &); /* Prevent use of these */
+ void operator=(sp_instr_set &);
+
+public:
+
+ sp_instr_set(uint offset, Item *val, enum enum_field_types type)
+ : sp_instr(), m_offset(offset), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_set()
+ {}
+
+ virtual int execute(THD *thd, int *offsetp);
+
+private:
+
+ uint m_offset;
+ Item *m_value;
+ enum enum_field_types m_type; // The declared type
+
+}; // class sp_instr_set : public sp_instr
+
+#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
new file mode 100644
index 00000000000..d2ab8cb93ac
--- /dev/null
+++ b/sql/sp_pcontext.cc
@@ -0,0 +1,91 @@
+/* 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 */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#if defined(WIN32) || defined(__WIN__)
+#undef SAFEMALLOC /* Problems with threads */
+#endif
+
+#include "mysql_priv.h"
+#include "sp_pcontext.h"
+
+sp_pcontext::sp_pcontext()
+ : m_params(0), m_framesize(0), m_i(0)
+{
+ m_pvar_size = 16;
+ m_pvar = (sp_pvar_t *)my_malloc(m_pvar_size * sizeof(sp_pvar_t), MYF(MY_WME));
+ if (m_pvar)
+ memset(m_pvar, 0, m_pvar_size * sizeof(sp_pvar_t));
+}
+
+void
+sp_pcontext::grow()
+{
+ uint sz = m_pvar_size + 8;
+ sp_pvar_t *a = (sp_pvar_t *)my_realloc((char *)m_pvar,
+ sz * sizeof(sp_pvar_t),
+ MYF(MY_WME | MY_ALLOW_ZERO_PTR));
+
+ if (a)
+ {
+ m_pvar_size = sz;
+ m_pvar = a;
+ }
+}
+
+/* This does a linear search (from newer to older variables, in case
+** we have shadowed names).
+** It's possible to have a more efficient allocation and search method,
+** but it might not be worth it. The typical number of parameters and
+** variables will in most cases be low (a handfull).
+** And this is only called during parsing.
+*/
+sp_pvar_t *
+sp_pcontext::find_pvar(LEX_STRING *name)
+{
+ String n(name->str, name->length, default_charset_info);
+ uint i = m_i;
+
+ while (i-- > 0)
+ {
+ if (stringcmp(&n, m_pvar[i].name->const_string()) == 0)
+ return m_pvar + i;
+ }
+ return NULL;
+}
+
+void
+sp_pcontext::push(LEX_STRING *name, enum enum_field_types type,
+ sp_param_mode_t mode)
+{
+ if (m_i >= m_pvar_size)
+ grow();
+ if (m_i < m_pvar_size)
+ {
+ if (m_i == m_framesize)
+ m_framesize += 1;
+ m_pvar[m_i].name= new Item_string(name->str, name->length,
+ default_charset_info);
+ m_pvar[m_i].type= type;
+ m_pvar[m_i].mode= mode;
+ m_pvar[m_i].offset= m_i;
+ m_pvar[m_i].isset= (mode == sp_param_out ? FALSE : TRUE);
+ m_i += 1;
+ }
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
new file mode 100644
index 00000000000..956b8d99d4d
--- /dev/null
+++ b/sql/sp_pcontext.h
@@ -0,0 +1,127 @@
+/* -*- 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_PCONTEXT_H_
+#define _SP_PCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+typedef enum
+{
+ sp_param_in,
+ sp_param_out,
+ sp_param_inout
+} sp_param_mode_t;
+
+typedef struct
+{
+ Item_string *name;
+ enum enum_field_types type;
+ sp_param_mode_t mode;
+ uint offset; // Offset in current frame
+ my_bool isset;
+} sp_pvar_t;
+
+class sp_pcontext : public Sql_alloc
+{
+ sp_pcontext(const sp_pcontext &); /* Prevent use of these */
+ void operator=(sp_pcontext &);
+
+ public:
+
+ sp_pcontext();
+
+ inline uint
+ max_framesize()
+ {
+ return m_framesize;
+ }
+
+ inline uint
+ current_framesize()
+ {
+ return m_i;
+ }
+
+ inline uint
+ params()
+ {
+ return m_params;
+ }
+
+ // Set the number of parameters to the current esize
+ inline void
+ set_params()
+ {
+ m_params= m_i;
+ }
+
+ inline void
+ set_type(uint i, enum enum_field_types type)
+ {
+ if (i < m_i)
+ m_pvar[i].type= type;
+ }
+
+ inline void
+ set_isset(uint i, my_bool val)
+ {
+ if (i < m_i)
+ m_pvar[i].isset= val;
+ }
+
+ void
+ push(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
+
+ inline void
+ pop(uint num = 1)
+ {
+ if (num >= m_i)
+ m_i -= num;
+ }
+
+ // Find by name
+ sp_pvar_t *
+ find_pvar(LEX_STRING *name);
+
+ // Find by index
+ sp_pvar_t *
+ find_pvar(uint i)
+ {
+ if (i >= m_i)
+ return NULL;
+ return m_pvar+i;
+ }
+
+private:
+
+ uint m_params; // The number of parameters
+ uint m_framesize; // The maximum framesize
+ uint m_i; // The current index (during parsing)
+
+ sp_pvar_t *m_pvar;
+ uint m_pvar_size; // Current size of m_pvar.
+
+ void
+ grow();
+
+}; // class sp_pcontext : public Sql_alloc
+
+
+#endif /* _SP_PCONTEXT_H_ */
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
new file mode 100644
index 00000000000..5ffbb0266e6
--- /dev/null
+++ b/sql/sp_rcontext.h
@@ -0,0 +1,82 @@
+/* -*- 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_RCONTEXT_H_
+#define _SP_RCONTEXT_H_
+
+class sp_rcontext : public Sql_alloc
+{
+ sp_rcontext(const sp_rcontext &); /* Prevent use of these */
+ void operator=(sp_rcontext &);
+
+ public:
+
+ sp_rcontext(uint size)
+ : m_count(0), m_size(size)
+ {
+ m_frame = (Item **)sql_alloc(size * sizeof(Item*));
+ m_outs = (int *)sql_alloc(size * sizeof(int));
+ }
+
+ ~sp_rcontext()
+ {
+ // Not needed?
+ //sql_element_free(m_frame);
+ }
+
+ inline void
+ push_item(Item *i)
+ {
+ if (m_count < m_size)
+ m_frame[m_count++] = i;
+ }
+
+ inline void
+ set_item(uint idx, Item *i)
+ {
+ if (idx < m_count)
+ m_frame[idx] = i;
+ }
+
+ inline Item *
+ get_item(uint idx)
+ {
+ return m_frame[idx];
+ }
+
+ inline void
+ set_oindex(uint idx, int oidx)
+ {
+ m_outs[idx] = oidx;
+ }
+
+ inline int
+ get_oindex(uint idx)
+ {
+ return m_outs[idx];
+ }
+
+private:
+
+ uint m_count;
+ uint m_size;
+ Item **m_frame;
+ int *m_outs;
+
+}; // class sp_rcontext : public Sql_alloc
+
+#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index ebd1d9d2b3c..855c97715ea 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -81,7 +81,7 @@ extern "C" void free_user_var(user_var_entry *entry)
THD::THD():user_time(0), fatal_error(0),
last_insert_id_used(0),
insert_id_used(0), rand_used(0), in_lock_tables(0),
- global_read_lock(0), bootstrap(0)
+ global_read_lock(0), bootstrap(0), spcont(NULL)
{
host=user=priv_user=db=query=ip=0;
host_or_ip="unknown ip";
diff --git a/sql/sql_class.h b/sql/sql_class.h
index ca56d2dcdf5..00236aee176 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -26,6 +26,7 @@
class Query_log_event;
class Load_log_event;
class Slave_log_event;
+class sp_rcontext;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY };
@@ -515,6 +516,7 @@ public:
bool query_error, bootstrap, cleanup_done;
bool volatile killed;
bool prepare_command;
+ sp_rcontext *spcont; // SP runtime context
/*
If we do a purge of binary logs, log index info of the threads
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 833f36dbe9f..d40d968225f 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -155,6 +155,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
{
LEX *lex= &thd->lex;
lex->next_state=STATE_START;
+ lex->buf= buf;
lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
lex->select_lex.create_refs=lex->in_comment=0;
@@ -170,6 +171,8 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->slave_thd_opt=0;
lex->sql_command=SQLCOM_END;
lex->safe_to_cache_query= 1;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
bzero(&lex->mi,sizeof(lex->mi));
return lex;
}
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 0c761baffa3..467ff26d5ab 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -21,6 +21,9 @@
class Table_ident;
class sql_exchange;
class LEX_COLUMN;
+class sp_head;
+class sp_instr;
+class sp_pcontext;
/*
The following hack is needed because mysql_yacc.cc does not define
@@ -72,6 +75,8 @@ enum enum_sql_command {
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CALL, SQLCOM_DROP_PROCEDURE,
+ SQLCOM_ALTER_PROCEDURE,
/* This should be the last !!! */
SQLCOM_END
@@ -402,6 +407,7 @@ typedef struct st_lex
SELECT_LEX_NODE *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
+ uchar *buf; /* The beginning of string, used by SPs */
uchar *ptr,*tok_start,*tok_end,*end_of_query;
char *length,*dec,*change,*name;
char *backup_dir; /* For RESTORE/BACKUP */
@@ -459,6 +465,8 @@ typedef struct st_lex
uint slave_thd_opt;
CHARSET_INFO *charset;
char *help_arg;
+ sp_head *sphead;
+ sp_pcontext *spcont;
} LEX;
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 660fd545bb6..fba726c95b6 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -27,6 +27,8 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -2739,6 +2741,84 @@ mysql_execute_command(THD *thd)
res= -1;
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ if (!lex->sphead)
+ res= -1;
+ else
+ {
+ res= lex->sphead->create(thd);
+ if (res < 0)
+ {
+ // QQ Error!
+ }
+ send_ok(thd);
+ }
+ break;
+ case SQLCOM_CALL:
+ {
+ Item_string *s;
+ sp_head *sp;
+
+ s= (Item_string*)lex->value_list.head();
+ sp= sp_find(thd, s);
+ if (! sp)
+ {
+ // QQ Error!
+ res= -1;
+ }
+ else
+ {
+ res= sp->execute(thd);
+ if (res == 0)
+ send_ok(thd);
+ }
+ }
+ break;
+ case SQLCOM_ALTER_PROCEDURE:
+ {
+ Item_string *s;
+ sp_head *sp;
+
+ s= (Item_string*)lex->value_list.head();
+ sp= sp_find(thd, s);
+ if (! sp)
+ {
+ // QQ Error!
+ res= -1;
+ }
+ else
+ {
+ /* QQ This is an no-op right now, since we haven't
+ put the characteristics in yet. */
+ send_ok(thd);
+ }
+ }
+ break;
+ case SQLCOM_DROP_PROCEDURE:
+ {
+ Item_string *s;
+ sp_head *sp;
+
+ s = (Item_string*)lex->value_list.head();
+ sp = sp_find(thd, s);
+ if (! sp)
+ {
+ // QQ Error!
+ res= -1;
+ }
+ else
+ {
+ String *name = s->const_string();
+
+ res= sp_drop(thd, name->c_ptr(), name->length());
+ if (res < 0)
+ {
+ // QQ Error!
+ }
+ send_ok(thd);
+ }
+ }
+ break;
default: /* Impossible */
send_ok(thd);
break;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index ed8e8f0fb51..33f928f2efe 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -35,6 +35,8 @@
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -120,6 +122,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token AVG_SYM
%token BEGIN_SYM
%token BINLOG_SYM
+%token CALL_SYM
%token CHANGE
%token CLIENT_SYM
%token COMMENT_SYM
@@ -198,6 +201,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token CONVERT_SYM
%token DATABASES
%token DATA_SYM
+%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
@@ -242,6 +246,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
+%token INOUT_SYM
%token INTO
%token IN_SYM
%token ISOLATION
@@ -256,6 +261,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token LIKE
%token LINES
%token LOCAL_SYM
+%token LOCATOR_SYM
%token LOG_SYM
%token LOGS_SYM
%token LONG_NUM
@@ -296,6 +302,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token OR
%token OR_OR_CONCAT
%token ORDER_SYM
+%token OUT_SYM
%token OUTER
%token OUTFILE
%token DUMPFILE
@@ -334,6 +341,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
+%token SPECIFIC_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
@@ -518,6 +526,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
+/* QQ This is a dummy, until we have solved the SET syntax problem. */
+%token SPSET_SYM
+
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
@@ -666,8 +677,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
union_clause union_list union_option
precision subselect_start opt_and
subselect_end select_var_list select_var_list_init help opt_len
+ statement
END_OF_INPUT
+%type <NONE> call sp_proc_body sp_proc_stmts sp_proc_stmt
+%type <num> sp_decls sp_decl sp_decl_idents sp_opt_inout
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM
@@ -693,10 +708,16 @@ query:
| verb_clause END_OF_INPUT {};
verb_clause:
+ statement
+ | begin
+ ;
+
+/* Verb clauses, except begin */
+statement:
alter
| analyze
| backup
- | begin
+ | call
| change
| check
| commit
@@ -874,7 +895,177 @@ create:
lex->udf.returns=(Item_result) $7;
lex->udf.dl=$9.str;
}
- ;
+ | CREATE PROCEDURE ident
+ {
+ LEX *lex= Lex;
+
+ lex->spcont = new sp_pcontext();
+ lex->sphead = new sp_head(&$3, lex);
+ }
+ '(' sp_dparam_list ')'
+ {
+ Lex->spcont->set_params();
+ }
+ sp_proc_body
+ {
+ Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ }
+ ;
+
+call:
+ CALL_SYM ident
+ {
+ LEX *lex = Lex;
+
+ lex->sql_command= SQLCOM_CALL;
+ lex->value_list.empty();
+ lex->value_list.push_back(
+ (Item*)new Item_string($2.str, $2.length, default_charset_info));
+ }
+ '(' sp_cparam_list ')' {}
+ ;
+
+/* CALL parameters */
+sp_cparam_list:
+ /* Empty */
+ | sp_cparams
+ ;
+
+sp_cparams:
+ sp_cparams ',' expr
+ {
+ Lex->value_list.push_back($3);
+ }
+ | expr
+ {
+ Lex->value_list.push_back($1);
+ }
+ ;
+
+/* SP parameter declaration list */
+sp_dparam_list:
+ /* Empty */
+ | sp_dparams
+ ;
+
+sp_dparams:
+ sp_dparams ',' sp_dparam
+ | sp_dparam
+ ;
+
+sp_dparam:
+ sp_opt_inout ident type sp_opt_locator
+ {
+ Lex->spcont->push(&$2,
+ (enum enum_field_types)$3,
+ (sp_param_mode_t)$1);
+ }
+ ;
+
+sp_opt_inout:
+ /* Empty */ { $$= sp_param_in; }
+ | IN_SYM { $$= sp_param_in; }
+ | OUT_SYM { $$= sp_param_out; }
+ | INOUT_SYM { $$= sp_param_inout; }
+ ;
+
+sp_opt_locator:
+ /* Empty */
+ | AS LOCATOR_SYM
+ ;
+
+sp_proc_body:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+ }
+ sp_proc_stmt
+ {
+ Lex->sphead->restore_lex(YYTHD);
+ }
+ | begin
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ Lex->spcont->pop($2);
+ }
+ ;
+
+sp_proc_stmts:
+ sp_proc_body ';'
+ | sp_proc_stmts sp_proc_body ';'
+ ;
+
+sp_decls:
+ /* Empty */
+ {
+ $$= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ $$= $1 + $2;
+ }
+ ;
+
+sp_decl:
+ DECLARE_SYM sp_decl_idents type
+ {
+ LEX *lex= Lex;
+ uint max= lex->spcont->current_framesize();
+
+ for (uint i = max-$2 ; i < max ; i++)
+ {
+ lex->spcont->set_type(i, (enum enum_field_types)$3);
+ lex->spcont->set_isset(i, FALSE);
+ }
+ $$= $2;
+ }
+ ;
+
+sp_decl_idents:
+ ident
+ {
+ Lex->spcont->push(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
+ Lex->spcont->push(&$3, (enum_field_types)0, sp_param_in);
+ $$= $1 + 1;
+ }
+ ;
+
+/* Dummy for the spset thing. Will go away when the SET problem is fixed. */
+sp_proc_stmt:
+ statement
+ {
+ LEX *lex= Lex;
+ sp_instr_stmt *i= new sp_instr_stmt();
+
+ i->set_lex(lex);
+ lex->sphead->add_instr(i);
+ }
+ |
+ /* QQ Dummy. We need to fix the old SET syntax to make it work for
+ local SP variables as well. */
+ SPSET_SYM ident EQ expr
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$2)))
+ YYABORT; /* Unknow variable */
+ else
+ {
+ /* QQ Check type match! */
+ sp_instr_set *i = new sp_instr_set(spv->offset, $4, spv->type);
+
+ lex->sphead->add_instr(i);
+ spv->isset= TRUE;
+ }
+ }
+ ;
create2:
'(' field_list ')' opt_create_table_options create3 {}
@@ -1393,8 +1584,27 @@ alter:
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
lex->create_info.table_charset=$4;
- };
+ }
+ | ALTER PROCEDURE opt_specific ident
+ /* QQ Characteristics missing for now */
+ opt_restrict
+ {
+ LEX *lex=Lex;
+
+ /* This is essensially an no-op right now, since we haven't
+ put the characteristics in yet. */
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->value_list.empty();
+ lex->value_list.push_back(
+ (Item*)new Item_string($4.str, $4.length, default_charset_info));
+ }
+ ;
+opt_specific:
+ /* Empty */
+ |
+ SPECIFIC_SYM
+ ;
alter_list:
| alter_list_item
@@ -2905,7 +3115,16 @@ drop:
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_FUNCTION;
lex->udf.name=$3.str;
- };
+ }
+ | DROP PROCEDURE ident opt_restrict
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_PROCEDURE;
+ lex->value_list.empty();
+ lex->value_list.push_back(
+ (Item*)new Item_string($3.str, $3.length, default_charset_info));
+ }
+ ;
table_list:
@@ -2958,7 +3177,6 @@ replace:
}
insert_field_spec
{}
- {}
;
insert_lock_option:
@@ -3642,8 +3860,25 @@ order_ident:
simple_ident:
ident
{
- SELECT_LEX_NODE *sel=Select;
- $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str);
+ sp_pvar_t *spv;
+ LEX *lex = Lex;
+ sp_pcontext *spc = lex->spcont;
+
+ if (spc && (spv = spc->find_pvar(&$1)))
+ { /* We're compiling a stored procedure and found a variable */
+ if (lex->sql_command != SQLCOM_CALL && ! spv->isset)
+ {
+ printf("QQ Referring to an unitialized variable\n");
+ YYABORT; /* QQ Referring to an unitialized variable */
+ }
+ else
+ $$ = (Item*) new Item_splocal(spv->offset);
+ }
+ else
+ {
+ SELECT_LEX_NODE *sel=Select;
+ $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str);
+ }
}
| ident '.' ident
{