Merging 4.1 to 5.0.

BitKeeper/etc/gone:
  auto-union
BitKeeper/etc/logging_ok:
  auto-union
client/mysql.cc:
  Auto merged
mysql-test/r/show_check.result:
  Auto merged
mysql-test/r/subselect.result:
  Auto merged
mysql-test/r/variables.result:
  Auto merged
sql/item.cc:
  Auto merged
sql/lex.h:
  Auto merged
sql/log.cc:
  Auto merged
sql/mysql_priv.h:
  Auto merged
sql/mysqld.cc:
  Auto merged
sql/set_var.cc:
  Auto merged
sql/sql_acl.cc:
  Auto merged
sql/sql_class.h:
  Auto merged
sql/sql_delete.cc:
  Auto merged
sql/sql_lex.cc:
  Auto merged
sql/sql_load.cc:
  Auto merged
sql/sql_parse.cc:
  Auto merged
sql/sql_select.cc:
  Auto merged
sql/share/swedish/errmsg.txt:
  Auto merged
sql/sql_yacc.yy:
  Auto merged
This commit is contained in:
unknown 2003-04-04 13:45:35 +02:00
commit b60e6bcf2d
78 changed files with 5552 additions and 200 deletions

View File

@ -72,9 +72,11 @@ papa@gbichot.local
paul@central.snake.net
paul@teton.kitebird.com
pem@mysql.com
pem@per-erik-martins-dator.local
peter@linux.local
peter@mysql.com
pgulutzan@linux.local
pmartin@build.mysql2.com
ram@gw.udmsearch.izhnet.ru
ram@mysql.r18.ru
ram@ram.(none)

View File

@ -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

622
Docs/sp-imp-spec.txt Normal file
View File

@ -0,0 +1,622 @@
Implementation specification for Stored Procedures
==================================================
- How parsing and execution of queries work
In order to execute a query, the function sql_parse.cc:mysql_parse() is
called, which in turn calls the parser (yyparse()) with an updated Lex
structure as the result. mysql_parse() then calls mysql_execute_command()
which dispatches on the command code (in Lex) to the corresponding code for
executing that particular query.
There are thre structures involved in the execution of a query which are of
interest to the stored procedure implementation:
- Lex (mentioned above) is the "compiled" query, that is the output from
the parser and what is then interpreted to do the actual work.
It constains an enum value (sql_command) which is the query type, and
all the data collected by the parser needed for the execution (table
names, fields, values, etc).
- THD is the "run-time" state of a connection, containing all that is
needed for a particular client connection, and, among other things, the
Lex structure currently being executed.
- Item_*: During parsing, all data is translated into "items", objects of
the subclasses of "Item", such as Item_int, Item_real, Item_string, etc,
for basic datatypes, and also various more specialized Item types for
expressions to be evaluated (Item_func objects).
- How to fit Stored Procedure into this scheme
- An overview of the classes and files for stored procedures
(More detailed APIs at the end of this file)
- class sp_head (sp_head.{cc,h})
This contains, among other things, an array of "instructions" and the
method for executing the procedure.
- class sp_pcontext (sp_pcontext.{cc,h}
This is the parse context for the procedure. It's primarily used during
parsing to keep track of local parameters, variables and labels, but
it's also used at CALL time do find parameters mode (IN, OUT or INOUT)
and type when setting up the runtime context.
- class sp_instr (sp_head.{cc,h})
This is the base class for "instructions", that is, what is generated
by the parser. It turns out that we only need 4 different sub classes:
- sp_instr_stmt
Execute a statement. This is the "call-out" any normal SQL statement,
like a SELECT, INSERT etc. It contains the Lex structure for the
statement in question.
- sp_instr_set
Set the value of a local variable (or parameter)
- sp_instr_jump
An unconditional jump.
- sp_instr_jump_if_not
Jump if condition is not true. It turns out that the negative test is
most convenient when generating the code for the flow control
constructs.
- class sp_rcontext (sp_rcontext.h)
This is the runtime context in the THD structure.
It contains an array of items, the parameters and local variables for
the currently executing stored procedure.
This means that variable value lookup is in runtime is constant time,
a simple index operation.
- class Item_splocal (Item.{cc,h})
This is a subclass of Item. Its sole purpose is to hide the fact that
the real Item is actually in the current frame (runtime context).
It contains the frame offset and defers all methods to the real Item
in the frame. This is what the parser generates for local variables.
- Utility functions (sp.{cc,h})
This contains functions for creating, dropping and finding a stored
procedure in the mysql.proc table (or internal cache, when it is
implemented).
- Parsing CREATE PROCEDURE ...
When parsing a CREATE PROCEDURE the parser first initializes the
sphead and spcont (runtime context) fields in the Lex.
The sql_command code for the result of parsing a is
SQLCOM_CREATE_PROCEDURE.
The parsing of the parameter list and body is relatively
straight-forward:
- Parameters:
name, type and mode (IN/OUT/INOUT) is pushed to spcont
- Declared local variables:
Same as parameters (mode is then IN)
- Local Variable references:
If an identifier is found in in spcont, an Item_splocal is created
with the variable's frame index, otherwise an Item_field or Item_ref
is created (as before).
- Statements:
The Lex in THD is replaced by a new Lex structure and the statement,
is parsed as usual. A sp_instr_stmt is created, containing the new
Lex, and added to added to the instructions in sphead.
Afterwards, the procedure's Lex is restored in THD.
- SET var:
Setting a local variable generates a sp_instr_set instruction,
containing the variable's frame offset, the expression (an Item),
and the type.
- Flow control:
Flow control constructs like, IF, WHILE, etc, generate a conditional
and unconditional jumps in the "obvious" way, but a few notes may
be required:
- Forward jumps: When jumping forward, the exact destination is not
known at the time of the creation of the jump instruction. The
sphead therefore contains list of instruction-label pairs for
each forward reference. When the position later is known, the
instructions in the list are updated with the correct location.
- Loop constructs have optional labels. If a loop doesn't have a
label, an anonymous label is generated to simplify the parsing.
- There are two types of CASE. The "simple" case is implemented
with an anonymous variable bound to the value to be tested.
- An example
Parsing the procedure:
create procedure a(s char(16))
begin
declare x int;
set x = 3;
while x > 0 do
set x = x-1;
insert into db.tab values (x, s);
end while
end
would generate the following structures:
______
thd: | | _________
| lex -+--->| | ___________________
|______| | spcont -+------------------->| "s",in,char(16):0 |
| sphead -+------ |("x",in,int :1)|
|_________| | |___________________|
____V__________________
| m_name: "a" |
| m_defstr: "create ..."|
| m_instr: ... |
|_______________________|
Note that the contents of the spcont is changing during the parsing,
at all times reflecting the state of the would-be runtime frame.
The m_instr is an array of instructions:
Pos. Instruction
0 sp_instr_set(1, '3')
1 sp_instr_jump_if_not(5, 'x>0')
2 sp_instr_set(1, 'x-1')
3 sp_instr_stmt('insert into ...')
4 sp_instr_jump(1)
5 <end>
Here, '3', 'x>0', etc, represent the Items or Lex for the respective
expressions or statements.
- Parsing CREATE FUNCTION ...
Creating a functions is essensially the same thing as for a PROCEDURE,
with the addition that a FUNCTION has a return type and a RETURN
statement, but no OUT or INOUT parameters.
The main difference during parsing is that we store the result type
in the sp_head. However, there are big differences when it comes to
invoking a FUNCTION. (See below.)
- Storing, caching, dropping...
As seen above, the entired definition string, including the "CREATE
PROCEDURE" (or "FUNCTION") is kept. The procedure definition string is
stored in the table mysql.proc with the name and type as the key, the
type being one of the enum ("procedure","function").
A PROCEDURE is just stored int the mysql.proc table. A FUNCTION has an
additional requirement. They will be called in expressions with the same
syntax as UDFs, so UDFs and stored FUNCTIONs share the namespace. Thus,
we must make sure that we do not have UDFs and FUNCTIONs with the same
name (even if they are storded in different places).
This means that we can reparse the procedure as many time as we want.
The first time, the resulting Lex is used to store the procedure in
the database (using the function sp.c:sp_create_procedure()).
The simplest way would be to just leave it at that, and re-read the
procedure from the database each time it is called. (And in fact, that's
the way the earliest implementation will work.)
However, this is not very efficient, and we can do better. The full
implementation should work like this:
1) Upon creation time, parse and store the procedure. Note that we still
need to parse it to catch syntax errors, but we can't check if called
procedures exists for instance.
2) Upon first CALL, read from the database, parse it, and cache the
resulting Lex in memory. This time we can do more error checking.
3) Upon subsequent CALLs, use the cached Lex.
Note that this implies that the Lex structure with its sphead must be
reentrant, that is, reusable and shareable between different threads
and calls. The runtime state for a procedure is kept in the sp_rcontext
in THD.
The mechanisms of storing, finding, and dropping procedures are
encapsulated in the files sp.{cc,h}.
- CALLing a procedure
A CALL is parsed just like any statement. The resulting Lex has the
sql_command SQLCOM_CALL, the procedure's name and the parameters are
pushed to the Lex' value_list.
sql_parse.cc:mysql_execute_command() then uses sp.cc:sp_find() to
get the sp_head for the procedure (which may have been read from the
database or feetched from the in-memory cache) and calls the sp_head's
method execute().
Note: It's important that substatements called by the procedure do not
do send_ok(). Fortunately, there is a flag in THD->net to disable
this during CALLs. If a substatement fails, it will however send
an error back to the client, so the CALL mechanism must return
immediately and without sending an error.
The sp_head::execute() method works as follows:
1) Keep a pointer to the old runtime context in THD (if any)
2) Create a new runtime context. The information about the required size
is in sp_head's parse time context.
3) Push each parameter (from the CALL's Lex->value_list) to the new
context. If it's an OUT or INOUT parameter, the parameter's offset
in the caller's frame is set in the new context as well.
4) For each instruction, call its execute() method.
The result is a pointer to the next instruction to execute (or NULL)
if an error occured.
5) On success, set the new values of the OUT and INOUT parameters in
the caller's frame.
- USE database
Before executing the instruction we also keeps the current default
database (if any). If this was changed during execution (i.e. a "USE"
statement has been executed), we restore the current database to the
original.
This is the most useful way to handle USE in procedures. If we didn't,
the caller would find himself in a different database after calling
a function, which can be confusing.
Restoring the database also gives full freedom to the procedure writer:
- It's possible to write "general" procedures that are independent of
the actual database name.
- It's possible to write procedures that work on a particular database
by calling USE, without having to use fully qualified table names
everywhere (which doesn't help if you want to call other, "general",
procedures anyway).
- Evaluating Items
There are three occasions where we need to evaluate an expression:
- When SETing a variable
- When CALLing a procedure
- When testing an expression for a branch (in IF, WHILE, etc)
The semantics in stored procedures is "call-by-value", so we have to
evaluate any "func" Items at the point of the CALL or SET, otherwise
we would get a kind of "lazy" evaluation with unexpected results with
respect to OUT parameters for instance.
For this the support function, sp_head.cc:eval_func_item() is needed.
- Calling a FUNCTION
Functions don't have an explicit call keyword like procedures. Instead,
they appear in expressions with the conventional syntax "fun(arg, ...)".
The problem is that we already have User Defined Functions (UDFs) which
are called the same way. A UDF is detected by the lexical analyzer (not
the parser!), in the find_keyword() function, and returns a UDF_*_FUNC
or UDA_*_SUM token with the udf_func object as the yylval.
So, stored functions must be handled in a simpilar way, and as a
consequence, UDFs and functions must not have the same name.
- Detecting and parsing a FUNCTION invokation
The existance of UDFs are checked during the lexical analysis (in
sql_lex.cc:find_keyword()). This has the drawback that they must
exist before they are refered to, which was ok before SPs existed,
but then it becomes a problem. The first implementation of SP FUNCTIONs
will work the same way, but this should be fixed a.s.a.p. (This will
required some reworking of the way UDFs are handled, which is why it's
not done from the start.)
For the time being, a FUNCTION is detected the same way, and returns
the token SP_FUNC. During the parsing we only check for the *existance*
of the function, we don't parse it, since wa can't call the parser
recursively.
When encountering a SP_FUNC with parameters in the expression parser,
an instance of the new Item_func_sp class is created. Unlike UDFs, we
don't have different classes for different return types, since we at
this point don't know the type.
- Collecting FUNCTIONs to invoke
A FUNCTION differs from a PROCEDURE in one important aspect: Whereas a
PROCEDURE is CALLed as statement by itself, a FUNCTION is invoked
"on-the-fly" during the execution of *another* statement.
This makes things a lot more complicated compared to CALL:
- We can't read and parse the FUNCTION from the mysql.proc table at the
point of invokation; the server requires that all tables used are
opened and locked at the beginning of the query execution.
One "obvious" solution would be to simply push "mysql.proc" to the list
of tables used by the query, but this implies a "join" with this table
if the query is a select, so it doesn't work (and we can't exclude this
table easily; since a priviledged used might in fact want to search
the proc table).
Another solution would of course be to allow the opening and closing
of the mysql.proc table during a query execution, but this it not
possible at the present.
So, the solution is to collect the names of the refered FUNCTIONs during
parsing in the lex.
Then, before doing anything else in mysql_execute_command(), read all
functions from the database an keep them in the THD, where the function
sp_find_function() can find them during the execution.
Note: Even when a global in-memory cache is implemented, we must still
make sure that the functions are indeed read and cached at this point.
The code that read and cache functions from the database must also be
invoked recursively for each read FUNCTION to make sure we have *all* the
functions we need.
In the absence of the real in-memory cache for SPs, a temporary solution
has been implemented with a per-THD cache for just FUNCTIONs. This is
handled by the functions
void sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
void sp_merge_funs(LEX *dst, LEX *src);
int sp_cache_functions(THD *thd, LEX *lex);
void sp_clear_function_cache(THD *thd);
in sp.cc.
- Parsing DROP PROCEDURE/FUNCTION
The procedure name is pushed to Lex->value_list.
The sql_command code for the result of parsing a is
SQLCOM_DROP_PROCEDURE/SQLCOM_DROP_FUNCTION.
Dropping is done by simply getting the procedure with the sp_find()
function and calling sp_drop() (both in sp.{cc,h}).
DROP PROCEDURE/FUNCTION also supports the non-standard "IF EXISTS",
analogous to other DROP statements in MySQL.
- Class and function APIs
- The parser context: sp_pcontext.h
typedef enum
{
sp_param_in,
sp_param_out,
sp_param_inout
} sp_param_mode_t;
typedef struct
{
Item_string *name;
enum enum_field_types type;
sp_param_mode_t mode;
uint offset; // Offset in current frame
my_bool isset;
} sp_pvar_t;
class sp_pcontext
{
sp_pcontext();
// Return the maximum frame size
uint max_framesize();
// Return the current frame size
uint current_framesize();
// Return the number of parameters
uint params();
// Set the number of parameters to the current frame size
void set_params();
// Set type of the variable at offset 'i' in the frame
void set_type(uint i, enum enum_field_types type);
// Mark the i:th variable to "set" (i.e. having a value) with
// 'val' true.
void set_isset(uint i, my_bool val);
// Push the variable 'name' to the frame.
void push(LEX_STRING *name,
enum enum_field_types type, sp_param_mode_t mode);
// Pop 'num' variables from the frame.
void pop(uint num = 1);
// Find variable by name
sp_pvar_t *find_pvar(LEX_STRING *name);
// Find variable by index
sp_pvar_t *find_pvar(uint i);
// Push label 'name' of instruction index 'ip' to the label context
sp_label_t *push_label(char *name, uint ip);
// Find label 'name' in the context
sp_label_t *find_label(char *name);
// Return the last pushed label
sp_label_t *last_label();
// Return and remove the last pushed label.
sp_label_t *pop_label();
}
- The run-time context (call frame): sp_rcontext.h
class sp_rcontext
{
// 'size' is the max size of the context
sp_rcontext(uint size);
// Push value (parameter) 'i' to the frame
void push_item(Item *i);
// Set slot 'idx' to value 'i'
void set_item(uint idx, Item *i);
// Return the item in slot 'idx'
Item *get_item(uint idx);
// Set the "out" index 'oidx' for slot 'idx. If it's an IN slot,
// use 'oidx' -1.
void set_oindex(uint idx, int oidx);
// Return the "out" index for slot 'idx'
int get_oindex(uint idx);
// Set the FUNCTION result
void set_result(Item *i);
// Get the FUNCTION result
Item *get_result();
}
- The procedure: sp_head.h
#define TYPE_ENUM_FUNCTION 1
#define TYPE_ENUM_PROCEDURE 2
class sp_head
{
int m_type; // TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE
sp_head(LEX_STRING *name, LEX*);
// Store this procedure in the database. This is a wrapper around
// the function sp_create_procedure().
int create(THD *);
// Invoke a FUNCTION
int
execute_function(THD *thd, Item **args, uint argcount, Item **resp);
// CALL a PROCEDURE
int
execute_procedure(THD *thd, List<Item> *args);
// Add the instruction to this procedure.
void add_instr(sp_instr *);
// Return the number of instructions.
uint instructions();
// Resets lex in 'thd' and keeps a copy of the old one.
void reset_lex(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 *);
// 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 *);
}
- Instructions
- The base class:
class sp_instr
{
// 'ip' is the index of this instruction
sp_instr(uint ip);
// 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 *, uint *nextp)
}
- Statement instruction:
class sp_instr_stmt : public sp_instr
{
sp_instr_stmt(uint ip);
int execute(THD *, uint *nextp);
// Set the statement's Lex
void set_lex(LEX *);
// Return the statement's Lex
LEX *get_lex();
}
- SET instruction:
class sp_instr_set : public sp_instr
{
// 'offset' is the variable's frame offset, 'val' the value,
// and 'type' the variable type.
sp_instr_set(uint ip,
uint offset, Item *val, enum enum_field_types type);
int execute(THD *, uint *nextp);
}
- Unconditional jump
class sp_instr_jump : public sp_instr
{
// No destination, must be set.
sp_instr_jump(uint ip);
// 'dest' is the destination instruction index.
sp_instr_jump(uint ip, uint dest);
int execute(THD *, uint *nextp);
// Set the destination instruction 'dest'.
void set_destination(uint dest);
}
- Conditional jump
class sp_instr_jump_if_not : public sp_instr_jump
{
// Jump if 'i' evaluates to false. Destination not set yet.
sp_instr_jump_if_not(uint ip, Item *i);
// Jump to 'dest' if 'i' evaluates to false.
sp_instr_jump_if_not(uint ip, Item *i, uint dest)
int execute(THD *, uint *nextp);
}
- Return a function value
class sp_instr_return : public sp_instr
{
// Return the value 'val'
sp_instr_return(uint ip, Item *val, enum enum_field_types type);
int execute(THD *thd, uint *nextp);
}
- Utility functions: sp.h
#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
// Finds a stored procedure given its name. Returns NULL if not found.
sp_head *sp_find_procedure(THD *, LEX_STRING *name);
// Store the procedure 'name' in the database. 'def' is the complete
// definition string ("create procedure ...").
int sp_create_procedure(THD *,
char *name, uint namelen,
char *def, uint deflen);
// Drop the procedure 'name' from the database.
int sp_drop_procedure(THD *, char *name, uint namelen);
// Finds a stored function given its name. Returns NULL if not found.
sp_head *sp_find_function(THD *, LEX_STRING *name);
// Store the function 'name' in the database. 'def' is the complete
// definition string ("create function ...").
int sp_create_function(THD *,
char *name, uint namelen,
char *def, uint deflen);
// Drop the function 'name' from the database.
int sp_drop_function(THD *, char *name, uint namelen);
--

