Merge
BitKeeper/etc/logging_ok: auto-union configure.in: Auto merged sql/Makefile.am: Auto merged sql/sql_class.cc: Auto merged sql/sql_class.h: Auto merged sql/sql_parse.cc: Auto merged sql/sql_yacc.yy: Auto merged sql/item.cc: SCCS merged sql/item.h: SCCS merged sql/lex.h: SCCS merged sql/sql_lex.cc: SCCS merged sql/sql_lex.h: SCCS merged
This commit is contained in:
commit
b09fe58c63
@ -6,6 +6,7 @@ FROM=$USER@mysql.com
|
||||
INTERNALS=internals@lists.mysql.com
|
||||
DOCS=docs-commit@mysql.com
|
||||
LIMIT=10000
|
||||
REPOV=5.0
|
||||
|
||||
if [ "$REAL_EMAIL" = "" ]
|
||||
then
|
||||
@ -27,15 +28,15 @@ CHANGESET=`bk -R prs -r+ -h -d':I:' ChangeSet`
|
||||
echo "Commit successful, notifying developers at $TO"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-4.1>
|
||||
List-ID: <bk.mysql-$REPOV>
|
||||
From: $FROM
|
||||
To: $TO
|
||||
Subject: bk commit - 4.1 tree ($CHANGESET)
|
||||
Subject: bk commit - $REPOV tree ($CHANGESET)
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) | head -n $LIMIT | /usr/sbin/sendmail -t
|
||||
) | /usr/sbin/sendmail -t
|
||||
|
||||
#++
|
||||
# internals@ mail
|
||||
@ -43,13 +44,13 @@ EOF
|
||||
echo "Notifying internals list at $INTERNALS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-4.1>
|
||||
List-ID: <bk.mysql-$REPOV>
|
||||
From: $FROM
|
||||
To: $INTERNALS
|
||||
Subject: bk commit into 4.1 tree ($CHANGESET)
|
||||
Subject: bk commit into $REPOV tree ($CHANGESET)
|
||||
|
||||
Below is the list of changes that have just been committed into a local
|
||||
4.1 repository of $USER. When $USER does a push these changes will
|
||||
$REPOV repository of $USER. When $USER does a push these changes will
|
||||
be propagated to the main repository and, within 24 hours after the
|
||||
push, to the public repository.
|
||||
For information on how to access the public repository
|
||||
@ -70,15 +71,15 @@ EOF
|
||||
echo "Notifying docs list at $DOCS"
|
||||
(
|
||||
cat <<EOF
|
||||
List-ID: <bk.mysql-4.1>
|
||||
List-ID: <bk.mysql-$REPOV>
|
||||
From: $FROM
|
||||
To: $DOCS
|
||||
Subject: bk commit - 4.1 tree (Manual) ($CHANGESET)
|
||||
Subject: bk commit - $REPOV tree (Manual) ($CHANGESET)
|
||||
|
||||
EOF
|
||||
bk changes -v -r+
|
||||
bk cset -r+ -d
|
||||
) | head -n $LIMIT | /usr/sbin/sendmail -t
|
||||
) | /usr/sbin/sendmail -t
|
||||
fi
|
||||
|
||||
else
|
||||
|
@ -4,7 +4,7 @@ dnl Process this file with autoconf to produce a configure script.
|
||||
AC_INIT(sql/mysqld.cc)
|
||||
AC_CANONICAL_SYSTEM
|
||||
# The Docs Makefile.am parses this line!
|
||||
AM_INIT_AUTOMAKE(mysql, 4.1.0-alpha)
|
||||
AM_INIT_AUTOMAKE(mysql, 5.0.0-alpha)
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
PROTOCOL_VERSION=10
|
||||
|
@ -306,6 +306,18 @@ then
|
||||
c_c="$c_c comment='Column privileges';"
|
||||
fi
|
||||
|
||||
if test ! -f $mdata/proc.frm
|
||||
then
|
||||
echo "Preparing proc table"
|
||||
|
||||
c_p="$c_p CREATE TABLE proc ("
|
||||
c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
|
||||
c_p="$c_p body blob DEFAULT '' NOT NULL,"
|
||||
c_p="$c_p PRIMARY KEY (name)"
|
||||
c_p="$c_p )"
|
||||
c_p="$c_p comment='Stored Procedures';"
|
||||
fi
|
||||
|
||||
echo "Installing all prepared tables"
|
||||
if (
|
||||
cat << END_OF_DATA
|
||||
@ -324,6 +336,7 @@ $i_f
|
||||
|
||||
$c_t
|
||||
$c_c
|
||||
$c_p
|
||||
END_OF_DATA
|
||||
cat fill_func_tables.sql
|
||||
) | eval "$execdir/mysqld $defaults --bootstrap --skip-grant-tables \
|
||||
|
@ -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 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 \
|
||||
@ -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 sp.cc
|
||||
gen_lex_hash_SOURCES = gen_lex_hash.cc
|
||||
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
|
||||
|
||||
|
19
sql/item.cc
19
sql/item.cc
@ -23,6 +23,7 @@
|
||||
#include <m_ctype.h>
|
||||
#include "my_dir.h"
|
||||
#include <assert.h>
|
||||
#include "sp_rcontext.h"
|
||||
|
||||
/*****************************************************************************
|
||||
** Item functions
|
||||
@ -149,6 +150,24 @@ CHARSET_INFO * Item::thd_charset() const
|
||||
return current_thd->variables.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);
|
||||
|
53
sql/item.h
53
sql/item.h
@ -97,6 +97,8 @@ public:
|
||||
CHARSET_INFO *charset() const { return str_value.charset(); };
|
||||
void set_charset(CHARSET_INFO *cs) { str_value.set_charset(cs); }
|
||||
virtual void set_outer_resolving() {}
|
||||
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; }
|
||||
@ -110,6 +112,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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class st_select_lex;
|
||||
class Item_ident :public Item
|
||||
{
|
||||
|
17
sql/lex.h
17
sql/lex.h
@ -60,6 +60,7 @@ static SYMBOL symbols[] = {
|
||||
{ "AS", SYM(AS),0,0},
|
||||
{ "ASC", SYM(ASC),0,0},
|
||||
{ "ASCII", SYM(ASCII_SYM),0,0},
|
||||
{ "ASENSITIVE", SYM(ASENSITIVE_SYM),0,0},
|
||||
{ "AVG", SYM(AVG_SYM),0,0},
|
||||
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0},
|
||||
{ "AUTO_INCREMENT", SYM(AUTO_INC),0,0},
|
||||
@ -80,6 +81,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},
|
||||
@ -107,6 +109,7 @@ static SYMBOL symbols[] = {
|
||||
{ "CURRENT_DATE", SYM(CURDATE),0,0},
|
||||
{ "CURRENT_TIME", SYM(CURTIME),0,0},
|
||||
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0},
|
||||
{ "CURSOR", SYM(CURSOR_SYM),0,0},
|
||||
{ "DATA", SYM(DATA_SYM),0,0},
|
||||
{ "DATABASE", SYM(DATABASE),0,0},
|
||||
{ "DATABASES", SYM(DATABASES),0,0},
|
||||
@ -118,6 +121,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},
|
||||
@ -140,6 +144,7 @@ static SYMBOL symbols[] = {
|
||||
{ "ERRORS", SYM(ERRORS),0,0},
|
||||
{ "END", SYM(END),0,0},
|
||||
{ "ELSE", SYM(ELSE),0,0},
|
||||
{ "ELSEIF", SYM(ELSEIF_SYM),0,0},
|
||||
{ "ESCAPE", SYM(ESCAPE_SYM),0,0},
|
||||
{ "ESCAPED", SYM(ESCAPED),0,0},
|
||||
{ "ENABLE", SYM(ENABLE_SYM),0,0},
|
||||
@ -194,6 +199,8 @@ 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},
|
||||
{ "INSENSITIVE", SYM(INSENSITIVE_SYM),0,0},
|
||||
{ "INSERT", SYM(INSERT),0,0},
|
||||
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
|
||||
{ "INT", SYM(INT_SYM),0,0},
|
||||
@ -211,12 +218,14 @@ static SYMBOL symbols[] = {
|
||||
{ "ISOLATION", SYM(ISOLATION),0,0},
|
||||
{ "ISAM", SYM(ISAM_SYM),0,0},
|
||||
{ "ISSUER", SYM(ISSUER_SYM),0,0},
|
||||
{ "ITERATE", SYM(ITERATE_SYM),0,0},
|
||||
{ "JOIN", SYM(JOIN_SYM),0,0},
|
||||
{ "KEY", SYM(KEY_SYM),0,0},
|
||||
{ "KEYS", SYM(KEYS),0,0},
|
||||
{ "KILL", SYM(KILL_SYM),0,0},
|
||||
{ "LAST", SYM(LAST_SYM),0,0},
|
||||
{ "LEADING", SYM(LEADING),0,0},
|
||||
{ "LEAVE", SYM(LEAVE_SYM),0,0},
|
||||
{ "LEFT", SYM(LEFT),0,0},
|
||||
{ "LEVEL", SYM(LEVEL_SYM),0,0},
|
||||
{ "LIKE", SYM(LIKE),0,0},
|
||||
@ -231,6 +240,7 @@ static SYMBOL symbols[] = {
|
||||
{ "LOGS", SYM(LOGS_SYM),0,0},
|
||||
{ "LONG", SYM(LONG_SYM),0,0},
|
||||
{ "LONGBLOB", SYM(LONGBLOB),0,0},
|
||||
{ "LOOP", SYM(LOOP_SYM),0,0},
|
||||
{ "LONGTEXT", SYM(LONGTEXT),0,0},
|
||||
{ "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0},
|
||||
{ "MASTER", SYM(MASTER_SYM),0,0},
|
||||
@ -280,6 +290,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},
|
||||
@ -307,6 +318,7 @@ 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},
|
||||
@ -324,6 +336,7 @@ static SYMBOL symbols[] = {
|
||||
{ "RTREE", SYM(RTREE_SYM),0,0},
|
||||
{ "SECOND", SYM(SECOND_SYM),0,0},
|
||||
{ "SELECT", SYM(SELECT_SYM),0,0},
|
||||
{ "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
|
||||
{ "SERIAL", SYM(SERIAL_SYM),0,0},
|
||||
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0},
|
||||
{ "SESSION", SYM(SESSION_SYM),0,0},
|
||||
@ -338,6 +351,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}, /* 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},
|
||||
@ -380,6 +395,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},
|
||||
@ -398,6 +414,7 @@ static SYMBOL symbols[] = {
|
||||
{ "WRITE", SYM(WRITE_SYM),0,0},
|
||||
{ "WHEN", SYM(WHEN_SYM),0,0},
|
||||
{ "WHERE", SYM(WHERE),0,0},
|
||||
{ "WHILE", SYM(WHILE_SYM),0,0},
|
||||
{ "XOR", SYM(XOR),0,0},
|
||||
{ "X509", SYM(X509_SYM),0,0},
|
||||
{ "YEAR", SYM(YEAR_SYM),0,0},
|
||||
|
119
sql/sp.cc
Normal file
119
sql/sp.cc
Normal file
@ -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;
|
||||
}
|
33
sql/sp.h
Normal file
33
sql/sp.h
Normal file
@ -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_ */
|
379
sql/sp_head.cc
Normal file
379
sql/sp_head.cc
Normal file
@ -0,0 +1,379 @@
|
||||
/* 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.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(THD *thd, Item *it, enum enum_field_types type)
|
||||
{
|
||||
it= it->this_item();
|
||||
|
||||
if (it->fix_fields(thd, 0, NULL))
|
||||
return it; // Shouldn't happen?
|
||||
|
||||
/* QQ How do we do this? Is there some better way? */
|
||||
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);
|
||||
String *s= it->val_str(&tmp);
|
||||
|
||||
it= new Item_string(s->c_ptr_quick(), s->length(), 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;
|
||||
}
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
sp_head::sp_head(LEX_STRING *name, LEX *lex)
|
||||
: m_simple_case(FALSE)
|
||||
{
|
||||
const char *dstr = (const char*)lex->buf;
|
||||
|
||||
m_call_lex= 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);
|
||||
m_backpatch.empty();
|
||||
}
|
||||
|
||||
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_call_lex->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_call_lex->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. Should do type checking?
|
||||
for (i = 0 ; (it= li++) && i < params ; i++)
|
||||
{
|
||||
sp_pvar_t *pvar = pctx->find_pvar(i);
|
||||
|
||||
if (! pvar)
|
||||
nctx->set_oindex(i, -1); // Shouldn't happen
|
||||
else
|
||||
{
|
||||
if (pvar->mode == sp_param_out)
|
||||
nctx->push_item(it->this_item()); // OUT
|
||||
else
|
||||
nctx->push_item(eval_func_item(thd, it, pvar->type)); // IN or INOUT
|
||||
// Note: If it's OUT or INOUT, it must be a variable.
|
||||
// QQ: Need to handle "global" user/host variables too!!!
|
||||
if (pvar->mode == sp_param_in)
|
||||
nctx->set_oindex(i, -1); // IN
|
||||
else // OUT or INOUT
|
||||
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 anyway (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)
|
||||
{
|
||||
sp_instr *i;
|
||||
|
||||
i = get_instr(ip); // Returns NULL when we're done.
|
||||
if (i == NULL)
|
||||
break;
|
||||
ret= i->execute(thd, &ip);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
// Reset lex during parsing, before we parse a sub statement.
|
||||
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;
|
||||
/* Clear all lists. (QQ Why isn't this reset by lex_start()?).
|
||||
We may be overdoing this, but we know for sure that value_list must
|
||||
be cleared at least. */
|
||||
thd->lex.col_list.empty();
|
||||
thd->lex.ref_list.empty();
|
||||
thd->lex.drop_list.empty();
|
||||
thd->lex.alter_list.empty();
|
||||
thd->lex.interval_list.empty();
|
||||
thd->lex.users_list.empty();
|
||||
thd->lex.columns.empty();
|
||||
thd->lex.key_list.empty();
|
||||
thd->lex.create_list.empty();
|
||||
thd->lex.insert_list= NULL;
|
||||
thd->lex.field_list.empty();
|
||||
thd->lex.value_list.empty();
|
||||
thd->lex.many_values.empty();
|
||||
thd->lex.var_list.empty();
|
||||
thd->lex.param_list.empty();
|
||||
thd->lex.proc_list.empty();
|
||||
thd->lex.auxilliary_table_list.empty();
|
||||
}
|
||||
|
||||
// Restore lex during parsing, after we have parsed a sub statement.
|
||||
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;
|
||||
|
||||
// Collect some data from the sub statement lex.
|
||||
if (thd->lex.sql_command == SQLCOM_CALL)
|
||||
{
|
||||
// We know they are Item_strings (since we put them there ourselves)
|
||||
// It would be slightly faster to keep the list sorted, but we need
|
||||
// an "insert before" method to do that.
|
||||
Item_string *proc= static_cast<Item_string*>(thd->lex.value_list.head());
|
||||
String *snew= proc->val_str(NULL);
|
||||
List_iterator_fast<Item_string> li(m_calls);
|
||||
Item_string *it;
|
||||
|
||||
while ((it= li++))
|
||||
{
|
||||
String *sold= it->val_str(NULL);
|
||||
|
||||
if (stringcmp(snew, sold) == 0)
|
||||
break;
|
||||
}
|
||||
if (! it)
|
||||
m_calls.push_back(proc);
|
||||
|
||||
}
|
||||
// Merge used tables
|
||||
// QQ ...or just open tables in thd->open_tables?
|
||||
// This is not entirerly clear at the moment, but for now, we collect
|
||||
// tables here.
|
||||
for (SELECT_LEX *sl= thd->lex.all_selects_list ;
|
||||
sl ;
|
||||
sl= sl->next_select())
|
||||
{
|
||||
for (TABLE_LIST *tables= sl->get_table_list() ;
|
||||
tables ;
|
||||
tables= tables->next)
|
||||
{
|
||||
List_iterator_fast<char *> li(m_tables);
|
||||
char **tb;
|
||||
|
||||
while ((tb= li++))
|
||||
if (strcasecmp(tables->real_name, *tb) == 0)
|
||||
break;
|
||||
if (! tb)
|
||||
m_tables.push_back(&tables->real_name);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
|
||||
}
|
||||
|
||||
void
|
||||
sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
|
||||
{
|
||||
bp_t *bp= (bp_t *)my_malloc(sizeof(bp_t), MYF(MY_WME));
|
||||
|
||||
if (bp)
|
||||
{
|
||||
bp->lab= lab;
|
||||
bp->instr= i;
|
||||
(void)m_backpatch.push_front(bp);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sp_head::backpatch(sp_label_t *lab)
|
||||
{
|
||||
bp_t *bp;
|
||||
uint dest= instructions();
|
||||
List_iterator_fast<bp_t> li(m_backpatch);
|
||||
|
||||
while ((bp= li++))
|
||||
if (bp->lab == lab)
|
||||
{
|
||||
sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr);
|
||||
|
||||
i->set_destination(dest);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
|
||||
//
|
||||
// sp_instr_stmt
|
||||
//
|
||||
int
|
||||
sp_instr_stmt::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
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
|
||||
|
||||
*nextp = m_ip+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// sp_instr_set
|
||||
//
|
||||
int
|
||||
sp_instr_set::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
thd->spcont->set_item(m_offset, eval_func_item(thd, m_value, m_type));
|
||||
*nextp = m_ip+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// sp_instr_jump_if
|
||||
//
|
||||
int
|
||||
sp_instr_jump_if::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
|
||||
|
||||
if (it->val_int())
|
||||
*nextp = m_dest;
|
||||
else
|
||||
*nextp = m_ip+1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// sp_instr_jump_if_not
|
||||
//
|
||||
int
|
||||
sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
|
||||
{
|
||||
Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
|
||||
|
||||
if (! it->val_int())
|
||||
*nextp = m_dest;
|
||||
else
|
||||
*nextp = m_ip+1;
|
||||
return 0;
|
||||
}
|
306
sql/sp_head.h
Normal file
306
sql/sp_head.h
Normal file
@ -0,0 +1,306 @@
|
||||
/* -*- 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>
|
||||
|
||||
struct sp_label;
|
||||
|
||||
class sp_instr;
|
||||
|
||||
class sp_head : public Sql_alloc
|
||||
{
|
||||
sp_head(const sp_head &); /* Prevent use of these */
|
||||
void operator=(sp_head &);
|
||||
|
||||
public:
|
||||
|
||||
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
|
||||
List<Item_string> m_calls; // Called procedures.
|
||||
List<char *> m_tables; // Used tables.
|
||||
|
||||
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);
|
||||
|
||||
// Put the instruction on the backpatch list, associated with the label.
|
||||
void
|
||||
push_backpatch(sp_instr *, struct sp_label *);
|
||||
|
||||
// Update all instruction with this label in the backpatch list to
|
||||
// the current position.
|
||||
void
|
||||
backpatch(struct sp_label *);
|
||||
|
||||
private:
|
||||
|
||||
Item_string *m_name;
|
||||
Item_string *m_defstr;
|
||||
LEX *m_call_lex; // The CALL's own lex
|
||||
LEX m_lex; // Temp. store for the other lex
|
||||
DYNAMIC_ARRAY m_instr; // The "instructions"
|
||||
typedef struct
|
||||
{
|
||||
struct sp_label *lab;
|
||||
sp_instr *instr;
|
||||
} bp_t;
|
||||
List<bp_t> m_backpatch; // Instructions needing backpaching
|
||||
|
||||
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
|
||||
|
||||
|
||||
//
|
||||
// "Instructions"...
|
||||
//
|
||||
|
||||
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(uint ip)
|
||||
: Sql_alloc(), m_ip(ip)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr()
|
||||
{}
|
||||
|
||||
// Execute this instrution. '*nextp' will be set to the index of the next
|
||||
// instruction to execute. (For most instruction this will be the
|
||||
// instruction following this one.)
|
||||
// Returns 0 on success, non-zero if some error occured.
|
||||
virtual int
|
||||
execute(THD *thd, uint *nextp)
|
||||
{ // Default is a no-op.
|
||||
*nextp = m_ip+1; // Next instruction
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
uint m_ip; // My index
|
||||
|
||||
}; // 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(uint ip)
|
||||
: sp_instr(ip)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_stmt()
|
||||
{}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
||||
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 ip, uint offset, Item *val, enum enum_field_types type)
|
||||
: sp_instr(ip), m_offset(offset), m_value(val), m_type(type)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_set()
|
||||
{}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
||||
private:
|
||||
|
||||
uint m_offset; // Frame offset
|
||||
Item *m_value;
|
||||
enum enum_field_types m_type; // The declared type
|
||||
|
||||
}; // class sp_instr_set : public sp_instr
|
||||
|
||||
|
||||
class sp_instr_jump : public sp_instr
|
||||
{
|
||||
sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
|
||||
void operator=(sp_instr_jump &);
|
||||
|
||||
public:
|
||||
|
||||
sp_instr_jump(uint ip)
|
||||
: sp_instr(ip)
|
||||
{}
|
||||
|
||||
sp_instr_jump(uint ip, uint dest)
|
||||
: sp_instr(ip), m_dest(dest)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_jump()
|
||||
{}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp)
|
||||
{
|
||||
*nextp= m_dest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void
|
||||
set_destination(uint dest)
|
||||
{
|
||||
m_dest= dest;
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
int m_dest; // Where we will go
|
||||
|
||||
}; // class sp_instr_jump : public sp_instr
|
||||
|
||||
|
||||
class sp_instr_jump_if : public sp_instr_jump
|
||||
{
|
||||
sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */
|
||||
void operator=(sp_instr_jump_if &);
|
||||
|
||||
public:
|
||||
|
||||
sp_instr_jump_if(uint ip, Item *i)
|
||||
: sp_instr_jump(ip), m_expr(i)
|
||||
{}
|
||||
|
||||
sp_instr_jump_if(uint ip, Item *i, uint dest)
|
||||
: sp_instr_jump(ip, dest), m_expr(i)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_jump_if()
|
||||
{}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
||||
private:
|
||||
|
||||
Item *m_expr; // The condition
|
||||
|
||||
}; // class sp_instr_jump_if : public sp_instr_jump
|
||||
|
||||
|
||||
class sp_instr_jump_if_not : public sp_instr_jump
|
||||
{
|
||||
sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
|
||||
void operator=(sp_instr_jump_if_not &);
|
||||
|
||||
public:
|
||||
|
||||
sp_instr_jump_if_not(uint ip, Item *i)
|
||||
: sp_instr_jump(ip), m_expr(i)
|
||||
{}
|
||||
|
||||
sp_instr_jump_if_not(uint ip, Item *i, uint dest)
|
||||
: sp_instr_jump(ip, dest), m_expr(i)
|
||||
{}
|
||||
|
||||
virtual ~sp_instr_jump_if_not()
|
||||
{}
|
||||
|
||||
virtual int execute(THD *thd, uint *nextp);
|
||||
|
||||
private:
|
||||
|
||||
Item *m_expr; // The condition
|
||||
|
||||
}; // class sp_instr_jump_if_not : public sp_instr_jump
|
||||
|
||||
#endif /* _SP_HEAD_H_ */
|
120
sql/sp_pcontext.cc
Normal file
120
sql/sp_pcontext.cc
Normal file
@ -0,0 +1,120 @@
|
||||
/* 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"
|
||||
#include "sp_head.h"
|
||||
|
||||
sp_pcontext::sp_pcontext()
|
||||
: m_params(0), m_framesize(0), m_i(0), m_genlab(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));
|
||||
m_label.empty();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
sp_label_t *
|
||||
sp_pcontext::push_label(char *name, uint ip)
|
||||
{
|
||||
sp_label_t *lab = (sp_label_t *)my_malloc(sizeof(sp_label_t), MYF(MY_WME));
|
||||
|
||||
if (lab)
|
||||
{
|
||||
lab->name= name;
|
||||
lab->ip= ip;
|
||||
m_label.push_front(lab);
|
||||
}
|
||||
return lab;
|
||||
}
|
||||
|
||||
sp_label_t *
|
||||
sp_pcontext::find_label(char *name)
|
||||
{
|
||||
List_iterator_fast<sp_label_t> li(m_label);
|
||||
sp_label_t *lab;
|
||||
|
||||
while ((lab= li++))
|
||||
if (strcasecmp(name, lab->name) == 0)
|
||||
return lab;
|
||||
|
||||
return NULL;
|
||||
}
|
155
sql/sp_pcontext.h
Normal file
155
sql/sp_pcontext.h
Normal file
@ -0,0 +1,155 @@
|
||||
/* -*- 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;
|
||||
|
||||
typedef struct sp_label
|
||||
{
|
||||
char *name;
|
||||
uint ip; // Instruction index
|
||||
} sp_label_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);
|
||||
|
||||
// Pop the last 'num' slots of the frame
|
||||
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;
|
||||
}
|
||||
|
||||
sp_label_t *
|
||||
push_label(char *name, uint ip);
|
||||
|
||||
sp_label_t *
|
||||
find_label(char *name);
|
||||
|
||||
inline sp_label_t *
|
||||
last_label()
|
||||
{
|
||||
return m_label.head();
|
||||
}
|
||||
|
||||
inline sp_label_t *
|
||||
pop_label()
|
||||
{
|
||||
return m_label.pop();
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
List<sp_label_t> m_label; // The label list
|
||||
uint m_genlab; // Gen. label counter
|
||||
|
||||
}; // class sp_pcontext : public Sql_alloc
|
||||
|
||||
|
||||
#endif /* _SP_PCONTEXT_H_ */
|
82
sql/sp_rcontext.h
Normal file
82
sql/sp_rcontext.h
Normal file
@ -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_ */
|
@ -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 };
|
||||
@ -524,6 +525,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;
|
||||
@ -172,6 +173,8 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
|
||||
lex->sql_command=SQLCOM_END;
|
||||
lex->safe_to_cache_query= 1;
|
||||
lex->tmp_table_used= 0;
|
||||
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
|
||||
@ -404,6 +409,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 */
|
||||
@ -462,6 +468,8 @@ typedef struct st_lex
|
||||
CHARSET_INFO *charset;
|
||||
char *help_arg;
|
||||
bool tmp_table_used;
|
||||
sp_head *sphead;
|
||||
sp_pcontext *spcont;
|
||||
} LEX;
|
||||
|
||||
|
||||
|
@ -29,6 +29,9 @@
|
||||
#include "ha_innodb.h"
|
||||
#endif
|
||||
|
||||
#include "sp_head.h"
|
||||
#include "sp.h"
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
/*
|
||||
Without SSL the handshake consists of one packet. This packet
|
||||
@ -2808,6 +2811,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;
|
||||
|
498
sql/sql_yacc.yy
498
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>
|
||||
|
||||
@ -121,6 +123,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
|
||||
@ -200,6 +203,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
|
||||
@ -244,6 +248,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
|
||||
@ -258,6 +263,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
|
||||
@ -298,6 +304,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
|
||||
@ -336,6 +343,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
|
||||
@ -522,6 +530,21 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
|
||||
%token SQL_SMALL_RESULT
|
||||
%token SQL_BUFFER_RESULT
|
||||
|
||||
%token CURSOR_SYM
|
||||
%token ELSEIF_SYM
|
||||
%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
|
||||
%token INSENSITIVE_SYM
|
||||
%token SENSITIVE_SYM
|
||||
/* 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
|
||||
@ -671,8 +694,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_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
|
||||
@ -698,10 +725,16 @@ query:
|
||||
| verb_clause END_OF_INPUT {};
|
||||
|
||||
verb_clause:
|
||||
statement
|
||||
| begin
|
||||
;
|
||||
|
||||
/* Verb clauses, except begin */
|
||||
statement:
|
||||
alter
|
||||
| analyze
|
||||
| backup
|
||||
| begin
|
||||
| call
|
||||
| change
|
||||
| check
|
||||
| commit
|
||||
@ -881,7 +914,414 @@ 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_stmt
|
||||
{
|
||||
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_stmts:
|
||||
sp_proc_stmt ';'
|
||||
| sp_proc_stmts sp_proc_stmt ';'
|
||||
;
|
||||
|
||||
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;
|
||||
}
|
||||
;
|
||||
|
||||
sp_proc_stmt:
|
||||
{
|
||||
Lex->sphead->reset_lex(YYTHD);
|
||||
}
|
||||
statement
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_instr_stmt *i= new sp_instr_stmt(lex->sphead->instructions());
|
||||
|
||||
i->set_lex(lex);
|
||||
lex->sphead->add_instr(i);
|
||||
lex->sphead->restore_lex(YYTHD);
|
||||
}
|
||||
| IF sp_if END IF {}
|
||||
| CASE_SYM WHEN_SYM
|
||||
{
|
||||
Lex->sphead->m_simple_case= FALSE;
|
||||
}
|
||||
sp_case END CASE_SYM {}
|
||||
| CASE_SYM expr WHEN_SYM
|
||||
{
|
||||
/* We "fake" this by using an anonymous variable which we
|
||||
set to the expression. Note that all WHENs are evaluate
|
||||
at the same frame level, so we then know that it's the
|
||||
top-most variable in the frame. */
|
||||
LEX *lex= Lex;
|
||||
uint offset= lex->spcont->current_framesize();
|
||||
sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
|
||||
offset, $2, MYSQL_TYPE_STRING);
|
||||
LEX_STRING dummy;
|
||||
|
||||
dummy.str= (char *)"";
|
||||
dummy.length= 0;
|
||||
lex->spcont->push(&dummy, MYSQL_TYPE_STRING, sp_param_in);
|
||||
lex->sphead->add_instr(i);
|
||||
lex->sphead->m_simple_case= TRUE;
|
||||
}
|
||||
sp_case END CASE_SYM
|
||||
{
|
||||
Lex->spcont->pop();
|
||||
}
|
||||
| sp_labeled_control
|
||||
{}
|
||||
| { /* Unlabeled controls get a secret label. */
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->spcont->push_label((char *)"", lex->sphead->instructions());
|
||||
}
|
||||
sp_unlabeled_control
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->sphead->backpatch(lex->spcont->pop_label());
|
||||
}
|
||||
| LEAVE_SYM IDENT
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp = lex->sphead;
|
||||
sp_label_t *lab= lex->spcont->find_label($2.str);
|
||||
|
||||
if (! lab)
|
||||
{
|
||||
printf("QQ LEAVE with no matching label\n");
|
||||
YYABORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
sp_instr_jump *i= new sp_instr_jump(sp->instructions());
|
||||
|
||||
sp->push_backpatch(i, lab); /* Jumping forward */
|
||||
sp->add_instr(i);
|
||||
}
|
||||
}
|
||||
| ITERATE_SYM IDENT
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_label_t *lab= lex->spcont->find_label($2.str);
|
||||
|
||||
if (! lab)
|
||||
{
|
||||
printf("QQ ITERATE with no matching label\n");
|
||||
YYABORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint ip= lex->sphead->instructions();
|
||||
sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); /* Jump back */
|
||||
|
||||
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(lex->sphead->instructions(),
|
||||
spv->offset, $4, spv->type);
|
||||
|
||||
lex->sphead->add_instr(i);
|
||||
spv->isset= TRUE;
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
sp_if:
|
||||
expr THEN_SYM
|
||||
{
|
||||
sp_head *sp= Lex->sphead;
|
||||
sp_pcontext *ctx= Lex->spcont;
|
||||
uint ip= sp->instructions();
|
||||
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $1);
|
||||
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
sp->add_instr(i);
|
||||
}
|
||||
sp_proc_stmts
|
||||
{
|
||||
sp_head *sp= Lex->sphead;
|
||||
sp_pcontext *ctx= Lex->spcont;
|
||||
uint ip= sp->instructions();
|
||||
sp_instr_jump *i = new sp_instr_jump(ip);
|
||||
|
||||
sp->add_instr(i);
|
||||
sp->backpatch(ctx->pop_label());
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
}
|
||||
sp_elseifs
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->sphead->backpatch(lex->spcont->pop_label());
|
||||
}
|
||||
;
|
||||
|
||||
sp_elseifs:
|
||||
/* Empty */
|
||||
| ELSEIF_SYM sp_if
|
||||
| ELSE sp_proc_stmts
|
||||
;
|
||||
|
||||
sp_case:
|
||||
expr THEN_SYM
|
||||
{
|
||||
sp_head *sp= Lex->sphead;
|
||||
sp_pcontext *ctx= Lex->spcont;
|
||||
uint ip= sp->instructions();
|
||||
sp_instr_jump_if_not *i;
|
||||
|
||||
if (! sp->m_simple_case)
|
||||
i= new sp_instr_jump_if_not(ip, $1);
|
||||
else
|
||||
{ /* Simple case: <caseval> = <whenval> */
|
||||
Item *var= (Item*) new Item_splocal(ctx->current_framesize()-1);
|
||||
Item *expr= Item_bool_func2::eq_creator(var, $1);
|
||||
|
||||
i= new sp_instr_jump_if_not(ip, expr);
|
||||
}
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
sp->add_instr(i);
|
||||
}
|
||||
sp_proc_stmts
|
||||
{
|
||||
sp_head *sp= Lex->sphead;
|
||||
sp_pcontext *ctx= Lex->spcont;
|
||||
uint ip= sp->instructions();
|
||||
sp_instr_jump *i = new sp_instr_jump(ip);
|
||||
|
||||
sp->add_instr(i);
|
||||
sp->backpatch(ctx->pop_label());
|
||||
sp->push_backpatch(i, ctx->push_label((char *)"", 0));
|
||||
}
|
||||
sp_whens
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
|
||||
lex->sphead->backpatch(lex->spcont->pop_label());
|
||||
}
|
||||
;
|
||||
|
||||
sp_whens:
|
||||
/* Empty */
|
||||
| WHEN_SYM sp_case
|
||||
| ELSE sp_proc_stmts
|
||||
;
|
||||
|
||||
sp_labeled_control:
|
||||
IDENT ':'
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_label_t *lab= lex->spcont->find_label($1.str);
|
||||
|
||||
if (lab)
|
||||
{
|
||||
printf("QQ Redefining label\n");
|
||||
YYABORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
lex->spcont->push_label($1.str,
|
||||
lex->sphead->instructions());
|
||||
}
|
||||
}
|
||||
sp_unlabeled_control IDENT
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_label_t *lab= lex->spcont->find_label($5.str);
|
||||
|
||||
if (! lab)
|
||||
{
|
||||
printf("QQ end-label without match\n");
|
||||
YYABORT;
|
||||
}
|
||||
else if (strcasecmp($5.str, lab->name) != 0)
|
||||
{
|
||||
printf("QQ mismatching labels\n");
|
||||
YYABORT;
|
||||
}
|
||||
else
|
||||
{
|
||||
lex->spcont->pop_label();
|
||||
lex->sphead->backpatch(lab);
|
||||
}
|
||||
}
|
||||
;
|
||||
|
||||
sp_unlabeled_control:
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
LEX *lex= Lex;
|
||||
sp_head *sp= lex->sphead;
|
||||
uint ip= sp->instructions();
|
||||
sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $2);
|
||||
|
||||
/* Jumping forward */
|
||||
sp->push_backpatch(i, lex->spcont->last_label());
|
||||
sp->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:
|
||||
'(' field_list ')' opt_create_table_options create3 {}
|
||||
@ -1437,8 +1877,27 @@ alter:
|
||||
LEX *lex=Lex;
|
||||
lex->sql_command=SQLCOM_ALTER_DB;
|
||||
lex->name=$3.str;
|
||||
};
|
||||
}
|
||||
| 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
|
||||
@ -2969,7 +3428,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:
|
||||
@ -3022,7 +3490,6 @@ replace:
|
||||
}
|
||||
insert_field_spec
|
||||
{}
|
||||
{}
|
||||
;
|
||||
|
||||
insert_lock_option:
|
||||
@ -3706,8 +4173,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
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user