Commit ef0c55c5 authored by unknown's avatar unknown

Most of the groundwork for sprint task 729 (implement FUNCTIONs).

Expanded the mysql.proc table, reworked the find/create/drop functions
completely, added new functions for FUNCTIONs (lotta functions here :),
got rid of some unnecessary use of Item_strings while at it. Extended
the parser correspondingly, and fiddled around a bit to make SP FUNCTIONs
coexist with UDFs.
Can now CREATE and DROP FUNCTIONs. Invoking yet to come...


Docs/sp-implemented.txt:
  Updated with info about CASCADE/RESTICT and METHOD, and some answers to questions.
include/mysqld_error.h:
  New error message for misuse of RETURN.
mysql-test/install_test_db.sh:
  Added enum field to mysql.proc to distinguish between FUNCTION and PROCEDURE.
mysql-test/r/sp.result:
  New test for creating and dropping FUNCTIONS.
mysql-test/t/sp.test:
  New test for creating and dropping FUNCTIONS.
scripts/mysql_install_db.sh:
  Added enum field to mysql.proc to distinguish between FUNCTION and PROCEDURE.
sql/lex.h:
  De-UDFed some symbol names, as they are now used for SPs as well.
  Added RETURN_SYM.
sql/share/czech/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/danish/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/dutch/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/english/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/estonian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/french/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/german/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/greek/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/hungarian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/italian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/japanese/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/korean/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/norwegian-ny/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/norwegian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/polish/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/portuguese/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/romanian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/russian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/serbian/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/slovak/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/spanish/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/swedish/errmsg.txt:
  New error message for misuse of RETURN.
sql/share/ukrainian/errmsg.txt:
  New error message for misuse of RETURN.
sql/sp.cc:
  Major rehack to accomodate FUNCTIONs, and to make it easier to add
  future in-memory cache of prepared SPs.
sql/sp.h:
  Major rehack to accomodate FUNCTIONs, and to make it easier to add
  future in-memory cache of prepared SPs.
sql/sp_head.cc:
  Now creates FUNCTIONs too. (And got rid of some unnecessary Item_string use.)
sql/sp_head.h:
  Now creates FUNCTIONs too. (And got rid of some unnecessary Item_string use.)
sql/sql_lex.h:
  New stored FUNCTION commands.
sql/sql_parse.cc:
  Added FUNCTION support ("drop" merged with the old UDF code), and made some
  additional changes for better error handling (following the sp.cc rehacking).
sql/sql_yacc.yy:
  Some former UDF specific symbols renamed.
  Added CREATE FUNCTION parsing.
  DROP FUNCTION had to be partly merged with the old UDF code, because of the similar
  syntax.
  RETURN statement added, but still a no-op.