99
Docs/sp-implemented.txt Normal file
View File

@ -0,0 +1,99 @@
Stored Procedures implemented 2003-03-07:
Summary of Not Yet Implemented:
- SQL queries (like SELECT, INSERT, UPDATE etc) in FUNCTION bodies
- External languages
- Access control
- Routine characteristics (mostly used for external languages)
- Prepared SP caching; SPs are fetched and reparsed at each call
- 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)
- CONDITIONs, HANDLERs, SIGNAL and RESIGNAL (will probably not be implemented)
Summary of what's implemented:
- SQL PROCEDUREs/FUNCTIONs (CREATE/DROP)
- CALL
- DECLARE of local variables
- BEGIN/END, SET, CASE, IF, LOOP, WHILE, REPEAT, ITERATE, LEAVE
- SELECT INTO local variables
- "Non-query" FUNCTIONs only
List of what's implemented:
- CREATE PROCEDURE|FUNCTION name ( args ) body
No routine characteristics yet.
- ALTER PROCEDURE|FUNCTION name ...
Is parsed, but a no-op (as there are no characteristics implemented yet).
CASCADE/RESTRICT is not implemented (and CASCADE probably will not be).
- DROP PROCEDURE|FUNCTION [IF EXISTS] name
CASCADE/RESTRICT is not implemented (and CASCADE probably will not be).
- CALL name (args)
OUT and INOUT parameters are only supported for local variables, and
therefore only useful when calling such procedures from within another
procedure.
Note: For the time being, when a procedure with OUT/INOUT parameter is
called, the out values are silently discarded. In the future, this
will either generate an error message, or it might even work to
call all procedures from the top-level.
- Function/Procedure body:
- BEGIN/END
Is parsed, but not the real thing with (optional) transaction
control, it only serves as block syntax for multiple statements (and
local variable binding).
Note: Multiple statements requires a client that can send bodies
containing ";". This is handled in the CLI clients mysql and
mysqltest with the "delimiter" command. Changing the end-of-query
delimiter ";" to for instance "|" allows ";" to be used in the
routine body.
- SET of local variables
Implemented as part of the pre-existing SET syntax. This allows an
extended syntax of "SET a=x, b=y, ..." where different variable types
(SP local and global) can be mixed. This also allows combinations
of local variables and some options that only make sense for
global/system variables; in that case the options are accepted but
ignored.
- The flow control constructs: CASE, IF, LOOP, WHILE, ITERATE and LEAVE
are fully implemented.
- SELECT ... INTO local variables (as well as global session variables)
is implemented. (Note: This is not SQL-99 feature, but common in other
databases.)
- A FUNCTION can have flow control contructs, but must not contain
an SQL query, like SELECT, INSERT, UPDATE, etc. The reason is that it's
hard to allow this is that a FUNCTION is executed as part of another
query (unlike a PROCEDURE, which is called as a statement). The table
locking scheme used makes it difficult to allow "subqueries" during
FUNCTION invokation.
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/issues:
- 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. Possibly we
should have optional, uset-settable, type checking.

View File

