Commit aae07a4d authored by pem@mysql.com's avatar pem@mysql.com

Simplistic, experimental framework for Stored Procedures (SPs).

Implements creation and dropping of PROCEDUREs, IN, OUT, and INOUT parameters,
single-statement procedures, rudimentary multi-statement (begin-end) prodedures
(when the client can handle it), and local variables.
Missing most of the embedded SQL language, all attributes, FUNCTIONs, error handling,
reparses procedures at each call (no caching), etc, etc.
Certainly buggy too, but procedures can actually be created and called....
parent 60934fb0
......@@ -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)
......
......@@ -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);
......
......@@ -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
*/
......
......@@ -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},
......
/* 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;
}
/* -*- 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_ */
/* 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;
}
}
/* -*- 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_ */
/* -*- 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_ */
......@@ -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";
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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;
......
......@@ -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;
......
......@@ -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,6 +895,176 @@ 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:
......@@ -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:
......@@ -3641,10 +3859,27 @@ order_ident:
simple_ident:
ident
{
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
{
SELECT_LEX_NODE *sel=Select;
......
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