parent e2b0c0ac
......@@ -11,6 +11,8 @@ Summary of Not Yet Implemented:
- SQL-99 COMMIT (related to BEGIN/END)
- DECLARE CURSOR ...
- FOR-loops (as it requires cursors)
- CASCADE/RESTRICT for ALTER and DROP
- ALTER/DROP METHOD (as it implies User Defined Types)
Summary of what's implemented:
......@@ -66,15 +68,23 @@ List of what's implemented:
databases.)
Open questions:
Closed questions:
- What is the expected result when creating a procedure with a name that
already exists? An error or overwrite?
Answer: Error
- Do PROCEDUREs and FUNCTIONs share namespace or not? I think not, but the
we need to flag the type in the mysql.proc table and the name alone is
not a unique key any more, or, we have separate tables.
(Unfortunately, mysql.func is already taken. Use "sfunc" and maybe even
rename "proc" into "sproc" while we still can, for consistency?)
Answer: Same tables, with an additional key-field for the type.
Open questions:
- SQL-99 variables and parameters are typed. For the present we don't do
any type checking, since this is the way MySQL works. I still don't know
if we should keep it this way, or implement type checking.
if we should keep it this way, or implement type checking. Possibly we
should have optional, uset-settable, type checking.
......@@ -276,5 +276,5 @@
#define ER_SP_LABEL_MISMATCH 1257
#define ER_SP_UNINIT_VAR 1258
#define ER_SP_BADSELECT 1259
#define ER_ERROR_MESSAGES 260
#define ER_SP_BADRETURN 1260
#define ER_ERROR_MESSAGES 261
......@@ -251,8 +251,9 @@ if test ! -f $mdata/proc.frm
then
c_p="$c_p CREATE TABLE proc ("
c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
c_p="$c_p type enum('function','procedure') NOT NULL,"
c_p="$c_p body blob DEFAULT '' NOT NULL,"
c_p="$c_p PRIMARY KEY (name)"
c_p="$c_p PRIMARY KEY (name,type)"
c_p="$c_p )"
c_p="$c_p comment='Stored Procedures';"
fi
......
......@@ -375,4 +375,20 @@ create table test.t2 select * from test.t1;
insert into test.t2 values (concat(x, "2"), y+2);
end;
drop procedure create_select;
create function foo returns real soname "foo.so";
Can't open shared library 'foo.so' (errno: 22 foo.so: cannot open shared object file: No such file or director)
create function e() returns double
return 2.7182818284590452354;
drop function e;
create function fac(n int unsigned) returns bigint unsigned
begin
declare f bigint unsigned;
set f = 1;
while n > 1 do
set f = f * n;
set n = n - 1;
end while;
return f;
end;
drop function fac;
drop table t1;
......@@ -433,5 +433,32 @@ end|
#drop table t2|
drop procedure create_select|
# Check that we get the right error, i.e. UDF declaration parses correctly,
# but foo.so doesn't exist.
--error 1126
create function foo returns real soname "foo.so"|
# A minimal, constant FUNCTION.
create function e() returns double
return 2.7182818284590452354|
drop function e|
# A function with flow control and a RETURN statement
create function fac(n int unsigned) returns bigint unsigned
begin
declare f bigint unsigned;
set f = 1;
while n > 1 do
set f = f * n;
set n = n - 1;
end while;
return f;
end|
drop function fac|
delimiter ;|
drop table t1;
......@@ -312,8 +312,9 @@ then
c_p="$c_p CREATE TABLE proc ("
c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
c_p="$c_p type enum('function','procedure') NOT NULL,"
c_p="$c_p body blob DEFAULT '' NOT NULL,"
c_p="$c_p PRIMARY KEY (name)"
c_p="$c_p PRIMARY KEY (name,type)"
c_p="$c_p )"
c_p="$c_p comment='Stored Procedures';"
fi
......
......@@ -175,7 +175,7 @@ static SYMBOL symbols[] = {
{ "FOR", SYM(FOR_SYM),0,0},
{ "FULL", SYM(FULL),0,0},
{ "FULLTEXT", SYM(FULLTEXT_SYM),0,0},
{ "FUNCTION", SYM(UDF_SYM),0,0},
{ "FUNCTION", SYM(FUNCTION_SYM),0,0},
{ "GEOMETRY", SYM(GEOMETRY_SYM),0,0},
{ "GLOBAL", SYM(GLOBAL_SYM),0,0},
{ "GRANT", SYM(GRANT),0,0},
......@@ -332,7 +332,8 @@ static SYMBOL symbols[] = {
{ "USER_RESOURCES", SYM(RESOURCES),0,0},
{ "RESTORE", SYM(RESTORE_SYM),0,0},
{ "RESTRICT", SYM(RESTRICT),0,0},
{ "RETURNS", SYM(UDF_RETURNS_SYM),0,0},
{ "RETURN", SYM(RETURN_SYM),0,0},
{ "RETURNS", SYM(RETURNS_SYM),0,0},
{ "REVOKE", SYM(REVOKE),0,0},
{ "RIGHT", SYM(RIGHT),0,0},
{ "RLIKE", SYM(REGEXP),0,0}, /* Like in mSQL2 */
......
......@@ -270,3 +270,4 @@ v/*
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -264,3 +264,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -272,3 +272,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -266,3 +266,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -271,3 +271,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -263,3 +263,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -263,3 +263,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -263,3 +263,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -263,3 +263,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -265,3 +265,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -265,3 +265,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -264,3 +264,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -257,3 +257,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -269,3 +269,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -262,3 +262,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -261,3 +261,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -266,3 +266,4 @@
"End-label without match"
"Referring to uninitialized variable"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a stored FUNCTION"
......@@ -19,79 +19,170 @@
#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_procedure(THD *thd, Item_string *iname)
/*
*
* DB storage of Stored PROCEDUREs and FUNCTIONs
*
*/
static int
db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
enum thr_lock_type ltype, TABLE **tablep)
{
DBUG_ENTER("sp_find_procedure");
extern int yyparse(void *thd);
LEX *tmplex;
DBUG_ENTER("db_find_routine_aux");
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
TABLE *table;
TABLE_LIST tables;
const char *defstr;
String *name;
sp_head *sp = NULL;
byte key[65]; // We know name is 64 and the enum is 1 byte
uint keylen;
int ret;
// Put the key together
keylen= namelen;
if (keylen > sizeof(key)-1)
keylen= sizeof(key)-1;
memcpy(key, name, keylen);
memset(key+keylen, (int)' ', sizeof(key)-1 - keylen); // Pad with space
key[sizeof(key)-1]= type;
keylen= sizeof(key);
name = iname->const_string();
DBUG_PRINT("enter", ("name: %*s", name->length(), name->c_ptr()));
memset(&tables, 0, sizeof(tables));
tables.db= (char*)"mysql";
tables.real_name= tables.alias= (char*)"proc";
if (! (table= open_ltable(thd, &tables, TL_READ)))
DBUG_RETURN(NULL);
if (! (table= open_ltable(thd, &tables, ltype)))
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
if (table->file->index_read_idx(table->record[0], 0,
(byte*)name->c_ptr(), name->length(),
key, keylen,
HA_READ_KEY_EXACT))
goto done;
{
close_thread_tables(thd);
DBUG_RETURN(SP_KEY_NOT_FOUND);
}
*tablep= table;
if ((defstr= get_field(&thd->mem_root, table->field[1])) == NULL)
goto done;
DBUG_RETURN(SP_OK);
}
static int
db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
{
DBUG_ENTER("db_find_routine");
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
extern int yyparse(void *thd);
LEX *tmplex;
TABLE *table;
const char *defstr;
int ret;
// QQ Set up our own mem_root here???
ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table);
if (ret != SP_OK)
goto done;
if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
goto done; // Error
ret= SP_PARSE_ERROR;
else
sp = tmplex->sphead;
*sphp= tmplex->sphead;
done:
if (table)
if (ret == SP_OK && table)
close_thread_tables(thd);
DBUG_RETURN(sp);
DBUG_RETURN(ret);
}
int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen)
static int
db_create_routine(THD *thd, int type,
char *name, uint namelen, char *def, uint deflen)
{
DBUG_ENTER("sp_create_procedure");
DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
int ret= 0;
DBUG_ENTER("db_create_routine");
DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def));
int ret;
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= SP_OPEN_TABLE_FAILED;
else
{
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((longlong)type);
table->field[2]->store(def, deflen, default_charset_info);
if (table->file->write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
else
ret= SP_OK;
}
restore_record(table, 2); // Get default values for fields
if (ret == SP_OK && table)
close_thread_tables(thd);
DBUG_RETURN(ret);
}
table->field[0]->store(name, namelen, default_charset_info);
table->field[1]->store(def, deflen, default_charset_info);
static int
db_drop_routine(THD *thd, int type, char *name, uint namelen)
{
DBUG_ENTER("db_drop_routine");
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
TABLE *table;
int ret;
ret= table->file->write_row(table->record[0]);
ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table);
if (ret == SP_OK)
{
if (table->file->delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
}
if (ret == SP_OK && table)
close_thread_tables(thd);
DBUG_RETURN(ret);
}
/*
*
* PROCEDURE
*
*/
sp_head *
sp_find_procedure(THD *thd, LEX_STRING *name)
{
DBUG_ENTER("sp_find_procedure");
sp_head *sp;
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
name->str, name->length, &sp) != SP_OK)
sp= NULL;
DBUG_RETURN(sp);
}
int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen)
{
DBUG_ENTER("sp_create_procedure");
DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
int ret;
ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen);
done:
close_thread_tables(thd);
DBUG_RETURN(ret);
}
......@@ -100,26 +191,55 @@ sp_drop_procedure(THD *thd, char *name, uint namelen)
{
DBUG_ENTER("sp_drop_procedure");
DBUG_PRINT("enter", ("name: %*s", namelen, name));
TABLE *table;
TABLE_LIST tables;
int ret;
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;
ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);
DBUG_RETURN(ret);
}
if ((error= table->file->delete_row(table->record[0])))
table->file->print_error(error, MYF(0));
}
close_thread_tables(thd);
DBUG_RETURN(0);
err:
close_thread_tables(thd);
DBUG_RETURN(-1);
/*
*
* FUNCTION
*
*/
sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
DBUG_ENTER("sp_find_function_i");
sp_head *sp;
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
name->str, name->length, &sp) != SP_OK)
sp= NULL;
DBUG_RETURN(sp);
}
int
sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen)
{
DBUG_ENTER("sp_create_function");
DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
int ret;
ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen);
DBUG_RETURN(ret);
}
int
sp_drop_function(THD *thd, char *name, uint namelen)
{
DBUG_ENTER("sp_drop_function");
DBUG_PRINT("enter", ("name: %*s", namelen, name));
int ret;
ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);
DBUG_RETURN(ret);
}
......@@ -18,11 +18,17 @@
#ifndef _SP_H_
#define _SP_H_
//
// Finds a stored procedure given its name. Returns NULL if not found.
//
// Return codes from sp_create_* and sp_drop_*:
#define SP_OK 0
#define SP_KEY_NOT_FOUND -1
#define SP_OPEN_TABLE_FAILED -2
#define SP_WRITE_ROW_FAILED -3
#define SP_DELETE_ROW_FAILED -4
#define SP_GET_FIELD_FAILED -5
#define SP_PARSE_ERROR -6
sp_head *
sp_find_procedure(THD *thd, Item_string *name);
sp_find_procedure(THD *thd, LEX_STRING *name);
int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen);
......@@ -30,15 +36,14 @@ sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen);
int
sp_drop_procedure(THD *thd, char *name, uint namelen);
#if 0
sp_head *
sp_find_function(THD *thd, Item_string *name);
sp_find_function(THD *thd, LEX_STRING *name);
int
sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen);
int
sp_drop_function(THD *thd, char *name, uint namelen);
#endif
#endif /* _SP_H_ */
......@@ -102,11 +102,20 @@ sp_head::create(THD *thd)
DBUG_ENTER("sp_head::create");
String *name= m_name->const_string();
String *def= m_defstr->const_string();
int ret;
DBUG_PRINT("info", ("type: %d name: %s def: %s",
m_type, name->c_ptr(), def->c_ptr()));
if (m_type == TYPE_ENUM_FUNCTION)
ret= sp_create_function(thd,
name->c_ptr(), name->length(),
def->c_ptr(), def->length());
else
ret= sp_create_procedure(thd,
name->c_ptr(), name->length(),
def->c_ptr(), def->length());
DBUG_PRINT("info", ("name: %s def: %s", name->c_ptr(), def->c_ptr()));
DBUG_RETURN(sp_create_procedure(thd,
name->c_ptr(), name->length(),
def->c_ptr(), def->length()));
DBUG_RETURN(ret);
}
int
......@@ -127,7 +136,7 @@ sp_head::execute(THD *thd)
{
uint i;
List_iterator_fast<Item> li(m_call_lex->value_list);
Item *it = li++; // Skip first one, it's the procedure name
Item *it;
nctx = new sp_rcontext(csize);
if (! octx)
......@@ -184,7 +193,7 @@ sp_head::execute(THD *thd)
if (ret == 0 && csize > 0)
{
List_iterator_fast<Item> li(m_call_lex->value_list);
Item *it = li++; // Skip first one, it's the procedure name
Item *it;
// Copy back all OUT or INOUT values to the previous frame, or
// set global user variables
......@@ -273,23 +282,18 @@ sp_head::restore_lex(THD *thd)
// 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;
char *proc= thd->lex.udf.name.str;
while ((it= li++))
{
String *sold= it->val_str(NULL);
List_iterator_fast<char *> li(m_calls);
char **it;
if (stringcmp(snew, sold) == 0)
while ((it= li++))
if (strcasecmp(proc, *it) == 0)
break;
}
if (! it)
m_calls.push_back(proc);
m_calls.push_back(&proc);
}
// Merge used tables
......
......@@ -24,6 +24,11 @@
#include <stddef.h>
// Values for the type enum. This reflects the order of the enum declaration
// in the CREATE TABLE command.
#define TYPE_ENUM_FUNCTION 1
#define TYPE_ENUM_PROCEDURE 2
struct sp_label;
class sp_instr;
......@@ -35,8 +40,10 @@ class sp_head : public Sql_alloc
public:
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
enum enum_field_types m_returns; // For FUNCTIONs only
my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
List<Item_string> m_calls; // Called procedures.
List<char *> m_calls; // Called procedures.
List<char *> m_tables; // Used tables.
static void *operator new(size_t size)
......@@ -87,6 +94,15 @@ class sp_head : public Sql_alloc
void
backpatch(struct sp_label *);
char *name(uint *lenp = 0) const
{
String *n= m_name->const_string();
if (lenp)
*lenp= n->length();
return n->c_ptr();
}
private:
Item_string *m_name;
......@@ -99,7 +115,7 @@ class sp_head : public Sql_alloc
struct sp_label *lab;
sp_instr *instr;
} bp_t;
List<bp_t> m_backpatch; // Instructions needing backpaching
List<bp_t> m_backpatch; // Instructions needing backpatching
inline sp_instr *
get_instr(uint i)
......
......@@ -75,8 +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,
SQLCOM_CREATE_PROCEDURE, SQLCOM_CALL,
SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
/* This should be the last !!! */
SQLCOM_END
......
......@@ -2763,26 +2763,24 @@ mysql_execute_command(THD *thd)
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
case SQLCOM_CREATE_FUNCTION:
if (check_access(thd,INSERT_ACL,"mysql",0,1))
break;
case SQLCOM_CREATE_FUNCTION: // UDF function
{
if (check_access(thd,INSERT_ACL,"mysql",0,1))
break;
#ifdef HAVE_DLOPEN
if (!(res = mysql_create_function(thd,&lex->udf)))
send_ok(thd);
sp_head *sph= sp_find_function(thd, &lex->udf.name);
if (sph)
{
net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
goto error;
}
if (!(res = mysql_create_function(thd,&lex->udf)))
send_ok(thd);
#else
res= -1;
res= -1;
#endif
break;
case SQLCOM_DROP_FUNCTION:
if (check_access(thd,DELETE_ACL,"mysql",0,1))
break;
#ifdef HAVE_DLOPEN
if (!(res = mysql_drop_function(thd,&lex->udf.name)))
send_ok(thd);
#else
res= -1;
#endif
break;
}
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
......@@ -2951,7 +2949,7 @@ mysql_execute_command(THD *thd)
res= -1;
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
break;
case SQLCOM_CREATE_PROCEDURE:
case SQLCOM_CREATE_PROCEDURE: // FUNCTION too (but not UDF!)
if (!lex->sphead)
{
send_error(thd, ER_SP_NO_RECURSIVE_CREATE);
......@@ -2959,22 +2957,35 @@ mysql_execute_command(THD *thd)
}
else
{
uint namelen;
char *name= lex->sphead->name(&namelen);
udf_func *udf = find_udf(name, namelen);
if (udf)
{
net_printf(thd, ER_UDF_EXISTS, name);
goto error;
}
res= lex->sphead->create(thd);
if (res != 0)
switch (res)
{
case SP_OK:
send_ok(thd);
break;
case SP_WRITE_ROW_FAILED:
send_error(thd, ER_SP_ALREADY_EXISTS);
goto error;
default:
send_error(thd, ER_SP_STORE_FAILED);
goto error;
}
send_ok(thd);
}
break;
case SQLCOM_CALL:
{
Item_string *s;
sp_head *sp;
s= (Item_string*)lex->value_list.head();
sp= sp_find_procedure(thd, s);
sp= sp_find_procedure(thd, &lex->udf.name);
if (! sp)
{
send_error(thd, ER_SP_DOES_NOT_EXIST);
......@@ -3002,12 +3013,14 @@ mysql_execute_command(THD *thd)
}
break;
case SQLCOM_ALTER_PROCEDURE:
case SQLCOM_ALTER_FUNCTION:
{
Item_string *s;
sp_head *sp;
s= (Item_string*)lex->value_list.head();
sp= sp_find_procedure(thd, s);
if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
sp= sp_find_procedure(thd, &lex->udf.name);
else
sp= sp_find_function(thd, &lex->udf.name);
if (! sp)
{
send_error(thd, ER_SP_DOES_NOT_EXIST);
......@@ -3022,28 +3035,41 @@ mysql_execute_command(THD *thd)
}
break;
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
{
Item_string *s;
sp_head *sp;
s = (Item_string*)lex->value_list.head();
sp = sp_find_procedure(thd, s);
if (! sp)
{
send_error(thd, ER_SP_DOES_NOT_EXIST);
goto error;
}
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
else
{
String *name = s->const_string();
res= sp_drop_procedure(thd, name->c_ptr(), name->length());
if (res != 0)
res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
#ifdef HAVE_DLOPEN
if (res == SP_KEY_NOT_FOUND)
{
send_error(thd, ER_SP_DROP_FAILED);
goto error;
udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length);
if (udf)
{
if (check_access(thd, DELETE_ACL, "mysql", 0, 1))
goto error;
if (!(res = mysql_drop_function(thd,&lex->udf.name)))
{
send_ok(thd);
break;
}
}
}
#endif
}
switch (res)
{
case SP_OK:
send_ok(thd);
break;
case SP_KEY_NOT_FOUND:
send_error(thd, ER_SP_DOES_NOT_EXIST);
goto error;
default:
send_error(thd, ER_SP_DROP_FAILED);
goto error;
}
}
break;
......
......@@ -366,9 +366,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token FUNC_ARG1
%token FUNC_ARG2
%token FUNC_ARG3
%token UDF_RETURNS_SYM
%token RETURN_SYM
%token RETURNS_SYM
%token UDF_SONAME_SYM
%token UDF_SYM
%token FUNCTION_SYM
%token UNCOMMITTED_SYM
%token UNDERSCORE_CHARSET
%token UNICODE_SYM
......@@ -902,27 +903,23 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
| CREATE udf_func_type UDF_SYM IDENT
| CREATE udf_func_type FUNCTION_SYM IDENT
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = $4;
lex->udf.type= $2;
}
UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
{
LEX *lex=Lex;
lex->udf.returns=(Item_result) $7;
lex->udf.dl=$9.str;
}
create_function_tail
{}
| CREATE PROCEDURE ident
{
LEX *lex= Lex;
lex->spcont = new sp_pcontext();
lex->sphead = new sp_head(&$3, lex);
lex->spcont= new sp_pcontext();
lex->sphead= new sp_head(&$3, lex);
lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
}
'(' sp_dparam_list ')'
'(' sp_pdparam_list ')'
{
Lex->spcont->set_params();
}
......@@ -932,15 +929,43 @@ create:
}
;
create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.returns=(Item_result) $2;
lex->udf.dl=$4.str;
}
| '('
{
LEX *lex= Lex;
lex->sql_command = SQLCOM_CREATE_PROCEDURE;
lex->spcont= new sp_pcontext();
lex->sphead= new sp_head(&lex->udf.name, lex);
lex->sphead->m_type= TYPE_ENUM_FUNCTION;
}
sp_fdparam_list ')'
{
Lex->spcont->set_params();
}
RETURNS_SYM type
{
Lex->sphead->m_returns= (enum enum_field_types)$7;
}
sp_proc_stmt
{}
;
call:
CALL_SYM ident
{
LEX *lex = Lex;
lex->sql_command= SQLCOM_CALL;
lex->udf.name= $2;
lex->value_list.empty();
lex->value_list.push_back(
(Item*)new Item_string($2.str, $2.length, default_charset_info));
}
'(' sp_cparam_list ')' {}
;
......@@ -962,18 +987,36 @@ sp_cparams:
}
;
/* SP parameter declaration list */
sp_dparam_list:
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
| sp_dparams
| sp_fdparams
;
sp_dparams:
sp_dparams ',' sp_dparam
| sp_dparam
sp_fdparams:
sp_fdparams ',' sp_fdparam
| sp_fdparam
;
sp_dparam:
sp_fdparam:
ident type sp_opt_locator
{
Lex->spcont->push(&$1, (enum enum_field_types)$2, sp_param_in);
}
;
/* Stored PROCEDURE parameter declaration list */
sp_pdparam_list:
/* Empty */
| sp_pdparams
;
sp_pdparams:
sp_pdparams ',' sp_pdparam
| sp_pdparam
;
sp_pdparam:
sp_opt_inout ident type sp_opt_locator
{
Lex->spcont->push(&$2,
......@@ -1068,6 +1111,20 @@ sp_proc_stmt:
lex->sphead->restore_lex(YYTHD);
}
}
| RETURN_SYM expr
{
LEX *lex= Lex;
if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
{
send_error(YYTHD, ER_SP_BADRETURN);
YYABORT;
}
else
{
/* QQ nothing yet */
}
}
| IF sp_if END IF {}
| CASE_SYM WHEN_SYM
{
......@@ -1886,7 +1943,7 @@ alter:
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
}
| ALTER PROCEDURE opt_specific ident
| ALTER PROCEDURE ident
/* QQ Characteristics missing for now */
opt_restrict
{
......@@ -1895,18 +1952,10 @@ alter:
/* 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));
lex->udf.name= $3;
}
;
opt_specific:
/* Empty */
|
SPECIFIC_SYM
;
alter_list:
| alter_list_item
| alter_list ',' alter_list_item;
......@@ -3540,23 +3589,20 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
| DROP UDF_SYM IDENT
| DROP FUNCTION_SYM IDENT opt_restrict
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_FUNCTION;
lex->udf.name = $3;
lex->udf.name= $3;
}
| 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));
lex->udf.name= $3;
}
;
table_list:
table_name
| table_list ',' table_name;
......@@ -4583,7 +4629,7 @@ keyword:
| TIMESTAMP {}
| TIME_SYM {}
| TYPE_SYM {}
| UDF_SYM {}
| FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| USE_FRM {}
......
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