@ -104,6 +104,7 @@ extern "C" {
#include "completion_hash.h"
#define PROMPT_CHAR '\\'
#define DEFAULT_DELIMITER ';'
typedef struct st_status
{
@ -156,6 +157,7 @@ static char pager[FN_REFLEN], outfile[FN_REFLEN];
static FILE *PAGER, *OUTFILE;
static MEM_ROOT hash_mem_root;
static uint prompt_counter;
static char delimiter= DEFAULT_DELIMITER;
#ifdef HAVE_SMEM
static char *shared_memory_base_name=0;
@ -183,7 +185,7 @@ static int com_quit(String *str,char*),
com_use(String *str,char*), com_source(String *str, char*),
com_rehash(String *str, char*), com_tee(String *str, char*),
com_notee(String *str, char*),
com_prompt(String *str, char*);
com_prompt(String *str, char*), com_delimiter(String *str, char*);
#ifdef USE_POPEN
static int com_nopager(String *str, char*), com_pager(String *str, char*),
@ -251,7 +253,8 @@ static COMMANDS commands[] = {
"Set outfile [to_outfile]. Append everything into given outfile." },
{ "use", 'u', com_use, 1,
"Use another database. Takes database name as argument." },
{ "delimiter", 'd', com_delimiter, 1,
"Set query delimiter. " },
/* Get bash-like expansion for some commands */
{ "create table", 0, 0, 0, ""},
{ "create database", 0, 0, 0, ""},
@ -921,7 +924,7 @@ static COMMANDS *find_command (char *name,char cmd_char)
{
while (my_isspace(charset_info,*name))
name++;
if (strchr(name,';') || strstr(name,"\\g"))
if (strchr(name, delimiter) || strstr(name,"\\g"))
return ((COMMANDS *) 0);
if ((end=strcont(name," \t")))
{
@ -1000,7 +1003,7 @@ static bool add_line(String &buffer,char *line,char *in_string,
return 1; // Quit
if (com->takes_params)
{
for (pos++ ; *pos && *pos != ';' ; pos++) ; // Remove parameters
for (pos++ ; *pos && *pos != delimiter; pos++) ; // Remove parameters
if (!*pos)
pos--;
}
@ -1016,7 +1019,7 @@ static bool add_line(String &buffer,char *line,char *in_string,
continue;
}
}
else if (!*ml_comment && inchar == ';' && !*in_string)
else if (!*ml_comment && inchar == delimiter && !*in_string)
{ // ';' is end of command
if (out != line)
buffer.append(line,(uint) (out-line)); // Add this line
@ -1533,7 +1536,7 @@ com_help(String *buffer __attribute__((unused)),
for (i = 0; commands[i].name; i++)
{
if (commands[i].func)
tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
tee_fprintf(stdout, "%-10s(\\%c)\t%s\n", commands[i].name,
commands[i].cmd_char, commands[i].doc);
}
}
@ -2345,6 +2348,37 @@ static int com_source(String *buffer, char *line)
return error;
}
/* ARGSUSED */
static int
com_delimiter(String *buffer __attribute__((unused)), char *line)
{
char *tmp;
char buff[256];
if (strlen(line)> 255)
{
put_info("'DELIMITER' command was too long.", INFO_ERROR);
return 0;
}
bzero(buff, sizeof(buff));
strmov(buff, line);
tmp= get_arg(buff, 0);
if (!tmp || !*tmp)
{
put_info("DELIMITER must be followed by a 'delimiter' char", INFO_ERROR);
return 0;
}
if (strlen(tmp)> 1)
{
put_info("Argument must be one char", INFO_ERROR);
return 0;
}
delimiter= *tmp;
return 0;
}
/* ARGSUSED */
static int

View File

@ -89,6 +89,7 @@
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
#define DEFAULT_DELIMITER ';'
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC,
@ -126,6 +127,8 @@ static int block_ok_stack[BLOCK_STACK_DEPTH];
static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors;
static CHARSET_INFO *charset_info= &my_charset_latin1;
static char delimiter= DEFAULT_DELIMITER;
DYNAMIC_ARRAY q_lines;
#include "sslopt-vars.h"
@ -202,7 +205,7 @@ Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
Q_WAIT_FOR_SLAVE_TO_STOP,
Q_REQUIRE_VERSION,
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
Q_ENABLE_INFO, Q_DISABLE_INFO,
Q_ENABLE_INFO, Q_DISABLE_INFO, Q_DELIMITER,
Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
Q_COMMENT_WITH_COMMAND
@ -265,6 +268,7 @@ const char *command_names[]=
"disable_warnings",
"enable_info",
"disable_info",
"delimiter",
0
};
@ -1532,6 +1536,16 @@ int do_while(struct st_query* q)
return 0;
}
int do_delimiter(char *p)
{
while (*p && my_isspace(charset_info,*p))
p++;
if (!*p)
die("Missing delimiter character\n");
delimiter=*p;
return 0;
}
int safe_copy_unescape(char* dest, char* src, int size)
{
@ -1611,7 +1625,7 @@ int read_line(char* buf, int size)
switch(state) {
case R_NORMAL:
/* Only accept '{' in the beginning of a line */
if (c == ';')
if (c == delimiter)
{
*p = 0;
return 0;
@ -1651,7 +1665,7 @@ int read_line(char* buf, int size)
*buf = 0;
return 0;
}
else if (c == ';' || c == '{')
else if (c == delimiter || c == '{')
{
*p = 0;
return 0;
@ -1671,7 +1685,7 @@ int read_line(char* buf, int size)
state = R_ESC_SLASH_Q1;
break;
case R_ESC_Q_Q1:
if (c == ';')
if (c == delimiter)
{
*p = 0;
return 0;
@ -1692,7 +1706,7 @@ int read_line(char* buf, int size)
state = R_ESC_SLASH_Q2;
break;
case R_ESC_Q_Q2:
if (c == ';')
if (c == delimiter)
{
*p = 0;
return 0;
@ -2545,6 +2559,9 @@ int main(int argc, char** argv)
do_sync_with_master2("");
break;
}
case Q_DELIMITER:
do_delimiter(q->first_argument);
break;
case Q_COMMENT: /* Ignore row */
case Q_COMMENT_WITH_COMMAND:
break;

View File

@ -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

View File

@ -271,4 +271,18 @@
#define ER_SLAVE_WAS_RUNNING 1252
#define ER_SLAVE_WAS_NOT_RUNNING 1253
#define ER_CUT_VALUE_GROUP_CONCAT 1254
#define ER_ERROR_MESSAGES 255
#define ER_SP_NO_RECURSIVE_CREATE 1255
#define ER_SP_ALREADY_EXISTS 1256
#define ER_SP_DOES_NOT_EXIST 1257
#define ER_SP_DROP_FAILED 1258
#define ER_SP_STORE_FAILED 1259
#define ER_SP_LILABEL_MISMATCH 1260
#define ER_SP_LABEL_REDEFINE 1261
#define ER_SP_LABEL_MISMATCH 1262
#define ER_SP_UNINIT_VAR 1263
#define ER_SP_BADSELECT 1264
#define ER_SP_BADRETURN 1265
#define ER_SP_BADQUERY 1266
#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1267
#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1268
#define ER_ERROR_MESSAGES 269

View File

@ -55,7 +55,8 @@ sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \
sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \
sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
spatial.cc gstream.cc sql_help.cc
spatial.cc gstream.cc sql_help.cc \
sp_head.cc sp_pcontext.cc sp.cc
EXTRA_DIST = lib_vio.c

View File

@ -69,6 +69,7 @@ c_t="" c_c=""
c_hl="" c_hl=""
c_hc="" c_hc=""
c_clr="" c_clr=""
c_p=""
# Check for old tables
if test ! -f $mdata/db.frm
@ -246,6 +247,17 @@ then
c_hc="$c_hc comment='categories of help topics';"
fi
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,type)"
c_p="$c_p )"
c_p="$c_p comment='Stored Procedures';"
fi
mysqld_boot=" $execdir/mysqld --no-defaults --bootstrap --skip-grant-tables \
--basedir=$basedir --datadir=$ldata --skip-innodb --skip-bdb $EXTRA_ARG"
echo "running $mysqld_boot"
@ -270,6 +282,9 @@ $c_c
$c_hl
$c_hc
$c_clr
$c_p
END_OF_DATA
then
exit 0

View File

@ -7,6 +7,7 @@ help_category
help_relation
help_topic
host
proc
tables_priv
user
show tables;
@ -22,6 +23,7 @@ help_category
help_relation
help_topic
host
proc
tables_priv
user
show tables;
@ -37,6 +39,7 @@ help_category
help_relation
help_topic
host
proc
tables_priv
user
show tables;

View File

@ -64,3 +64,16 @@ use test_$1;
create table t1 (c int);
insert into test_$1.t1 set test_$1.t1.c = '1';
drop database test_$1;
use test;
drop table if exists t1,t2,t3;
create table t1(id1 int not null auto_increment primary key, t char(12));
create table t2(id2 int not null, t char(12));
create table t3(id3 int not null, t char(12), index(id3));
select count(*) from t2;
count(*)
500
insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
select count(*) from t2;
count(*)
25500
drop table if exists t1,t2,t3;

View File

@ -127,6 +127,7 @@ insert into t1 values (1);
show open tables;
Database Table In_use Name_locked
test t1 0 0
mysql proc 0 0
drop table t1;
create table t1 (a int not null, b VARCHAR(10), INDEX (b) ) AVG_ROW_LENGTH=10 CHECKSUM=1 COMMENT="test" TYPE=MYISAM MIN_ROWS=10 MAX_ROWS=100 PACK_KEYS=1 DELAY_KEY_WRITE=1 ROW_FORMAT=fixed;
show create table t1;

View File

@ -0,0 +1,73 @@
delete from mysql.proc;
create procedure proc1()
set @x = 42;
create function func1() returns int
return 42;
create procedure foo()
create procedure bar() set @x=3;
Can't create a PROCEDURE from within another stored routine
create procedure foo()
create function bar() returns double return 2.3;
Can't create a FUNCTION from within another stored routine
create procedure proc1()
set @x = 42;
PROCEDURE proc1 already exists
create function func1() returns int
return 42;
FUNCTION func1 already exists
alter procedure foo;
PROCEDURE foo does not exist
alter function foo;
FUNCTION foo does not exist
drop procedure foo;
PROCEDURE foo does not exist
drop function foo;
FUNCTION foo does not exist
call foo();
PROCEDURE foo does not exist
drop procedure if exists foo;
Warnings:
Warning 1256 PROCEDURE foo does not exist
create procedure foo()
foo: loop
leave bar;
end loop;
LEAVE with no matching label: bar
create procedure foo()
foo: loop
iterate bar;
end loop;
ITERATE with no matching label: bar
create procedure foo()
foo: loop
foo: loop
set @x=2;
end loop foo;
end loop foo;
Redefining label foo
create procedure foo()
foo: loop
set @x=2;
end loop bar;
End-label bar without match
create procedure foo(out x int)
begin
declare y int;
set x = y;
end;
Referring to uninitialized variable y
create procedure foo(x int)
select * from test.t1;
SELECT in a stored procedure must have INTO
create procedure foo()
return 42;
RETURN is only allowed in a FUNCTION
create function foo() returns int
begin
declare x int;
select max(c) into x from test.t;
return x;
end;
Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION
drop procedure proc1;
drop function func1;

503
mysql-test/r/sp.result Normal file
View File

@ -0,0 +1,503 @@
use test;
drop table if exists t1;
drop table if exists t2;
create table t1 (
id char(16) not null,
data int not null
);
create table t2 (
s char(16) not null,
i int not null,
d double not null
);
create procedure foo42()
insert into test.t1 values ("foo", 42);
call foo42();
select * from t1;
id data
foo 42
delete from t1;
drop procedure foo42;
create procedure u()
use sptmp;
drop database if exists sptmp;
create database sptmp;
use test;
call u();
select database();
database()
test
drop database sptmp;
drop procedure u;
create procedure bar(x char(16), y int)
insert into test.t1 values (x, y);
call bar("bar", 666);
select * from t1;
id data
bar 666
delete from t1;
create procedure two(x1 char(16), x2 char(16), y int)
begin
insert into test.t1 values (x1, y);
insert into test.t1 values (x2, y);
end;
call two("one", "two", 3);
select * from t1;
id data
one 3
two 3
delete from t1;
drop procedure two;
create procedure locset(x char(16), y int)
begin
declare z1, z2 int;
set z1 = y;
set z2 = z1+2;
insert into test.t1 values (x, z2);
end;
call locset("locset", 19);
select * from t1;
id data
locset 21
delete from t1;
drop procedure locset;
create procedure mixset(x char(16), y int)
begin
declare z int;
set @z = y, z = 666, max_join_size = 100;
insert into test.t1 values (x, z);
end;
call mixset("mixset", 19);
show variables like 'max_join_size';
Variable_name Value
max_join_size 100
select id,data,@z from t1;
id data @z
mixset 666 19
delete from t1;
drop procedure mixset;
create procedure zip(x char(16), y int)
begin
declare z int;
call zap(y, z);
call bar(x, z);
end;
create procedure zap(x int, out y int)
begin
declare z int;
set z = x+1, y = z;
end;
call zip("zip", 99);
select * from t1;
id data
zip 100
delete from t1;
drop procedure zip;
drop procedure zap;
drop procedure bar;
create procedure iotest(x1 char(16), x2 char(16), y int)
begin
call inc2(x2, y);
insert into test.t1 values (x1, y);
end;
create procedure inc2(x char(16), y int)
begin
call inc(y);
insert into test.t1 values (x, y);
end;
create procedure inc(inout io int)
set io = io + 1;
call iotest("io1", "io2", 1);
select * from t1;
id data
io2 2
io1 1
delete from t1;
drop procedure iotest;
drop procedure inc2;
drop procedure inc;
create procedure cbv1()
begin
declare y int default 3;
call cbv2(y+1, y);
insert into test.t1 values ("cbv1", y);
end;
create procedure cbv2(y1 int, inout y2 int)
begin
set y2 = 4711;
insert into test.t1 values ("cbv2", y1);
end;
call cbv1();
select * from t1;
id data
cbv2 4
cbv1 4711
delete from t1;
drop procedure cbv1;
drop procedure cbv2;
create procedure a0(x int)
while x do
set x = x-1;
insert into test.t1 values ("a0", x);
end while;
call a0(3);
select * from t1;
id data
a0 2
a0 1
a0 0
delete from t1;
drop procedure a0;
create procedure a(x int)
while x > 0 do
set x = x-1;
insert into test.t1 values ("a", x);
end while;
call a(3);
select * from t1;
id data
a 2
a 1
a 0
delete from t1;
drop procedure a;
create procedure b(x int)
repeat
insert into test.t1 values (repeat("b",3), x);
set x = x-1;
until x = 0 end repeat;
call b(3);
select * from t1;
id data
bbb 3
bbb 2
bbb 1
delete from t1;
drop procedure b;
create procedure b2(x int)
repeat(select 1 into outfile 'b2');
insert into test.t1 values (repeat("b2",3), x);
set x = x-1;
until x = 0 end repeat;
drop procedure b2;
create procedure c(x int)
hmm: while x > 0 do
insert into test.t1 values ("c", x);
set x = x-1;
iterate hmm;
insert into test.t1 values ("x", x);
end while hmm;
call c(3);
select * from t1;
id data
c 3
c 2
c 1
delete from t1;
drop procedure c;
create procedure d(x int)
hmm: while x > 0 do
insert into test.t1 values ("d", x);
set x = x-1;
leave hmm;
insert into test.t1 values ("x", x);
end while hmm;
call d(3);
select * from t1;
id data
d 3
delete from t1;
drop procedure d;
create procedure e(x int)
foo: loop
if x = 0 then
leave foo;
end if;
insert into test.t1 values ("e", x);
set x = x-1;
end loop foo;
call e(3);
select * from t1;
id data
e 3
e 2
e 1
delete from t1;
drop procedure e;
create procedure f(x int)
if x < 0 then
insert into test.t1 values ("f", 0);
elseif x = 0 then
insert into test.t1 values ("f", 1);
else
insert into test.t1 values ("f", 2);
end if;
call f(-2);
call f(0);
call f(4);
select * from t1;
id data
f 0
f 1
f 2
delete from t1;
drop procedure f;
create procedure g(x int)
case
when x < 0 then
insert into test.t1 values ("g", 0);
when x = 0 then
insert into test.t1 values ("g", 1);
else
insert into test.t1 values ("g", 2);
end case;
call g(-42);
call g(0);
call g(1);
select * from t1;
id data
g 0
g 1
g 2
delete from t1;
drop procedure g;
create procedure h(x int)
case x
when 0 then
insert into test.t1 values ("h0", x);
when 1 then
insert into test.t1 values ("h1", x);
else
insert into test.t1 values ("h?", x);
end case;
call h(0);
call h(1);
call h(17);
select * from t1;
id data
h0 0
h1 1
h? 17
delete from t1;
drop procedure h;
create procedure into_test(x char(16), y int)
begin
insert into test.t1 values (x, y);
select id,data into x,y from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end;
call into_test("into", 100);
select * from t1;
id data
into 100
into2 102
delete from t1;
drop procedure into_test;
create procedure into_test2(x char(16), y int)
begin
insert into test.t1 values (x, y);
select id,data into x,@z from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end;
call into_test2("into", 100);
select id,data,@z from t1;
id data @z
into 100 100
into2 102 100
delete from t1;
drop procedure into_test2;
create procedure into_outfile(x char(16), y int)
begin
insert into test.t1 values (x, y);
select * into outfile "/tmp/spout" from test.t1;
insert into test.t1 values (concat(x, "2"), y+2);
end;
call into_outfile("ofile", 1);
delete from t1;
drop procedure into_outfile;
create procedure into_dumpfile(x char(16), y int)
begin
insert into test.t1 values (x, y);
select * into dumpfile "/tmp/spdump" from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end;
call into_dumpfile("dfile", 1);
delete from t1;
drop procedure into_dumpfile;
create procedure create_select(x char(16), y int)
begin
insert into test.t1 values (x, y);
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 e() returns double
return 2.7182818284590452354;
set @e = e();
select e(), @e;
e() @e
2.718281828459 2.718281828459
create function inc(i int) returns int
return i+1;
select inc(1), inc(99), inc(-71);
inc(1) inc(99) inc(-71)
2 100 -70
create function mul(x int, y int) returns int
return x*y;
select mul(1,1), mul(3,5), mul(4711, 666);
mul(1,1) mul(3,5) mul(4711, 666)
1 15 3137526
create function append(s1 char(8), s2 char(8)) returns char(16)
return concat(s1, s2);
select append("foo", "bar");
append("foo", "bar")
foobar
create function fac(n int unsigned) returns bigint unsigned
begin
declare f bigint unsigned default 1;
while n > 1 do
set f = f * n;
set n = n - 1;
end while;
return f;
end;
select fac(1), fac(2), fac(5), fac(10);
fac(1) fac(2) fac(5) fac(10)
1 2 120 3628800
create function fun(d double, i int, u int unsigned) returns double
return mul(inc(i), fac(u)) / e();
select fun(2.3, 3, 5);
fun(2.3, 3, 5)
176.58213176229
insert into t2 values (append("xxx", "yyy"), mul(4,3), e());
insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6));
select * from t2 where s = append("a", "b");
s i d
ab 24 1324.36598821719
select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2);
s i d
xxxyyy 12 2.71828182845905
ab 24 1324.36598821719
select * from t2 where d = e();
s i d
xxxyyy 12 2.71828182845905
select * from t2;
s i d
xxxyyy 12 2.71828182845905
ab 24 1324.36598821719
delete from t2;
drop function e;
drop function inc;
drop function mul;
drop function append;
drop function fun;
drop table if exists fac;
create table fac (n int unsigned not null primary key, f bigint unsigned);
create procedure ifac(n int unsigned)
begin
declare i int unsigned default 1;
if n > 20 then
set n = 20; # bigint overflow otherwise
end if;
while i <= n do
begin
insert into test.fac values (i, fac(i));
set i = i + 1;
end;
end while;
end;
call ifac(20);
select * from fac;
n f
1 1
2 2
3 6
4 24
5 120
6 720
7 5040
8 40320
9 362880
10 3628800
11 39916800
12 479001600
13 6227020800
14 87178291200
15 1307674368000
16 20922789888000
17 355687428096000
18 6402373705728000
19 121645100408832000
20 2432902008176640000
drop table fac;
drop procedure ifac;
drop function fac;
drop table if exists primes;
create table primes (
i int unsigned not null primary key,
p bigint unsigned not null
);
insert into primes values
( 0, 3), ( 1, 5), ( 2, 7), ( 3, 11), ( 4, 13),
( 5, 17), ( 6, 19), ( 7, 23), ( 8, 29), ( 9, 31),
(10, 37), (11, 41), (12, 43), (13, 47), (14, 53),
(15, 59), (16, 61), (17, 67), (18, 71), (19, 73),
(20, 79), (21, 83), (22, 89), (23, 97), (24, 101),
(25, 103), (26, 107), (27, 109), (28, 113), (29, 127),
(30, 131), (31, 137), (32, 139), (33, 149), (34, 151),
(35, 157), (36, 163), (37, 167), (38, 173), (39, 179),
(40, 181), (41, 191), (42, 193), (43, 197), (44, 199);
create procedure opp(n bigint unsigned, out pp bool)
begin
declare r double;
declare b, s bigint unsigned default 0;
set r = sqrt(n);
again:
loop
if s = 45 then
set b = b+200, s = 0;
else
begin
declare p bigint unsigned;
select t.p into p from test.primes t where t.i = s;
if b+p > r then
set pp = 1;
leave again;
end if;
if mod(n, b+p) = 0 then
set pp = 0;
leave again;
end if;
set s = s+1;
end;
end if;
end loop again;
end;
create procedure ip(m int unsigned)
begin
declare p bigint unsigned;
declare i int unsigned;
set i=45, p=201;
while i < m do
begin
declare pp bool default 0;
call opp(p, pp);
if pp then
insert into test.primes values (i, p);
set i = i+1;
end if;
set p = p+2;
end;
end while;
end;
call ip(200);
select * from primes where i=45 or i=100 or i=199;
i p
45 211
100 557
199 1229
drop table primes;
drop procedure opp;
drop procedure ip;
drop table t1;
drop table t2;

View File

@ -14,6 +14,6 @@ update t1 set n = 3;
unlock tables;
show status like 'Table_lock%';
Variable_name Value
Table_locks_immediate 3
Table_locks_immediate 4
Table_locks_waited 1
drop table t1;

View File

@ -593,7 +593,6 @@ x
3
3
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
You can't specify target table 't1' for update in FROM clause
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
select * from t1;
x
@ -601,6 +600,8 @@ x
2
3
3
11
11
0
drop table t1, t2, t3;
CREATE TABLE t1 (x int not null, y int, primary key (x));

View File

@ -297,6 +297,8 @@ set sql_buffer_result=1;
set sql_log_bin=1;
set sql_log_off=1;
set sql_log_update=1;
Warnings:
Note 1266 The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored.
set sql_low_priority_updates=1;
set sql_max_join_size=200;
select @@sql_max_join_size,@@max_join_size;

View File

@ -65,3 +65,34 @@ use test_$1;
create table t1 (c int);
insert into test_$1.t1 set test_$1.t1.c = '1';
drop database test_$1;
use test;
--disable_warnings
drop table if exists t1,t2,t3;
--enable_warnings
create table t1(id1 int not null auto_increment primary key, t char(12));
create table t2(id2 int not null, t char(12));
create table t3(id3 int not null, t char(12), index(id3));
disable_query_log;
let $1 = 100;
while ($1)
{
let $2 = 5;
eval insert into t1(t) values ('$1');
while ($2)
{
eval insert into t2(id2,t) values ($1,'$2');
let $3 = 10;
while ($3)
{
eval insert into t3(id3,t) values ($1,'$2');
dec $3;
}
dec $2;
}
dec $1;
}
enable_query_log;
select count(*) from t2;
insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
select count(*) from t2;
drop table if exists t1,t2,t3;

111
mysql-test/t/sp-error.test Normal file
View File

@ -0,0 +1,111 @@
#
# Stored PROCEDURE error tests
#
# Make sure we don't have any procedures left.
delete from mysql.proc;
delimiter |;
# Check that we get the right error, i.e. UDF declaration parses correctly,
# but foo.so doesn't exist.
# QQ This generates an error message containing a misleading errno which
# might vary between systems (it usually doesn't have anything to do with
# the actual failing dlopen()).
#--error 1126
#create function foo returns real soname "foo.so"|
create procedure proc1()
set @x = 42|
create function func1() returns int
return 42|
# Can't create recursively
--error 1254
create procedure foo()
create procedure bar() set @x=3|
--error 1254
create procedure foo()
create function bar() returns double return 2.3|
# Already exists
--error 1255
create procedure proc1()
set @x = 42|
--error 1255
create function func1() returns int
return 42|
# Does not exist
--error 1256
alter procedure foo|
--error 1256
alter function foo|
--error 1256
drop procedure foo|
--error 1256
drop function foo|
--error 1256
call foo()|
drop procedure if exists foo|
# LEAVE/ITERATE with no match
--error 1259
create procedure foo()
foo: loop
leave bar;
end loop|
--error 1259
create procedure foo()
foo: loop
iterate bar;
end loop|
# Redefining label
--error 1260
create procedure foo()
foo: loop
foo: loop
set @x=2;
end loop foo;
end loop foo|
# End label mismatch
--error 1261
create procedure foo()
foo: loop
set @x=2;
end loop bar|
# Referring to undef variable
--error 1262
create procedure foo(out x int)
begin
declare y int;
set x = y;
end|
# We require INTO in SELECTs (for now; this might change in the future)
--error 1263
create procedure foo(x int)
select * from test.t1|
# RETURN in FUNCTION only
--error 1264
create procedure foo()
return 42|
# Doesn't allow queries in FUNCTIONs (for now :-( )
--error 1265
create function foo() returns int
begin
declare x int;
select max(c) into x from test.t;
return x;
end|
drop procedure proc1|
drop function func1|
delimiter ;|

588
mysql-test/t/sp.test Normal file
View File

@ -0,0 +1,588 @@
#
# Basic stored PROCEDURE tests
#
#
use test;
--disable_warnings
drop table if exists t1;
drop table if exists t2;
--enable_warnings
create table t1 (
id char(16) not null,
data int not null
);
create table t2 (
s char(16) not null,
i int not null,
d double not null
);
# Single statement, no params.
create procedure foo42()
insert into test.t1 values ("foo", 42);
call foo42();
select * from t1;
delete from t1;
drop procedure foo42;
# USE test: Make sure we remain in the same DB.
create procedure u()
use sptmp;
--disable_warnings
drop database if exists sptmp;
--enable_warnings
create database sptmp;
use test;
call u();
select database();
drop database sptmp;
drop procedure u;
# Single statement, two IN params.
create procedure bar(x char(16), y int)
insert into test.t1 values (x, y);
call bar("bar", 666);
select * from t1;
delete from t1;
# Don't drop procedure yet...
# Now for multiple statements...
delimiter |;
# Two statements.
create procedure two(x1 char(16), x2 char(16), y int)
begin
insert into test.t1 values (x1, y);
insert into test.t1 values (x2, y);
end|
call two("one", "two", 3)|
select * from t1|
delete from t1|
drop procedure two|
# Simple test of local variables and SET.
create procedure locset(x char(16), y int)
begin
declare z1, z2 int;
set z1 = y;
set z2 = z1+2;
insert into test.t1 values (x, z2);
end|
call locset("locset", 19)|
select * from t1|
delete from t1|
drop procedure locset|
# The peculiar (non-standard) mixture of variables types in SET.
create procedure mixset(x char(16), y int)
begin
declare z int;
set @z = y, z = 666, max_join_size = 100;
insert into test.t1 values (x, z);
end|
call mixset("mixset", 19)|
show variables like 'max_join_size'|
select id,data,@z from t1|
delete from t1|
drop procedure mixset|
# Multiple CALL statements, one with OUT parameter.
create procedure zip(x char(16), y int)
begin
declare z int;
call zap(y, z);
call bar(x, z);
end|
# SET local variables and OUT parameter.
create procedure zap(x int, out y int)
begin
declare z int;
set z = x+1, y = z;
end|
call zip("zip", 99)|
select * from t1|
delete from t1|
drop procedure zip|
drop procedure zap|
drop procedure bar|
# INOUT test
create procedure iotest(x1 char(16), x2 char(16), y int)
begin
call inc2(x2, y);
insert into test.t1 values (x1, y);
end|
create procedure inc2(x char(16), y int)
begin
call inc(y);
insert into test.t1 values (x, y);
end|
create procedure inc(inout io int)
set io = io + 1|
call iotest("io1", "io2", 1)|
select * from t1|
delete from t1|
drop procedure iotest|
drop procedure inc2|
drop procedure inc|
# Call-by-value test
# The expected result is:
# ("cbv2", 4)
# ("cbv1", 4711)
create procedure cbv1()
begin
declare y int default 3;
call cbv2(y+1, y);
insert into test.t1 values ("cbv1", y);
end|
create procedure cbv2(y1 int, inout y2 int)
begin
set y2 = 4711;
insert into test.t1 values ("cbv2", y1);
end|
call cbv1()|
select * from t1|
delete from t1|
drop procedure cbv1|
drop procedure cbv2|
# Basic tests of the flow control constructs
# Just test on 'x'...
create procedure a0(x int)
while x do
set x = x-1;
insert into test.t1 values ("a0", x);
end while|
call a0(3)|
select * from t1|
delete from t1|
drop procedure a0|
# The same, but with a more traditional test.
create procedure a(x int)
while x > 0 do
set x = x-1;
insert into test.t1 values ("a", x);
end while|
call a(3)|
select * from t1|
delete from t1|
drop procedure a|
# REPEAT
create procedure b(x int)
repeat
insert into test.t1 values (repeat("b",3), x);
set x = x-1;
until x = 0 end repeat|
call b(3)|
select * from t1|
delete from t1|
drop procedure b|
# Check that repeat isn't parsed the wrong way
create procedure b2(x int)
repeat(select 1 into outfile 'b2');
insert into test.t1 values (repeat("b2",3), x);
set x = x-1;
until x = 0 end repeat|
# We don't actually want to call it.
drop procedure b2|
# Labelled WHILE with ITERATE (pointless really)
create procedure c(x int)
hmm: while x > 0 do
insert into test.t1 values ("c", x);
set x = x-1;
iterate hmm;
insert into test.t1 values ("x", x);
end while hmm|
call c(3)|
select * from t1|
delete from t1|
drop procedure c|
# Labelled WHILE with LEAVE
create procedure d(x int)
hmm: while x > 0 do
insert into test.t1 values ("d", x);
set x = x-1;
leave hmm;
insert into test.t1 values ("x", x);
end while hmm|
call d(3)|
select * from t1|
delete from t1|
drop procedure d|
# LOOP, with simple IF statement
create procedure e(x int)
foo: loop
if x = 0 then
leave foo;
end if;
insert into test.t1 values ("e", x);
set x = x-1;
end loop foo|
call e(3)|
select * from t1|
delete from t1|
drop procedure e|
# A full IF statement
create procedure f(x int)
if x < 0 then
insert into test.t1 values ("f", 0);
elseif x = 0 then
insert into test.t1 values ("f", 1);
else
insert into test.t1 values ("f", 2);
end if|
call f(-2)|
call f(0)|
call f(4)|
select * from t1|
delete from t1|
drop procedure f|
# This form of CASE is really just syntactic sugar for IF-ELSEIF-...
create procedure g(x int)
case
when x < 0 then
insert into test.t1 values ("g", 0);
when x = 0 then
insert into test.t1 values ("g", 1);
else
insert into test.t1 values ("g", 2);
end case|
call g(-42)|
call g(0)|
call g(1)|
select * from t1|
delete from t1|
drop procedure g|
# The "simple CASE"
create procedure h(x int)
case x
when 0 then
insert into test.t1 values ("h0", x);
when 1 then
insert into test.t1 values ("h1", x);
else
insert into test.t1 values ("h?", x);
end case|
call h(0)|
call h(1)|
call h(17)|
select * from t1|
delete from t1|
drop procedure h|
# SELECT INTO local variables
create procedure into_test(x char(16), y int)
begin
insert into test.t1 values (x, y);
select id,data into x,y from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end|
call into_test("into", 100)|
select * from t1|
delete from t1|
drop procedure into_test|
# SELECT INTO with a mix of local and global variables
create procedure into_test2(x char(16), y int)
begin
insert into test.t1 values (x, y);
select id,data into x,@z from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end|
call into_test2("into", 100)|
select id,data,@z from t1|
delete from t1|
drop procedure into_test2|
# These two (and the two procedures above) caused an assert() to fail in
# sql_base.cc:lock_tables() at some point.
create procedure into_outfile(x char(16), y int)
begin
insert into test.t1 values (x, y);
select * into outfile "/tmp/spout" from test.t1;
insert into test.t1 values (concat(x, "2"), y+2);
end|
system rm -f /tmp/spout|
call into_outfile("ofile", 1)|
system rm -f /tmp/spout|
delete from t1|
drop procedure into_outfile|
create procedure into_dumpfile(x char(16), y int)
begin
insert into test.t1 values (x, y);
select * into dumpfile "/tmp/spdump" from test.t1 limit 1;
insert into test.t1 values (concat(x, "2"), y+2);
end|
system rm -f /tmp/spdump|
call into_dumpfile("dfile", 1)|
system rm -f /tmp/spdump|
delete from t1|
drop procedure into_dumpfile|
create procedure create_select(x char(16), y int)
begin
insert into test.t1 values (x, y);
create table test.t2 select * from test.t1;
insert into test.t2 values (concat(x, "2"), y+2);
end|
# This doesn't work right now. It suffers from the same problem as the ones
# above, but the fix caused create.test to hang. :-(
#call create_select("cs", 90)|
#select * from t1, t2|
#delete from t1|
#drop table t2|
drop procedure create_select|
# A minimal, constant FUNCTION.
create function e() returns double
return 2.7182818284590452354|
set @e = e()|
select e(), @e|
# A minimal function with one argument
create function inc(i int) returns int
return i+1|
select inc(1), inc(99), inc(-71)|
# A minimal function with two arguments
create function mul(x int, y int) returns int
return x*y|
select mul(1,1), mul(3,5), mul(4711, 666)|
# A minimal string function
create function append(s1 char(8), s2 char(8)) returns char(16)
return concat(s1, s2)|
select append("foo", "bar")|
# A function with flow control
create function fac(n int unsigned) returns bigint unsigned
begin
declare f bigint unsigned default 1;
while n > 1 do
set f = f * n;
set n = n - 1;
end while;
return f;
end|
select fac(1), fac(2), fac(5), fac(10)|
# Nested calls
create function fun(d double, i int, u int unsigned) returns double
return mul(inc(i), fac(u)) / e()|
select fun(2.3, 3, 5)|
# Various function calls in differen statements
insert into t2 values (append("xxx", "yyy"), mul(4,3), e())|
insert into t2 values (append("a", "b"), mul(2,mul(3,4)), fun(1.7, 4, 6))|
# These don't work yet.
select * from t2 where s = append("a", "b")|
select * from t2 where i = mul(4,3) or i = mul(mul(3,4),2)|
select * from t2 where d = e()|
select * from t2|
delete from t2|
drop function e|
drop function inc|
drop function mul|
drop function append|
drop function fun|
#
# Some "real" examples
#
# fac
--disable_warnings
drop table if exists fac|
--enable_warnings
create table fac (n int unsigned not null primary key, f bigint unsigned)|
create procedure ifac(n int unsigned)
begin
declare i int unsigned default 1;
if n > 20 then
set n = 20; # bigint overflow otherwise
end if;
while i <= n do
begin
insert into test.fac values (i, fac(i));
set i = i + 1;
end;
end while;
end|
call ifac(20)|
select * from fac|
drop table fac|
drop procedure ifac|
drop function fac|
# primes
--disable_warnings
drop table if exists primes|
--enable_warnings
create table primes (
i int unsigned not null primary key,
p bigint unsigned not null
)|
insert into primes values
( 0, 3), ( 1, 5), ( 2, 7), ( 3, 11), ( 4, 13),
( 5, 17), ( 6, 19), ( 7, 23), ( 8, 29), ( 9, 31),
(10, 37), (11, 41), (12, 43), (13, 47), (14, 53),
(15, 59), (16, 61), (17, 67), (18, 71), (19, 73),
(20, 79), (21, 83), (22, 89), (23, 97), (24, 101),
(25, 103), (26, 107), (27, 109), (28, 113), (29, 127),
(30, 131), (31, 137), (32, 139), (33, 149), (34, 151),
(35, 157), (36, 163), (37, 167), (38, 173), (39, 179),
(40, 181), (41, 191), (42, 193), (43, 197), (44, 199)|
create procedure opp(n bigint unsigned, out pp bool)
begin
declare r double;
declare b, s bigint unsigned default 0;
set r = sqrt(n);
again:
loop
if s = 45 then
set b = b+200, s = 0;
else
begin
declare p bigint unsigned;
select t.p into p from test.primes t where t.i = s;
if b+p > r then
set pp = 1;
leave again;
end if;
if mod(n, b+p) = 0 then
set pp = 0;
leave again;
end if;
set s = s+1;
end;
end if;
end loop again;
end|
create procedure ip(m int unsigned)
begin
declare p bigint unsigned;
declare i int unsigned;
set i=45, p=201;
while i < m do
begin
declare pp bool default 0;
call opp(p, pp);
if pp then
insert into test.primes values (i, p);
set i = i+1;
end if;
set p = p+2;
end;
end while;
end|
# This isn't the fastest way in the world to compute prime numbers, so
# don't be too ambition. ;-)
call ip(200)|
# We don't want to select the entire table here, just pick a few
# examples.
select * from primes where i=45 or i=100 or i=199|
drop table primes|
drop procedure opp|
drop procedure ip|
delimiter ;|
drop table t1;
drop table t2;

View File

@ -343,7 +343,6 @@ INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
select * from t1;
INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2;
select * from t1;
-- error 1093
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
-- sleep 1

View File

@ -353,6 +353,19 @@ 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 type enum('function','procedure') NOT NULL,"
c_p="$c_p body blob DEFAULT '' NOT NULL,"
c_p="$c_p PRIMARY KEY (name,type)"
c_p="$c_p )"
c_p="$c_p comment='Stored Procedures';"
fi
echo "Installing privilege tables"
if (
cat << END_OF_DATA
@ -371,6 +384,7 @@ $i_f
$c_t
$c_c
$c_p
END_OF_DATA
if test -n "$fill_help_tables"
then

View File

@ -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)

View File

@ -22,6 +22,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
#include "sp_rcontext.h"
/*****************************************************************************
** Item functions
@ -175,6 +176,23 @@ CHARSET_INFO * Item::default_charset() const
return current_thd->db_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);
}
bool Item::set_charset(CHARSET_INFO *cs1, enum coercion co1,
CHARSET_INFO *cs2, enum coercion co2)
{

View File

@ -123,6 +123,8 @@ public:
bool set_charset(CHARSET_INFO *cs1, enum coercion co1,
CHARSET_INFO *cs2, enum coercion co2);
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; }
@ -136,6 +138,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
{

View File

@ -30,6 +30,9 @@
#ifdef HAVE_COMPRESS
#include <zlib.h>
#endif
#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp.h"
/* return TRUE if item is a constant */
@ -2839,3 +2842,74 @@ longlong Item_func_srid::val_int()
uint32 res= uint4korr(swkb->ptr());
return (longlong) res;
}
int
Item_func_sp::execute(Item **itp)
{
DBUG_ENTER("Item_func_sp::execute");
THD *thd= current_thd;
if (! m_sp)
m_sp= sp_find_function(thd, &m_name);
if (! m_sp)
DBUG_RETURN(-1);
DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp));
}
enum enum_field_types
Item_func_sp::field_type() const
{
DBUG_ENTER("Item_func_sp::field_type");
if (! m_sp)
m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
if (m_sp)
{
DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
DBUG_RETURN(m_sp->m_returns);
}
DBUG_RETURN(MYSQL_TYPE_STRING);
}
Item_result
Item_func_sp::result_type() const
{
DBUG_ENTER("Item_func_sp::result_type");
DBUG_PRINT("info", ("m_sp = %p", m_sp));
if (! m_sp)
m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
if (m_sp)
{
DBUG_RETURN(m_sp->result());
}
DBUG_RETURN(STRING_RESULT);
}
void
Item_func_sp::fix_length_and_dec()
{
DBUG_ENTER("Item_func_sp::fix_length_and_dec");
if (! m_sp)
m_sp= sp_find_function(current_thd, &m_name);
if (m_sp)
{
switch (m_sp->result()) {
case STRING_RESULT:
maybe_null= 1;
max_length= 0;
break;
case REAL_RESULT:
decimals= NOT_FIXED_DEC;
max_length= float_length(decimals);
break;
case INT_RESULT:
decimals= 0;
max_length= 21;
break;
}
}
DBUG_VOID_RETURN;
}

View File

@ -1179,3 +1179,69 @@ enum Item_cast
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
};
/*
*
* Stored FUNCTIONs
*
*/
class sp_head;
class Item_func_sp :public Item_func
{
private:
LEX_STRING m_name;
mutable sp_head *m_sp;
int execute(Item **itp);
public:
Item_func_sp(LEX_STRING name)
:Item_func(), m_name(name), m_sp(NULL)
{}
Item_func_sp(LEX_STRING name, List<Item> &list)
:Item_func(list), m_name(name), m_sp(NULL)
{}
virtual ~Item_func_sp()
{}
const char *func_name() const
{
return m_name.str;
}
enum enum_field_types field_type() const;
Item_result result_type() const;
longlong val_int()
{
return (longlong)Item_func_sp::val();
}
double val()
{
Item *it;
if (execute(&it))
return 0.0;
return it->val();
}
String *val_str(String *str)
{
Item *it;
if (execute(&it))
return NULL;
return it->val_str(str);
}
void fix_length_and_dec();
};

View File

@ -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},
@ -81,6 +82,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},
@ -109,6 +111,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},
@ -120,6 +123,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},
@ -142,6 +146,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},
@ -172,7 +177,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},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0},
{ "GLOBAL", SYM(GLOBAL_SYM),0,0},
@ -198,6 +203,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},
@ -215,12 +222,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},
@ -236,6 +245,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},
@ -290,6 +300,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},
@ -319,13 +330,15 @@ static SYMBOL symbols[] = {
{ "REPAIR", SYM(REPAIR),0,0},
{ "REPLACE", SYM(REPLACE),0,0},
{ "REPLICATION", SYM(REPLICATION),0,0},
{ "REPEAT", SYM(REPEAT_SYM),0,0},
{ "REPEATABLE", SYM(REPEATABLE_SYM),0,0},
{ "REQUIRE", SYM(REQUIRE_SYM),0,0},
{ "RESET", SYM(RESET_SYM),0,0},
{ "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 */
@ -337,6 +350,7 @@ static SYMBOL symbols[] = {
{ "SECOND", SYM(SECOND_SYM),0,0},
{ "SEPARATOR", SYM(SEPARATOR_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},
@ -351,6 +365,7 @@ 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},
{ "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},
@ -393,6 +408,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},
@ -411,6 +427,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},
@ -591,7 +608,6 @@ static SYMBOL sql_functions[] = {
{ "RADIANS", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_radians)},
{ "RAND", SYM(RAND),0,0},
{ "RELEASE_LOCK", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_release_lock)},
{ "REPEAT", SYM(FUNC_ARG2),0,CREATE_FUNC(create_func_repeat)},
{ "REVERSE", SYM(FUNC_ARG1),0,CREATE_FUNC(create_func_reverse)},
{ "ROUND", SYM(ROUND),0,0},
{ "RPAD", SYM(FUNC_ARG3),0,CREATE_FUNC(create_func_rpad)},

View File

@ -30,7 +30,7 @@
#include <stdarg.h>
#include <m_ctype.h> // For test_if_number
MYSQL_LOG mysql_log,mysql_update_log,mysql_slow_log,mysql_bin_log;
MYSQL_LOG mysql_log, mysql_slow_log, mysql_bin_log;
extern I_List<i_string> binlog_do_db, binlog_ignore_db;
static bool test_if_number(const char *str,
@ -1020,7 +1020,7 @@ err:
/*
Write to normal (not rotable) log
This is the format for the 'normal', 'slow' and 'update' logs.
This is the format for the 'normal' log.
*/
bool MYSQL_LOG::write(THD *thd,enum enum_server_command command,
@ -1392,8 +1392,7 @@ err:
/*
Write update log in a format suitable for incremental backup
This is also used by the slow query log.
Write to the slow query log.
*/
bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
@ -1409,12 +1408,6 @@ bool MYSQL_LOG::write(THD *thd,const char *query, uint query_length,
int tmp_errno=0;
char buff[80],*end;
end=buff;
if (!(thd->options & OPTION_UPDATE_LOG) &&
(thd->master_access & SUPER_ACL))
{
VOID(pthread_mutex_unlock(&LOCK_log));
return 0;
}
if ((specialflag & SPECIAL_LONG_LOG_FORMAT) || query_start_arg)
{
current_time=time(NULL);

View File

@ -364,7 +364,7 @@ extern "C" pthread_handler_decl(handle_one_connection,arg);
extern "C" pthread_handler_decl(handle_bootstrap,arg);
void end_thread(THD *thd,bool put_in_cache);
void flush_thread_cache();
void mysql_execute_command(THD *thd);
int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length);

View File

@ -22,6 +22,7 @@
#include "sql_repl.h"
#include "repl_failsafe.h"
#include "stacktrace.h"
#include "mysys_err.h"
#ifdef HAVE_BERKELEY_DB
#include "ha_berkeley.h"
#endif
@ -345,7 +346,7 @@ uint mysql_port;
uint test_flags = 0, select_errors=0, dropping_tables=0,ha_open_options=0;
uint volatile thread_count=0, thread_running=0, kill_cached_threads=0,
wake_thread=0;
ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
ulong thd_startup_options=(OPTION_AUTO_IS_NULL |
OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE );
uint protocol_version=PROTOCOL_VERSION;
struct system_variables global_system_variables;
@ -912,7 +913,6 @@ void clean_up(bool print_message)
mysql_log.cleanup();
mysql_slow_log.cleanup();
mysql_update_log.cleanup();
mysql_bin_log.cleanup();
#ifdef HAVE_REPLICATION
@ -2181,9 +2181,55 @@ static int init_server_components()
LOG_NORMAL);
if (opt_update_log)
{
open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
NullS, LOG_NEW);
using_update_log=1;
/*
Update log is removed since 5.0. But we still accept the option.
The idea is if the user already uses the binlog and the update log,
we completely ignore any option/variable related to the update log, like
if the update log did not exist. But if the user uses only the update log,
then we translate everything into binlog for him (with warnings).
Implementation of the above :
- If mysqld is started with --log-update and --log-bin,
ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
is used, and turn off --sql-bin-update-same.
This will completely ignore SQL_LOG_UPDATE
- If mysqld is started with --log-update only,
change it to --log-bin (with the filename passed to log-update,
plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
used, and turn on --sql-bin-update-same.
This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
Note that we tell the user that --sql-bin-update-same is deprecated and
does nothing, and we don't take into account if he used this option or
not; but internally we give this variable a value to have the behaviour we
want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
As sql-bin-update-same, log-update and log-bin cannot be changed by the user
after starting the server (they are not variables), the user will not
later interfere with the settings we do here.
*/
if (opt_bin_log)
{
opt_sql_bin_update= 0;
sql_print_error("The update log is no longer supported by MySQL in \
version 5.0 and above. It is replaced by the binary log.");
}
else
{
opt_sql_bin_update= 1;
opt_bin_log= 1;
if (opt_update_logname)
{
// as opt_bin_log==0, no need to free opt_bin_logname
if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
exit(EXIT_OUT_OF_MEMORY);
sql_print_error("The update log is no longer supported by MySQL in \
version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
with --log-bin='%s' instead.",opt_bin_logname);
}
else
sql_print_error("The update log is no longer supported by MySQL in \
version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
with --log-bin instead.");
}
}
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
@ -3669,7 +3715,8 @@ struct my_option my_long_options[] =
(gptr*) &myisam_log_filename, (gptr*) &myisam_log_filename, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-update", OPT_UPDATE_LOG,
"Log updates to file.# where # is a unique number if not given.",
"The update log is deprecated since version 5.0, is replaced by the binary \
log and this option justs turns on --log-bin instead.",
(gptr*) &opt_update_logname, (gptr*) &opt_update_logname, 0, GET_STR,
OPT_ARG, 0, 0, 0, 0, 0, 0},
{"log-slow-queries", OPT_SLOW_QUERY_LOG,
@ -3920,9 +3967,9 @@ struct my_option my_long_options[] =
(gptr*) &mysql_unix_port, (gptr*) &mysql_unix_port, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
"If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.",
(gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL,
NO_ARG, 0, 0, 0, 0, 0, 0},
"The update log is deprecated since version 5.0, is replaced by the binary \
log and this option does nothing anymore.",
0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sql-mode", OPT_SQL_MODE,
"Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,

View File

@ -78,6 +78,7 @@ static void sys_set_default_charset(THD *thd, enum_var_type type);
static bool set_option_bit(THD *thd, set_var *var);
static bool set_option_autocommit(THD *thd, set_var *var);
static bool set_log_update(THD *thd, set_var *var);
static bool set_log_bin(THD *thd, set_var *var);
static void fix_low_priority_updates(THD *thd, enum_var_type type);
static void fix_tx_isolation(THD *thd, enum_var_type type);
static void fix_net_read_timeout(THD *thd, enum_var_type type);
@ -269,7 +270,7 @@ static sys_var_thd_bit sys_log_update("sql_log_update",
set_log_update,
OPTION_UPDATE_LOG);
static sys_var_thd_bit sys_log_binlog("sql_log_bin",
set_log_update,
set_log_bin,
OPTION_BIN_LOG);
static sys_var_thd_bit sys_sql_warnings("sql_warnings",
set_option_bit,
@ -1383,6 +1384,30 @@ static bool set_option_autocommit(THD *thd, set_var *var)
static bool set_log_update(THD *thd, set_var *var)
{
/*
The update log is not supported anymore since 5.0.
See sql/mysqld.cc/, comments in function init_server_components() for an
explaination of the different warnings we send below
*/
if (opt_sql_bin_update)
{
((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
OPTION_UPDATE_LOG);
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_UPDATE_LOG_DEPRECATED_TRANSLATED,
ER(ER_UPDATE_LOG_DEPRECATED_TRANSLATED));
}
else
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
ER_UPDATE_LOG_DEPRECATED_IGNORED,
ER(ER_UPDATE_LOG_DEPRECATED_IGNORED));
set_option_bit(thd, var);
return 0;
}
static bool set_log_bin(THD *thd, set_var *var)
{
if (opt_sql_bin_update)
((sys_var_thd_bit*) var->var)->bit_flag|= (OPTION_BIN_LOG |
@ -1391,6 +1416,7 @@ static bool set_log_update(THD *thd, set_var *var)
return 0;
}
static byte *get_warning_count(THD *thd)
{
thd->sys_var_tmp.long_value=

View File

@ -264,3 +264,17 @@ v/*
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -258,3 +258,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -266,3 +266,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -256,3 +256,17 @@
"The slave was already running"
"The slave was already stopped"
"%d line(s) was(were) cut by group_concat()"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -260,3 +260,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -265,3 +265,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -257,3 +257,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -257,3 +257,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -257,3 +257,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -257,3 +257,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -259,3 +259,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -259,3 +259,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -257,3 +257,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -251,3 +251,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -263,3 +263,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -256,3 +256,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -255,3 +255,17 @@
"COLLATION '%s' är inte tillåtet för CHARACTER SET '%s'"
"Slaven har redan startat"
"Slaven har redan stoppat"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

View File

@ -260,3 +260,17 @@
"COLLATION '%s' is not valid for CHARACTER SET '%s'"
"The slave was already running"
"The slave was already stopped"
"Can't create a %s from within another stored routine"
"%s %s already exists"
"%s %s does not exist"
"Failed to DROP %s %s"
"Failed to CREATE %s %s"
"%s with no matching label: %s"
"Redefining label %s"
"End-label %s without match"
"Referring to uninitialized variable %s"
"SELECT in a stored procedure must have INTO"
"RETURN is only allowed in a FUNCTION"
"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."

396
sql/sp.cc Normal file
View File

@ -0,0 +1,396 @@
/* 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"
static sp_head *
sp_find_cached_function(THD *thd, char *name, uint namelen);
/*
*
* DB storage of Stored PROCEDUREs and FUNCTIONs
*
*/
// *openeed=true means we opened ourselves
static int
db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
enum thr_lock_type ltype, TABLE **tablep, bool *opened)
{
DBUG_ENTER("db_find_routine_aux");
DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
TABLE *table;
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);
for (table= thd->open_tables ; table ; table= table->next)
if (strcmp(table->table_cache_key, "mysql") == 0 &&
strcmp(table->real_name, "proc") == 0)
break;
if (table)
*opened= FALSE;
else
{
TABLE_LIST tables;
memset(&tables, 0, sizeof(tables));
tables.db= (char*)"mysql";
tables.real_name= tables.alias= (char*)"proc";
if (! (table= open_ltable(thd, &tables, ltype)))
{
*tablep= NULL;
DBUG_RETURN(SP_OPEN_TABLE_FAILED);
}
*opened= TRUE;
}
if (table->file->index_read_idx(table->record[0], 0,
key, keylen,
HA_READ_KEY_EXACT))
{
*tablep= NULL;
DBUG_RETURN(SP_KEY_NOT_FOUND);
}
*tablep= table;
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;
bool opened;
// QQ Set up our own mem_root here???
ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
if (ret != SP_OK)
goto done;
if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
if (opened)
{
close_thread_tables(thd);
table= NULL;
}
tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
ret= SP_PARSE_ERROR;
else
*sphp= tmplex->sphead;
done:
if (table && opened)
close_thread_tables(thd);
DBUG_RETURN(ret);
}
static int
db_create_routine(THD *thd, int type,
char *name, uint namelen, char *def, uint deflen)
{
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";
if (! (table= open_ltable(thd, &tables, TL_WRITE)))
ret= SP_OPEN_TABLE_FAILED;
else
{
restore_record(table, 2); // Get default values for fields
table->field[0]->store(name, namelen, system_charset_info);
table->field[1]->store((longlong)type);
table->field[2]->store(def, deflen, system_charset_info);
if (table->file->write_row(table->record[0]))
ret= SP_WRITE_ROW_FAILED;
else
ret= SP_OK;
}
close_thread_tables(thd);
DBUG_RETURN(ret);
}
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;
bool opened;
ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
if (ret == SP_OK)
{
if (table->file->delete_row(table->record[0]))
ret= SP_DELETE_ROW_FAILED;
}
if (opened)
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);
DBUG_RETURN(ret);
}
int
sp_drop_procedure(THD *thd, char *name, uint namelen)
{
DBUG_ENTER("sp_drop_procedure");
DBUG_PRINT("enter", ("name: %*s", namelen, name));
int ret;
ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);
DBUG_RETURN(ret);
}
/*
*
* FUNCTION
*
*/
sp_head *
sp_find_function(THD *thd, LEX_STRING *name)
{
DBUG_ENTER("sp_find_function");
sp_head *sp;
DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
sp= sp_find_cached_function(thd, name->str, name->length);
if (! sp)
{
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);
}
// QQ Temporary until the function call detection in sql_lex has been reworked.
bool
sp_function_exists(THD *thd, LEX_STRING *name)
{
TABLE *table;
bool ret= FALSE;
bool opened;
if (db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
name->str, name->length, TL_READ,
&table, &opened) == SP_OK)
{
ret= TRUE;
}
if (opened)
close_thread_tables(thd);
return ret;
}
/*
*
* The temporary FUNCTION cache. (QQ This will be rehacked later, but
* it's needed now to make functions work at all.)
*
*/
void
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
{
List_iterator_fast<char> li(lex->spfuns);
char *fn;
while ((fn= li++))
{
if (my_strncasecmp(system_charset_info, fn, fun.str, fun.length) == 0)
break;
}
if (! fn)
{
char *s= sql_strmake(fun.str, fun.length);
lex->spfuns.push_back(s);
}
}
void
sp_merge_funs(LEX *dst, LEX *src)
{
List_iterator_fast<char> li(src->spfuns);
char *fn;
while ((fn= li++))
{
LEX_STRING lx;
lx.str= fn; lx.length= strlen(fn);
sp_add_fun_to_lex(dst, lx);
}
}
/* QQ Not terribly efficient right now, but it'll do for starters.
We should actually open the mysql.proc table just once. */
int
sp_cache_functions(THD *thd, LEX *lex)
{
List_iterator<char> li(lex->spfuns);
char *fn;
enum_sql_command cmd= lex->sql_command;
int ret= 0;
while ((fn= li++))
{
List_iterator_fast<sp_head> lisp(thd->spfuns);
sp_head *sp;
while ((sp= lisp++))
{
if (my_strcasecmp(system_charset_info, fn, sp->name()) == 0)
break;
}
if (sp)
continue;
if (db_find_routine(thd, TYPE_ENUM_FUNCTION, fn, strlen(fn), &sp) == SP_OK)
{
ret= sp_cache_functions(thd, &thd->lex);
if (ret)
break;
thd->spfuns.push_back(sp);
}
else
{
send_error(thd, ER_SP_DOES_NOT_EXIST);
ret= 1;
}
}
lex->sql_command= cmd;
return ret;
}
void
sp_clear_function_cache(THD *thd)
{
List_iterator_fast<sp_head> li(thd->spfuns);
sp_head *sp;
while ((sp= li++))
sp->destroy();
thd->spfuns.empty();
}
static sp_head *
sp_find_cached_function(THD *thd, char *name, uint namelen)
{
List_iterator_fast<sp_head> li(thd->spfuns);
sp_head *sp;
while ((sp= li++))
{
if (my_strncasecmp(system_charset_info, name, sp->name(), namelen) == 0)
break;
}
return sp;
}

65
sql/sp.h Normal file
View File

@ -0,0 +1,65 @@
/* -*- 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_
// 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, LEX_STRING *name);
int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen);
int
sp_drop_procedure(THD *thd, char *name, uint namelen);
sp_head *
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);
// QQ Temporary until the function call detection in sql_lex has been reworked.
bool
sp_function_exists(THD *thd, LEX_STRING *name);
// QQ More temporary stuff until the real cache is implemented. This is
// needed since we have to read the functions before we do anything else.
void
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
void
sp_merge_funs(LEX *dst, LEX *src);
int
sp_cache_functions(THD *thd, LEX *lex);
void
sp_clear_function_cache(THD *thd);
#endif /* _SP_H_ */

549
sql/sp_head.cc Normal file
View File

@ -0,0 +1,549 @@
/* 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"
Item_result
sp_map_result_type(enum enum_field_types type)
{
switch (type)
{
case MYSQL_TYPE_TINY:
case MYSQL_TYPE_SHORT:
case MYSQL_TYPE_LONG:
case MYSQL_TYPE_LONGLONG:
case MYSQL_TYPE_INT24:
return INT_RESULT;
case MYSQL_TYPE_DECIMAL:
case MYSQL_TYPE_FLOAT:
case MYSQL_TYPE_DOUBLE:
return REAL_RESULT;
default:
return STRING_RESULT;
}
}
/* 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)
{
DBUG_ENTER("eval_func_item");
it= it->this_item();
DBUG_PRINT("info", ("type: %d", type));
if (it->fix_fields(thd, 0, NULL))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(it); // Shouldn't happen?
}
/* QQ How do we do this? Is there some better way? */
if (type == MYSQL_TYPE_NULL)
it= new Item_null();
else
{
switch (sp_map_result_type(type)) {
case INT_RESULT:
DBUG_PRINT("info", ("INT_RESULT: %d", it->val_int()));
it= new Item_int(it->val_int());
break;
case REAL_RESULT:
DBUG_PRINT("info", ("REAL_RESULT: %g", it->val()));
it= new Item_real(it->val());
break;
default:
{
char buffer[MAX_FIELD_WIDTH];
String tmp(buffer, sizeof(buffer), it->charset());
String *s= it->val_str(&tmp);
DBUG_PRINT("info", ("default result: %*s", s->length(), s->c_ptr_quick()))
it= new Item_string(sql_strmake(s->c_ptr_quick(), s->length()),
s->length(), it->charset());
break;
}
}
}
DBUG_RETURN(it);
}
sp_head::sp_head(LEX_STRING *name, LEX *lex)
: Sql_alloc(), m_simple_case(FALSE)
{
DBUG_ENTER("sp_head::sp_head");
const char *dstr = (const char*)lex->buf;
DBUG_PRINT("info", ("name: %s", name->str));
m_name.length= name->length;
m_name.str= name->str;
m_defstr.length= lex->end_of_query - lex->buf;
m_defstr.str= sql_strmake(dstr, m_defstr.length);
m_pcont= lex->spcont;
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
m_backpatch.empty();
DBUG_VOID_RETURN;
}
int
sp_head::create(THD *thd)
{
DBUG_ENTER("sp_head::create");
int ret;
DBUG_PRINT("info", ("type: %d name: %s def: %s",
m_type, m_name.str, m_defstr.str));
if (m_type == TYPE_ENUM_FUNCTION)
ret= sp_create_function(thd,
m_name.str, m_name.length,
m_defstr.str, m_defstr.length);
else
ret= sp_create_procedure(thd,
m_name.str, m_name.length,
m_defstr.str, m_defstr.length);
DBUG_RETURN(ret);
}
void
sp_head::destroy()
{
DBUG_ENTER("sp_head::destroy");
DBUG_PRINT("info", ("name: %s", m_name.str));
delete_dynamic(&m_instr);
m_pcont->destroy();
DBUG_VOID_RETURN;
}
int
sp_head::execute(THD *thd)
{
DBUG_ENTER("sp_head::execute");
char olddbname[128];
char *olddbptr= thd->db;
int ret= 0;
uint ip= 0;
if (olddbptr)
{
uint i= 0;
char *p= olddbptr;
/* Fast inline strncpy without padding... */
while (*p && i < sizeof(olddbname))
olddbname[i++]= *p++;
if (i == sizeof(olddbname))
i-= 1; // QQ Error or warning for truncate?
olddbname[i]= '\0';
}
do
{
sp_instr *i;
i = get_instr(ip); // Returns NULL when we're done.
if (i == NULL)
break;
DBUG_PRINT("execute", ("Instruction %u", ip));
ret= i->execute(thd, &ip);
} while (ret == 0 && !thd->killed);
DBUG_PRINT("info", ("ret=%d killed=%d", ret, thd->killed));
if (thd->killed)
ret= -1;
/* If the DB has changed, the pointer has changed too, but the
original thd->db will then have been freed */
if (olddbptr && olddbptr != thd->db)
{
/* QQ Maybe we should issue some special error message or warning here,
if this fails?? */
if (! thd->killed)
ret= mysql_change_db(thd, olddbname);
}
DBUG_RETURN(ret);
}
int
sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
{
DBUG_ENTER("sp_head::execute_function");
DBUG_PRINT("info", ("function %s", m_name.str));
uint csize = m_pcont->max_framesize();
uint params = m_pcont->params();
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
uint i;
int ret;
// QQ Should have some error checking here? (no. of args, types, etc...)
nctx= new sp_rcontext(csize);
for (i= 0 ; i < params && i < argcount ; i++)
{
sp_pvar_t *pvar = m_pcont->find_pvar(i);
nctx->push_item(eval_func_item(thd, *argp++, pvar->type));
}
// The rest of the frame are local variables which are all IN.
// QQ See comment in execute_procedure below.
for (; i < csize ; i++)
nctx->push_item(NULL);
thd->spcont= nctx;
ret= execute(thd);
if (ret == 0)
*resp= nctx->get_result();
thd->spcont= octx;
DBUG_RETURN(ret);
}
int
sp_head::execute_procedure(THD *thd, List<Item> *args)
{
DBUG_ENTER("sp_head::execute_procedure");
DBUG_PRINT("info", ("procedure %s", m_name.str));
int ret;
sp_instr *p;
uint csize = m_pcont->max_framesize();
uint params = m_pcont->params();
sp_rcontext *octx = thd->spcont;
sp_rcontext *nctx = NULL;
my_bool tmp_octx = FALSE; // True if we have allocated a temporary octx
if (csize > 0)
{
uint i;
List_iterator_fast<Item> li(*args);
Item *it;
nctx = new sp_rcontext(csize);
if (! octx)
{ // Create a temporary old context
octx = new sp_rcontext(csize);
tmp_octx = TRUE;
}
// QQ: No error checking whatsoever right now. Should do type checking?
for (i = 0 ; (it= li++) && i < params ; i++)
{
sp_pvar_t *pvar = m_pcont->find_pvar(i);
if (! pvar)
nctx->set_oindex(i, -1); // Shouldn't happen
else
{
if (pvar->mode == sp_param_out)
nctx->push_item(NULL); // 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;
}
ret= execute(thd);
// Don't copy back OUT values if we got an error
if (ret == 0 && csize > 0)
{
List_iterator_fast<Item> li(*args);
Item *it;
// Copy back all OUT or INOUT values to the previous frame, or
// set global user variables
for (uint i = 0 ; (it= li++) && i < params ; i++)
{
int oi = nctx->get_oindex(i);
if (oi >= 0)
{
if (! tmp_octx)
octx->set_item(nctx->get_oindex(i), nctx->get_item(i));
else
{ // A global user variable
#if 0
// QQ This works if the parameter really is a user variable, but
// for the moment we can't assure that, so it will crash if it's
// something else. So for now, we just do nothing, to avoid a crash.
// Note: This also assumes we have a get_name() method in
// the Item_func_get_user_var class.
Item *item= nctx->get_item(i);
Item_func_set_user_var *suv;
Item_func_get_user_var *guv= static_cast<Item_func_get_user_var*>(it);
suv= new Item_func_set_user_var(guv->get_name(), item);
suv->fix_fields(thd, NULL, &item);
suv->fix_length_and_dec();
suv->update();
#endif
}
}
}
if (tmp_octx)
thd->spcont= NULL;
else
thd->spcont= octx;
}
DBUG_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.
sp_merge_funs(&m_lex, &thd->lex);
#if 0
// We're not using this at the moment.
if (thd->lex.sql_command == SQLCOM_CALL)
{
// It would be slightly faster to keep the list sorted, but we need
// an "insert before" method to do that.
char *proc= thd->lex.udf.name.str;
List_iterator_fast<char *> li(m_calls);
char **it;
while ((it= li++))
if (my_strcasecmp(system_charset_info, proc, *it) == 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 (my_strcasecmp(system_charset_info, tables->real_name, *tb) == 0)
break;
if (! tb)
m_tables.push_back(&tables->real_name);
}
}
#endif
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 *)sql_alloc(sizeof(bp_t));
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)
{
DBUG_ENTER("sp_instr_stmt::execute");
DBUG_PRINT("info", ("command: %d", m_lex.sql_command));
LEX olex; // The other lex
int res;
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;
res= mysql_execute_command(thd);
if (thd->lock || thd->open_tables || thd->derived_tables)
{
thd->proc_info="closing tables";
close_thread_tables(thd); /* Free tables */
}
memcpy(&thd->lex, &olex, sizeof(LEX)); // Restore the other lex
*nextp = m_ip+1;
DBUG_RETURN(res);
}
//
// sp_instr_set
//
int
sp_instr_set::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_set::execute");
DBUG_PRINT("info", ("offset: %u", m_offset));
thd->spcont->set_item(m_offset, eval_func_item(thd, m_value, m_type));
*nextp = m_ip+1;
DBUG_RETURN(0);
}
//
// sp_instr_jump
//
int
sp_instr_jump::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_jump::execute");
DBUG_PRINT("info", ("destination: %u", m_dest));
*nextp= m_dest;
DBUG_RETURN(0);
}
//
// sp_instr_jump_if
//
int
sp_instr_jump_if::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_jump_if::execute");
DBUG_PRINT("info", ("destination: %u", m_dest));
Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
if (it->val_int())
*nextp = m_dest;
else
*nextp = m_ip+1;
DBUG_RETURN(0);
}
//
// sp_instr_jump_if_not
//
int
sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_jump_if_not::execute");
DBUG_PRINT("info", ("destination: %u", m_dest));
Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
if (! it->val_int())
*nextp = m_dest;
else
*nextp = m_ip+1;
DBUG_RETURN(0);
}
//
// sp_instr_return
//
int
sp_instr_return::execute(THD *thd, uint *nextp)
{
DBUG_ENTER("sp_instr_return::execute");
thd->spcont->set_result(eval_func_item(thd, m_value, m_type));
*nextp= UINT_MAX;
DBUG_RETURN(0);
}

354
sql/sp_head.h Normal file
View File

@ -0,0 +1,354 @@
/* -*- 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>
// 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
Item_result
sp_map_result_type(enum enum_field_types type);
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:
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
#if 0
// We're not using this at the moment.
List<char *> m_calls; // Called procedures.
List<char *> m_tables; // Used tables.
#endif
sp_head(LEX_STRING *name, LEX *lex);
int
create(THD *thd);
// Free memory
void
destroy();
int
execute_function(THD *thd, Item **args, uint argcount, Item **resp);
int
execute_procedure(THD *thd, List<Item> *args);
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 *);
char *name(uint *lenp = 0) const
{
if (lenp)
*lenp= m_name.length;
return m_name.str;
}
inline Item_result result()
{
return sp_map_result_type(m_returns);
}
private:
LEX_STRING m_name;
LEX_STRING m_defstr;
sp_pcontext *m_pcont; // Parse context
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 backpatching
inline sp_instr *
get_instr(uint i)
{
sp_instr *ip;
if (i < m_instr.elements)
get_dynamic(&m_instr, (gptr)&ip, i);
else
ip= NULL;
return ip;
}
int
execute(THD *thd);
}; // 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);
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
class sp_instr_return : public sp_instr
{
sp_instr_return(const sp_instr_return &); /* Prevent use of these */
void operator=(sp_instr_return &);
public:
sp_instr_return(uint ip, Item *val, enum enum_field_types type)
: sp_instr(ip), m_value(val), m_type(type)
{}
virtual ~sp_instr_return()
{}
virtual int execute(THD *thd, uint *nextp);
protected:
Item *m_value;
enum enum_field_types m_type;
}; // class sp_instr_return : public sp_instr
#endif /* _SP_HEAD_H_ */

118
sql/sp_pcontext.cc Normal file
View File

@ -0,0 +1,118 @@
/* 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()
: Sql_alloc(), m_params(0), m_framesize(0), m_genlab(0)
{
VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
m_label.empty();
}
void
sp_pcontext::destroy()
{
delete_dynamic(&m_pvar);
m_label.empty();
}
/* 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)
{
uint i = m_pvar.elements;
while (i-- > 0)
{
sp_pvar_t *p= find_pvar(i);
uint len= (p->name.length > name->length ?
p->name.length : name->length);
if (my_strncasecmp(system_charset_info,
name->str,
p->name.str,
len) == 0)
{
return p;
}
}
return NULL;
}
void
sp_pcontext::push(LEX_STRING *name, enum enum_field_types type,
sp_param_mode_t mode)
{
sp_pvar_t *p= (sp_pvar_t *)sql_alloc(sizeof(sp_pvar_t));
if (p)
{
if (m_pvar.elements == m_framesize)
m_framesize += 1;
p->name.str= name->str;
p->name.length= name->length;
p->type= type;
p->mode= mode;
p->offset= m_pvar.elements;
p->isset= (mode == sp_param_out ? FALSE : TRUE);
insert_dynamic(&m_pvar, (gptr)&p);
}
}
sp_label_t *
sp_pcontext::push_label(char *name, uint ip)
{
sp_label_t *lab = (sp_label_t *)sql_alloc(sizeof(sp_label_t));
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 (my_strcasecmp(system_charset_info, name, lab->name) == 0)
return lab;
return NULL;
}

162
sql/sp_pcontext.h Normal file
View File

@ -0,0 +1,162 @@
/* -*- 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
{
LEX_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();
// Free memory
void
destroy();
inline uint
max_framesize()
{
return m_framesize;
}
inline uint
current_framesize()
{
return m_pvar.elements;
}
inline uint
params()
{
return m_params;
}
// Set the number of parameters to the current esize
inline void
set_params()
{
m_params= m_pvar.elements;
}
inline void
set_type(uint i, enum enum_field_types type)
{
sp_pvar_t *p= find_pvar(i);
if (p)
p->type= type;
}
inline void
set_isset(uint i, my_bool val)
{
sp_pvar_t *p= find_pvar(i);
if (p)
p->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)
{
while (num--)
pop_dynamic(&m_pvar);
}
// Find by name
sp_pvar_t *
find_pvar(LEX_STRING *name);
// Find by index
sp_pvar_t *
find_pvar(uint i)
{
sp_pvar_t *p;
if (i < m_pvar.elements)
get_dynamic(&m_pvar, (gptr)&p, i);
else
p= NULL;
return p;
}
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
DYNAMIC_ARRAY m_pvar;
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_ */

95
sql/sp_rcontext.h Normal file
View File

@ -0,0 +1,95 @@
/* -*- 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_result(NULL)
{
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];
}
inline void
set_result(Item *it)
{
m_result= it;
}
inline Item *
get_result()
{
return m_result;
}
private:
uint m_count;
uint m_size;
Item **m_frame;
int *m_outs;
Item *m_result; // For FUNCTIONs
}; // class sp_rcontext : public Sql_alloc
#endif /* _SP_RCONTEXT_H_ */

View File

@ -1164,7 +1164,6 @@ bool change_password(THD *thd, const char *host, const char *user,
acl_user->user ? acl_user->user : "",
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
mysql_update_log.write(thd, buff, query_length);
Query_log_event qinfo(thd, buff, query_length, 0);
mysql_bin_log.write(&qinfo);
DBUG_RETURN(0);

View File

@ -36,6 +36,8 @@
#endif
#include <mysys_err.h>
#include <sp_rcontext.h>
/*
The following is used to initialise Table_ident with a internal
table name
@ -86,7 +88,7 @@ extern "C" void free_user_var(user_var_entry *entry)
THD::THD():user_time(0), is_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= "connecting host";
@ -196,6 +198,9 @@ THD::THD():user_time(0), is_fatal_error(0),
pthread_mutex_unlock(&LOCK_thread_count);
randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
}
/* QQ init the temporary function cache */
spfuns.empty();
}
@ -287,6 +292,10 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
// extern void sp_clear_function_cache(THD *);
// sp_clear_function_cache(this);
cleanup_done=1;
DBUG_VOID_RETURN;
}
@ -1008,9 +1017,12 @@ bool select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
List_iterator_fast<Item> li(list);
List_iterator_fast<LEX_STRING> gl(var_list);
List_iterator_fast<my_var> gl(var_list);
Item *item;
my_var *mv;
LEX_STRING *ls;
row_count= 0;
if (var_list.elements != list.elements)
{
my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
@ -1019,19 +1031,39 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
unit=u;
while ((item=li++))
{
ls= gl++;
Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item);
xx->fix_length_and_dec();
vars.push_back(xx);
mv=gl++;
ls= &mv->s;
if (mv->local)
{
(void)local_vars.push_back(new Item_splocal(mv->offset));
}
else
{
Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item);
xx->fix_length_and_dec();
vars.push_back(xx);
}
}
return 0;
}
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
List_iterator_fast<Item_splocal> var_li(local_vars);
List_iterator_fast<my_var> my_li(var_list);
List_iterator_fast<Item> it(items);
Item_func_set_user_var *xx;
Item_splocal *yy;
Item *item;
my_var *zz;
DBUG_ENTER("send_data");
if (unit->offset_limit_cnt)
{ // using limit offset,count
unit->offset_limit_cnt--;
DBUG_RETURN(0);
}
if (unit->offset_limit_cnt)
{ // Using limit offset,count
@ -1043,8 +1075,21 @@ bool select_dumpvar::send_data(List<Item> &items)
my_error(ER_TOO_MANY_ROWS, MYF(0));
DBUG_RETURN(1);
}
while ((xx=li++))
xx->update();
while ((zz=my_li++) && (item=it++))
{
if (zz->local)
{
if ((yy=var_li++))
{
thd->spcont->set_item(yy->get_offset(), item);
}
}
else
{
if ((xx=li++))
xx->update();
}
}
DBUG_RETURN(0);
}

View File

@ -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 };
@ -530,6 +531,8 @@ public:
bool volatile killed;
bool prepare_command;
bool tmp_table_used;
sp_rcontext *spcont; // SP runtime context
List<sp_head> spfuns; // SP FUNCTIONs
/*
If we do a purge of binary logs, log index info of the threads
@ -1003,13 +1006,22 @@ public:
bool send_eof();
};
class my_var : public Sql_alloc {
public:
LEX_STRING s;
bool local;
uint offset;
my_var (LEX_STRING& j, bool i, uint o) : s(j), local(i), offset(o) {}
~my_var() {}
};
class select_dumpvar :public select_result {
ha_rows row_count;
public:
List<LEX_STRING> var_list;
List<my_var> var_list;
List<Item_func_set_user_var> vars;
select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
List<Item_splocal> local_vars;
select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_fields(List<Item> &list, uint flag) {return 0;}

View File

@ -231,7 +231,6 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
query= thd->query;
query_length= thd->query_length;
}
mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
@ -283,7 +282,6 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
create_info->table_charset : default_charset_info;
}
mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@ -369,7 +367,6 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query=thd->query;
query_length=thd->query_length;
}
mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);

View File

@ -179,7 +179,6 @@ cleanup:
log_delayed= (transactional_table || table->tmp_table);
if (deleted && (error <= 0 || !transactional_table))
{
mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@ -486,7 +485,6 @@ bool multi_delete::send_eof()
*/
if (deleted && (error <= 0 || normal_tables))
{
mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@ -601,7 +599,6 @@ end:
{
if (!error)
{
mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,

View File

@ -23,7 +23,7 @@
static int check_null_fields(THD *thd,TABLE *entry);
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list);
static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup,
char *query, uint query_length, int log_on);
char *query, uint query_length, bool log_on);
static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table);
@ -38,9 +38,6 @@ static void unlink_blobs(register TABLE *table);
#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
#endif
#define DELAYED_LOG_UPDATE 1
#define DELAYED_LOG_BIN 2
/*
Check if insert fields are correct
Updates table->time_stamp to point to timestamp field or 0, depending on
@ -114,8 +111,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
By default, both logs are enabled (this won't cause problems if the server
runs without --log-update or --log-bin).
*/
int log_on= DELAYED_LOG_UPDATE | DELAYED_LOG_BIN ;
bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->master_access & SUPER_ACL));
bool transactional_table, log_delayed, bulk_insert;
uint value_count;
ulong counter = 1;
@ -130,14 +126,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->lex.select_lex.table_list.first;
DBUG_ENTER("mysql_insert");
if (thd->master_access & SUPER_ACL)
{
if (!(thd->options & OPTION_UPDATE_LOG))
log_on&= ~(int) DELAYED_LOG_UPDATE;
if (!(thd->options & OPTION_BIN_LOG))
log_on&= ~(int) DELAYED_LOG_BIN;
}
/*
in safe mode or with skip-new change delayed insert to be regular
if we are told to replace duplicates, the insert cannot be concurrent
@ -338,7 +326,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
log_delayed= (transactional_table || table->tmp_table);
if ((info.copied || info.deleted) && (error <= 0 || !transactional_table))
{
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@ -562,13 +549,12 @@ public:
char *record,*query;
enum_duplicates dup;
time_t start_time;
bool query_start_used,last_insert_id_used,insert_id_used;
int log_query;
bool query_start_used,last_insert_id_used,insert_id_used, log_query;
ulonglong last_insert_id;
ulong time_stamp;
uint query_length;
delayed_row(enum_duplicates dup_arg, int log_query_arg)
delayed_row(enum_duplicates dup_arg, bool log_query_arg)
:record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
~delayed_row()
{
@ -871,7 +857,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
char *query, uint query_length, int log_on)
char *query, uint query_length, bool log_on)
{
delayed_row *row=0;
delayed_insert *di=thd->di;
@ -1258,15 +1244,10 @@ bool delayed_insert::handle_inserts(void)
using_ignore=0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
}
if (row->query)
if (row->query && row->log_query && using_bin_log)
{
if (row->log_query & DELAYED_LOG_UPDATE)
mysql_update_log.write(&thd,row->query, row->query_length);
if (row->log_query & DELAYED_LOG_BIN && using_bin_log)
{
Query_log_event qinfo(&thd, row->query, row->query_length,0);
mysql_bin_log.write(&qinfo);
}
Query_log_event qinfo(&thd, row->query, row->query_length,0);
mysql_bin_log.write(&qinfo);
}
if (table->blob_fields)
free_delayed_insert_blobs(table);
@ -1449,7 +1430,6 @@ bool select_insert::send_eof()
if (last_insert_id)
thd->insert_id(last_insert_id); // For update log
::send_ok(thd,info.copied,last_insert_id,buff);
mysql_update_log.write(thd,thd->query,thd->query_length);
return 0;
}
}

View File

@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
#include "sp.h"
#include "sp_head.h"
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
@ -109,6 +111,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
LEX *lex= &thd->lex;
lex->thd= thd;
lex->next_state=MY_LEX_START;
lex->buf= buf;
lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
lex->select_lex.create_refs=lex->in_comment=0;
@ -122,6 +125,9 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->yacc_yyss=lex->yacc_yyvs=0;
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
lex->sphead= NULL;
lex->spcont= NULL;
lex->spfuns.empty();
return lex;
}
@ -145,6 +151,17 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.length=len;
return symbol->tok;
}
LEX_STRING ls;
ls.str = (char *)tok; ls.length= len;
if (function && sp_function_exists(current_thd, &ls)) // QQ temp fix
{
lex->safe_to_cache_query= 0;
lex->yylval->lex_str.str= lex->thd->strmake((char*)lex->tok_start, len);
lex->yylval->lex_str.length= len;
return SP_FUNC;
}
#ifdef HAVE_DLOPEN
udf_func *udf;
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))

View File

@ -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_CREATE_SPFUNCTION, SQLCOM_CALL,
SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
/* This should be the last !!! */
SQLCOM_END
@ -419,6 +424,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 */
@ -479,6 +485,9 @@ typedef struct st_lex
CHARSET_INFO *charset;
char *help_arg;
SQL_LIST *gorder_list;
sp_head *sphead;
sp_pcontext *spcont;
List<char> spfuns; /* Called functions */
inline void uncacheable()
{

View File

@ -315,9 +315,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
sprintf(name,ER(ER_LOAD_INFO),info.records,info.deleted,
info.records-info.copied,thd->cuted_fields);
send_ok(thd,info.copied+info.deleted,0L,name);
// on the slave thd->query is never initialized
if (!thd->slave_thread)
mysql_update_log.write(thd,thd->query,thd->query_length);
if (!log_delayed)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;

View File

@ -26,6 +26,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
@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
/* Used in error handling only */
#define SP_TYPE_STRING(LP) \
((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
#define SP_COM_STRING(LP) \
((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
(LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
(LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
"FUNCTION" : "PROCEDURE")
extern int yyparse(void *thd);
extern "C" pthread_mutex_t THR_LOCK_keycache;
#ifdef SOLARIS
@ -1511,6 +1523,14 @@ restore_user:
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
/*
Clear the SP function cache after each statement (QQ this is a temporary
solution; caching will be rehacked later).
Note: Must do this before we free_root.
*/
sp_clear_function_cache(thd);
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@ -1565,7 +1585,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
void
int
mysql_execute_command(THD *thd)
{
int res= 0;
@ -1575,6 +1595,13 @@ mysql_execute_command(THD *thd)
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
{
if (sp_cache_functions(thd, lex))
DBUG_RETURN(-1);
}
/*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
@ -1597,7 +1624,7 @@ mysql_execute_command(THD *thd)
given and the table list says the query should not be replicated
*/
if (table_rules_on && tables && !tables_ok(thd,tables))
DBUG_VOID_RETURN;
DBUG_RETURN(0);
#ifndef TO_BE_DELETED
/*
This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
@ -1632,7 +1659,7 @@ mysql_execute_command(THD *thd)
{
if (res < 0 || thd->net.report_error)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
DBUG_VOID_RETURN;
DBUG_RETURN(res);
}
}
}
@ -1645,7 +1672,7 @@ mysql_execute_command(THD *thd)
!tables_ok(thd,tables))
#endif
)
DBUG_VOID_RETURN;
DBUG_RETURN(0);
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
switch (lex->sql_command) {
@ -1684,7 +1711,7 @@ mysql_execute_command(THD *thd)
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
DBUG_VOID_RETURN;
goto error;
}
else
thd->send_explain_fields(result);
@ -1947,7 +1974,7 @@ mysql_execute_command(THD *thd)
find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
DBUG_VOID_RETURN;
DBUG_RETURN(-1);
}
if (tables->next)
{
@ -2029,7 +2056,7 @@ mysql_execute_command(THD *thd)
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
break;
goto error;
}
{
LOCK_ACTIVE_MI;
@ -2042,7 +2069,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
break;
goto error;
#else
{
ulong priv=0;
@ -2134,7 +2161,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@ -2147,7 +2174,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_CREATE:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
if (check_db_used(thd, tables) ||
@ -2225,7 +2252,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
goto error;
}
res= mysql_update(thd,tables,
select_lex->item_list,
@ -2246,7 +2273,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
goto error;
}
{
const char *msg= 0;
@ -2282,7 +2309,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
DBUG_VOID_RETURN;
goto error;
}
res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
select_lex->item_list, lex->value_list,
@ -2323,8 +2350,7 @@ mysql_execute_command(THD *thd)
if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
DBUG_VOID_RETURN;
lex->select_lex.options |= OPTION_BUFFER_RESULT;
}
/* Skip first table, which is the table we are inserting in */
@ -2499,7 +2525,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@ -2533,7 +2559,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db))
@ -2546,7 +2572,7 @@ mysql_execute_command(THD *thd)
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
@ -2585,7 +2611,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
char *db=tables->db;
@ -2610,7 +2636,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
DBUG_VOID_RETURN;
goto error;
#else
{
char *db=tables->db;
@ -2792,26 +2818,25 @@ 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)
{
sph->destroy(); // QQ Free memory. Remove this when caching!!!
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:
{
@ -2853,14 +2878,11 @@ mysql_execute_command(THD *thd)
goto error;
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
lex->grant,
lex->sql_command == SQLCOM_REVOKE)))
lex->sql_command == SQLCOM_REVOKE)) &&
mysql_bin_log.is_open())
{
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
mysql_bin_log.write(&qinfo);
}
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
mysql_bin_log.write(&qinfo);
}
}
else
@ -2875,7 +2897,6 @@ mysql_execute_command(THD *thd)
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@ -2980,16 +3001,168 @@ 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_SPFUNCTION:
if (!lex->sphead)
{
res= -1; // Shouldn't happen
break;
}
else
{
uint namelen;
char *name= lex->sphead->name(&namelen);
#ifdef HAVE_DLOPEN
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
{
udf_func *udf = find_udf(name, namelen);
if (udf)
{
net_printf(thd, ER_UDF_EXISTS, name);
goto error;
}
}
#endif
res= lex->sphead->create(thd);
lex->sphead->destroy(); // QQ Free memory. Remove this when caching!!!
switch (res)
{
case SP_OK:
send_ok(thd);
break;
case SP_WRITE_ROW_FAILED:
net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
goto error;
default:
net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
goto error;
}
}
break;
case SQLCOM_CALL:
{
sp_head *sp;
sp= sp_find_procedure(thd, &lex->udf.name);
if (! sp)
{
net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name);
goto error;
}
else
{
#ifndef EMBEDDED_LIBRARY
// When executing substatements, they're assumed to send_error when
// it happens, but not to send_ok.
my_bool nsok= thd->net.no_send_ok;
thd->net.no_send_ok= TRUE;
#endif
res= sp->execute_procedure(thd, &lex->value_list);
#ifndef EMBEDDED_LIBRARY
thd->net.no_send_ok= nsok;
#endif
sp->destroy(); // QQ Free memory. Remove this when caching!!!
if (res == 0)
send_ok(thd);
else
goto error; // Substatement should already have sent error
}
}
break;
case SQLCOM_ALTER_PROCEDURE:
case SQLCOM_ALTER_FUNCTION:
{
sp_head *sp;
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)
{
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
goto error;
}
else
{
/* QQ This is an no-op right now, since we haven't
put the characteristics in yet. */
sp->destroy(); // QQ Free memory. Remove this when caching!!!
send_ok(thd);
}
}
break;
case SQLCOM_DROP_PROCEDURE:
case SQLCOM_DROP_FUNCTION:
{
if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
else
{
res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
#ifdef HAVE_DLOPEN
if (res == SP_KEY_NOT_FOUND)
{
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:
if (lex->drop_if_exists)
{
push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
SP_COM_STRING(lex), lex->udf.name.str);
res= 0;
send_ok(thd);
break;
}
net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
lex->udf.name.str);
goto error;
default:
net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
lex->udf.name.str);
goto error;
}
}
break;
default: /* Impossible */
send_ok(thd);
break;
}
thd->proc_info="query end"; // QQ
// We end up here if res == 0 and send_ok() has been done,
// or res != 0 and no send_error() has yet been done.
if (res < 0)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
DBUG_RETURN(res);
error:
DBUG_VOID_RETURN;
// We end up here if send_error() has already been done.
DBUG_RETURN(-1);
}
@ -3968,7 +4141,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
if (options & REFRESH_LOG)
{
mysql_log.new_file(1);
mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
#ifdef HAVE_REPLICATION
if (expire_logs_days)

View File

@ -79,7 +79,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
/* Lets hope this doesn't fail as the result will be messy */
if (!error)
{
mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);

View File

@ -191,7 +191,8 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
send_error(thd, 0, NullS);
res= 1;
}
delete result;
if (result != lex->result)
delete result;
return res;
}

View File

@ -255,15 +255,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (some_tables_deleted || tmp_table_deleted)
{
query_cache_invalidate3(thd, tables, 0);
if (!dont_log_query)
if (!dont_log_query && mysql_bin_log.is_open())
{
mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
tmp_table_deleted && !some_tables_deleted);
mysql_bin_log.write(&qinfo);
}
Query_log_event qinfo(thd, thd->query, thd->query_length,
tmp_table_deleted && !some_tables_deleted);
mysql_bin_log.write(&qinfo);
}
}
@ -908,17 +904,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
thd->tmp_table_used= 1;
}
if (!tmp_table && !no_log)
{
if (!tmp_table && !no_log && mysql_bin_log.is_open())
// Must be written before unlock
mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
test(create_info->options &
HA_LEX_CREATE_TMP_TABLE));
mysql_bin_log.write(&qinfo);
}
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
test(create_info->options &
HA_LEX_CREATE_TMP_TABLE));
mysql_bin_log.write(&qinfo);
}
error=0;
end:
@ -1742,7 +1734,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
if (!error)
{
mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@ -2123,7 +2114,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
my_free((gptr) new_table,MYF(0));
goto err;
}
mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@ -2252,7 +2242,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
}
thd->proc_info="end";
mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);

View File

@ -323,7 +323,6 @@ int mysql_update(THD *thd,
log_delayed= (transactional_table || table->tmp_table);
if (updated && (error <= 0 || !transactional_table))
{
mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@ -932,7 +931,6 @@ bool multi_update::send_eof()
if (updated && (local_error <= 0 || !trans_safe))
{
mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,

View File

@ -35,6 +35,9 @@
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
#include "sp_head.h"
#include "sp_pcontext.h"
#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
@ -121,6 +124,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
@ -202,6 +206,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
@ -247,6 +252,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
@ -261,6 +267,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
@ -304,6 +311,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
@ -343,6 +351,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
@ -363,9 +372,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
@ -504,6 +514,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token ROUND
%token SECOND_SYM
%token SHARE_SYM
%token SP_FUNC
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
@ -533,6 +544,18 @@ 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
%token REPEAT_SYM
%token UNTIL_SYM
%token WHILE_SYM
%token ASENSITIVE_SYM
%token INSENSITIVE_SYM
%token SENSITIVE_SYM
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
@ -565,6 +588,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_db
NCHAR_STRING
SP_FUNC ident_or_spfunc
%type <lex_str_ptr>
opt_table_alias
@ -600,7 +624,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr
using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
exists_subselect exists_subselect_init
exists_subselect exists_subselect_init sp_opt_default
%type <item_list>
expr_list udf_expr_list when_list ident_list ident_list_arg
@ -684,8 +708,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
union_clause union_list union_option
precision subselect_start opt_and charset
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
@ -711,10 +739,16 @@ query:
| verb_clause END_OF_INPUT {};
verb_clause:
statement
| begin
;
/* Verb clauses, except begin */
statement:
alter
| analyze
| backup
| begin
| call
| change
| check
| commit
@ -883,20 +917,525 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
| CREATE udf_func_type UDF_SYM IDENT_sys
| CREATE udf_func_type FUNCTION_SYM ident_or_spfunc
{
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_sys
create_function_tail
{}
| CREATE PROCEDURE ident
{
LEX *lex= Lex;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
YYABORT;
}
lex->spcont= new sp_pcontext();
lex->sphead= new sp_head(&$3, lex);
lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
}
'(' sp_pdparam_list ')'
{
Lex->spcont->set_params();
}
sp_proc_stmt
{
Lex->sql_command= SQLCOM_CREATE_PROCEDURE;
}
;
ident_or_spfunc:
IDENT_sys { $$= $1; }
| SP_FUNC { $$= $1; }
;
create_function_tail:
RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
lex->udf.returns=(Item_result) $7;
lex->udf.dl=$9.str;
lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.returns=(Item_result) $2;
lex->udf.dl=$4.str;
}
;
| '('
{
LEX *lex= Lex;
if (lex->sphead)
{
net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
YYABORT;
}
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
{
Lex->sql_command = SQLCOM_CREATE_SPFUNCTION;
}
;
call:
CALL_SYM ident_or_spfunc
{
LEX *lex = Lex;
lex->sql_command= SQLCOM_CALL;
lex->udf.name= $2;
lex->value_list.empty();
}
'(' 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);
}
;
/* Stored FUNCTION parameter declaration list */
sp_fdparam_list:
/* Empty */
| sp_fdparams
;
sp_fdparams:
sp_fdparams ',' sp_fdparam
| sp_fdparam
;
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,
(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 sp_opt_default
{
LEX *lex= Lex;
uint max= lex->spcont->current_framesize();
enum enum_field_types type= (enum enum_field_types)$3;
Item *it= $4;
for (uint i = max-$2 ; i < max ; i++)
{
lex->spcont->set_type(i, type);
if (! it)
lex->spcont->set_isset(i, FALSE);
else
{
sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
i, it, type);
lex->sphead->add_instr(in);
lex->spcont->set_isset(i, TRUE);
}
}
$$= $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_opt_default:
/* Empty */ { $$ = NULL; }
| DEFAULT expr { $$ = $2; }
;
sp_proc_stmt:
{
Lex->sphead->reset_lex(YYTHD);
}
statement
{
LEX *lex= Lex;
if (lex->sql_command == SQLCOM_SELECT && !lex->result)
{
send_error(YYTHD, ER_SP_BADSELECT);
YYABORT;
}
else
{
/* Don't add an instruction for empty SET statements.
** (This happens if the SET only contained local variables,
** which get their set instructions generated separately.)
*/
if (lex->sql_command != SQLCOM_SET_OPTION ||
!lex->var_list.is_empty())
{
/* Currently we can't handle queries inside a FUNCTION,
** because of the way table locking works.
** This is unfortunate, and limits the usefulness of functions
** a great deal, but it's nothing we can do about this at the
** moment.
*/
if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
lex->sql_command != SQLCOM_SET_OPTION)
{
send_error(YYTHD, ER_SP_BADQUERY);
YYABORT;
}
else
{
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);
}
}
| RETURN_SYM expr
{
LEX *lex= Lex;
if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
{
send_error(YYTHD, ER_SP_BADRETURN);
YYABORT;
}
else
{
sp_instr_return *i=
new sp_instr_return(lex->sphead->instructions(),
$2, lex->sphead->m_returns);
lex->sphead->add_instr(i);
}
}
| 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)
{
net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str);
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)
{
net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str);
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);
}
}
;
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)
{
net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
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 || strcasecmp($5.str, lab->name) != 0)
{
net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str);
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);
}
| REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM
{
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 {}
@ -1526,8 +2065,30 @@ alter:
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
};
}
| ALTER PROCEDURE 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->udf.name= $3;
}
| ALTER FUNCTION_SYM 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_FUNCTION;
lex->udf.name= $3;
}
;
alter_list:
| alter_list_item
@ -2215,6 +2776,8 @@ simple_expr:
{ $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);}
| FUNC_ARG3 '(' expr ',' expr ',' expr ')'
{ $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);}
| REPEAT_SYM '(' expr ',' expr ')'
{ $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
@ -2452,6 +3015,14 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
| SP_FUNC '(' udf_expr_list ')'
{
sp_add_fun_to_lex(Lex, $1);
if ($3)
$$= new Item_func_sp($1, *$3);
else
$$= new Item_func_sp($1);
}
| UDA_CHAR_SUM '(' udf_expr_list ')'
{
if ($3 != NULL)
@ -3107,12 +3678,34 @@ select_var_list:
| select_var_ident {}
;
select_var_ident: '@' ident_or_text
select_var_ident:
'@' ident_or_text
{
LEX *lex=Lex;
if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING))))
if (lex->result)
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0));
else
YYABORT;
}
| ident_or_text
{
LEX *lex=Lex;
if (!lex->spcont)
YYABORT;
sp_pvar_t *t;
if (!(t=lex->spcont->find_pvar(&$1)))
{
send_error(lex->thd, ER_SYNTAX_ERROR);
YYABORT;
}
if (! lex->result)
YYABORT;
else
{
((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset));
t->isset= TRUE;
}
}
;
into:
@ -3192,13 +3785,21 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
| DROP UDF_SYM IDENT_sys
| DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_FUNCTION;
lex->udf.name = $3;
};
lex->drop_if_exists= $3;
lex->udf.name= $4;
}
| DROP PROCEDURE if_exists IDENT_sys opt_restrict
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_PROCEDURE;
lex->drop_if_exists= $3;
lex->udf.name= $4;
}
;
table_list:
table_name
@ -3254,7 +3855,6 @@ replace:
}
insert_field_spec
{}
{}
;
insert_lock_option:
@ -3993,8 +4593,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)
{
net_printf(YYTHD, ER_SP_UNINIT_VAR, $1.str);
YYABORT;
}
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
{
@ -4307,7 +4924,7 @@ keyword:
| TIMESTAMP {}
| TIME_SYM {}
| TYPE_SYM {}
| UDF_SYM {}
| FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| USE_FRM {}
@ -4362,15 +4979,12 @@ opt_var_ident_type:
;
option_value:
'@' ident_or_text equal expr
{
Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
}
| internal_variable_name equal set_expr_or_default
'@' ident_or_text equal expr
{
LEX *lex=Lex;
lex->var_list.push_back(new set_var(lex->option_type, $1, $3));
Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
}
| internal_or_splocal
{}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
@ -4471,6 +5085,32 @@ internal_variable_name:
}
;
internal_or_splocal:
ident equal set_expr_or_default
{
LEX *lex= Lex;
sp_pcontext *spc= lex->spcont;
sp_pvar_t *spv;
if (!spc || !(spv = spc->find_pvar(&$1)))
{ /* Not an SP local variable */
sys_var *tmp= find_sys_var($1.str, $1.length);
if (!tmp)
YYABORT;
lex->var_list.push_back(new set_var(lex->option_type, tmp, $3));
}
else
{ /* An SP local variable */
sp_instr_set *i= new sp_instr_set(lex->sphead->instructions(),
spv->offset, $3, spv->type);
lex->sphead->add_instr(i);
spv->isset= TRUE;
}
}
;
isolation_types:
READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
| READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }