Merge 4.1 into 5.0

This commit is contained in:
pem@mysql.com 2003-08-26 11:51:09 +02:00
commit 7f158dd12a
126 changed files with 7642 additions and 1083 deletions

View File

@ -311,6 +311,8 @@ libmysql_r/conf_to_src
libmysql_r/my_static.h
libmysql_r/mysys_priv.h
libmysqld/backup_dir
libmysqld/client.c
libmysqld/client_settings.h
libmysqld/convert.cc
libmysqld/derror.cc
libmysqld/errmsg.c
@ -355,6 +357,7 @@ libmysqld/item_sum.cc
libmysqld/item_timefunc.cc
libmysqld/item_uniq.cc
libmysqld/key.cc
libmysqld/libmysql.c
libmysqld/lock.cc
libmysqld/log.cc
libmysqld/log_event.cc
@ -366,6 +369,7 @@ libmysqld/net_serv.cc
libmysqld/opt_ft.cc
libmysqld/opt_range.cc
libmysqld/opt_sum.cc
libmysqld/pack.c
libmysqld/password.c
libmysqld/procedure.cc
libmysqld/protocol.cc
@ -374,6 +378,10 @@ libmysqld/repl_failsafe.cc
libmysqld/set_var.cc
libmysqld/simple-test
libmysqld/slave.cc
libmysqld/sp.cc
libmysqld/sp_cache.cc
libmysqld/sp_head.cc
libmysqld/sp_pcontext.cc
libmysqld/spatial.cc
libmysqld/sql_acl.cc
libmysqld/sql_analyse.cc
@ -622,7 +630,3 @@ vio/test-ssl
vio/test-sslclient
vio/test-sslserver
vio/viotest-ssl
libmysqld/client.c
libmysqld/client_settings.h
libmysqld/libmysql.c
libmysqld/pack.c

View File

@ -91,10 +91,12 @@ paul@ice.local
paul@ice.snake.net
paul@teton.kitebird.com
pem@mysql.com
pem@per-erik-martins-dator.local
peter@linux.local
peter@mysql.com
peterg@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

@ -2815,6 +2815,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...)
{
va_list args;
NETWARE_YIELD
va_start(args, fmt);
(void) vfprintf(file, fmt, args);
#ifdef OS2
@ -2828,6 +2829,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...)
void tee_fputs(const char *s, FILE *file)
{
NETWARE_YIELD
fputs(s, file);
#ifdef OS2
fflush( file);
@ -2839,6 +2841,7 @@ void tee_fputs(const char *s, FILE *file)
void tee_puts(const char *s, FILE *file)
{
NETWARE_YIELD
fputs(s, file);
fputs("\n", file);
#ifdef OS2

View File

@ -90,6 +90,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,
@ -127,6 +128,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"
@ -203,7 +206,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
@ -267,6 +270,7 @@ const char *command_names[]=
"disable_warnings",
"enable_info",
"disable_info",
"delimiter",
0
};
@ -1546,6 +1550,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)
{
@ -1625,7 +1639,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;
@ -1665,7 +1679,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;
@ -1685,7 +1699,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;
@ -1706,7 +1720,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;
@ -2578,6 +2592,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.1-alpha)
AM_INIT_AUTOMAKE(mysql, 5.0.0-alpha)
AM_CONFIG_HEADER(config.h)
PROTOCOL_VERSION=10
@ -36,7 +36,7 @@ for i in $AVAILABLE_LANGUAGES
do
AVAILABLE_LANGUAGES_ERRORS="$AVAILABLE_LANGUAGES_ERRORS $i/errmsg.sys"
case $host_os in
netware* | modesto*)
netware*)
echo "$i/errmsg.sys: $i/errmsg.txt
\$(top_builddir)/extra/comp_err.linux \$^ $i/errmsg.sys" \
>> $AVAILABLE_LANGUAGES_ERRORS_RULES
@ -458,7 +458,7 @@ else
*cygwin*)
FIND_PROC="$PS -e | grep mysqld | grep \" \$\$PID \" > /dev/null"
;;
*netware* | *modesto*)
*netware*)
FIND_PROC=
;;
*)
@ -1541,7 +1541,7 @@ AC_SUBST(LIBDL)
# System characteristics
case $SYSTEM_TYPE in
*netware* | *modesto*) ;;
*netware*) ;;
*)
AC_SYS_RESTARTABLE_SYSCALLS
;;
@ -1571,10 +1571,12 @@ else
fi
if expr "$SYSTEM_TYPE" : ".*netware.*" > /dev/null; then
DEBUG_CFLAGS="$DEBUG_CFLAGS -DDEBUG -sym internal,codeview4"
DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -DDEBUG -sym internal,codeview4"
OPTIMIZE_CFLAGS="$OPTIMIZE_CFLAGS -DNDEBUG"
OPTIMIZE_CXXFLAGS="$OPTIMIZE_CXXFLAGS -DNDEBUG"
DEBUG_CFLAGS="-g -DDEBUG -sym internal,codeview4"
DEBUG_CXXFLAGS="-g -DDEBUG -sym internal,codeview4"
DEBUG_OPTIMIZE_CC="-DDEBUG"
DEBUG_OPTIMIZE_CXX="-DDEBUG"
OPTIMIZE_CFLAGS="-O3 -DNDEBUG"
OPTIMIZE_CXXFLAGS="-O3 -DNDEBUG"
fi
AC_ARG_WITH(debug,

View File

@ -14,7 +14,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/* Defines for netware compatible with MySQL */
/* Header for NetWare compatible with MySQL */
#ifndef _config_netware_h
#define _config_netware_h
/* required headers */
#include <unistd.h>
@ -32,6 +35,10 @@
#include <pthread.h>
#include <termios.h>
#ifdef __cplusplus
extern "C" {
#endif
/* required adjustments */
#undef HAVE_READDIR_R
#undef HAVE_RWLOCK_INIT
@ -80,6 +87,15 @@
/* do not use the extended time in LibC sys\stat.h */
#define _POSIX_SOURCE
/* Some macros for portability */
/* kernal call on NetWare that will only yield if our time slice is up */
void kYieldIfTimeSliceUp(void);
/* some macros for portability */
#define set_timespec(ABSTIME,SEC) { (ABSTIME).tv_sec=(SEC); (ABSTIME).tv_nsec=0; }
#ifdef __cplusplus
}
#endif
#endif /* _config_netware_h */

View File

@ -73,6 +73,13 @@
#endif
#endif /* _WIN32... */
/* extra protection against CPU Hogs on NetWare */
#ifdef __NETWARE__
#define NETWARE_YIELD { kYieldIfTimeSliceUp(); }
#else
#define NETWARE_YIELD { }
#endif
/*
The macros below are borrowed from include/linux/compiler.h in the
Linux kernel. Use them to indicate the likelyhood of the truthfulness

View File

@ -123,6 +123,8 @@ enum enum_server_command
#define NET_WRITE_TIMEOUT 60 /* Timeout on write */
#define NET_WAIT_TIMEOUT 8*60*60 /* Wait for new query */
#define ONLY_KILL_QUERY 1
struct st_vio; /* Only C */
typedef struct st_vio Vio;

View File

@ -289,4 +289,20 @@
#define ER_CANT_AGGREGATE_NCOLLATIONS 1270
#define ER_VARIABLE_IS_NOT_STRUCT 1271
#define ER_UNKNOWN_COLLATION 1272
#define ER_ERROR_MESSAGES 273
#define ER_SP_NO_RECURSIVE_CREATE 1273
#define ER_SP_ALREADY_EXISTS 1274
#define ER_SP_DOES_NOT_EXIST 1275
#define ER_SP_DROP_FAILED 1276
#define ER_SP_STORE_FAILED 1277
#define ER_SP_LILABEL_MISMATCH 1278
#define ER_SP_LABEL_REDEFINE 1279
#define ER_SP_LABEL_MISMATCH 1280
#define ER_SP_UNINIT_VAR 1281
#define ER_SP_BADSELECT 1282
#define ER_SP_BADRETURN 1283
#define ER_SP_BADQUERY 1284
#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1285
#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1286
#define ER_QUERY_INTERRUPTED 1287
#define ER_SP_WRONG_NO_OF_ARGS 1288
#define ER_ERROR_MESSAGES 289

View File

@ -3163,6 +3163,7 @@ static MYSQL_DATA *read_binary_rows(MYSQL_STMT *stmt)
if (pkt_len > 1)
{
mysql->warning_count= uint2korr(cp+1);
mysql->server_status= uint2korr(cp+3);
DBUG_PRINT("info",("warning_count: %ld", mysql->warning_count));
}
DBUG_PRINT("exit",("Got %d rows",result->rows));

View File

@ -56,7 +56,8 @@ sqlsources = 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 sp_cache.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources)
libmysqld_a_SOURCES=

View File

@ -2604,7 +2604,7 @@ static int sort_get_next_record(MI_SORT_PARAM *sort_param)
char llbuff[22],llbuff2[22];
DBUG_ENTER("sort_get_next_record");
if (*killed_ptr(param))
if (*killed_ptr(param->thd))
DBUG_RETURN(1);
switch (share->data_file_type) {

View File

@ -1648,9 +1648,9 @@ err:
DBUG_RETURN(1);
} /* sort_record_index */
volatile bool *killed_ptr(MI_CHECK *param)
int *killed_ptr(void *thd)
{
return (bool *)(& param->thd); /* always NULL */
return (int *)thd; /* always NULL */
}
/* print warnings and errors */

View File

@ -695,7 +695,7 @@ int mi_open_keyfile(MYISAM_SHARE *share);
void mi_setup_functions(register MYISAM_SHARE *share);
/* Functions needed by mi_check */
volatile bool *killed_ptr(MI_CHECK *param);
int *killed_ptr(void *thd);
void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_warning _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_info _VARARGS((MI_CHECK *param, const char *fmt,...));

View File

@ -848,7 +848,8 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
uchar *strpos;
BUFFPEK *buffpek,**refpek;
QUEUE queue;
volatile bool *killed= killed_ptr(info->sort_info->param);
int *killed= killed_ptr(info->sort_info->param->thd);
DBUG_ENTER("merge_buffers");
count=error=0;

View File

@ -8,6 +8,7 @@ help_keyword
help_relation
help_topic
host
proc
tables_priv
user
show tables;
@ -24,6 +25,7 @@ help_keyword
help_relation
help_topic
host
proc
tables_priv
user
show tables;
@ -40,6 +42,7 @@ help_keyword
help_relation
help_topic
host
proc
tables_priv
user
show tables;

View File

@ -76,3 +76,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

@ -1242,3 +1242,27 @@ show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
drop table t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40,t41,t42,t43,t44,t45,t46,t47,t48,t49,t50,t51,t52,t53,t54,t55,t56,t57,t58,t59,t60,t61,t62,t63,t64,t65,t66,t67,t68,t69,t70,t71,t72,t73,t74,t75,t76,t77,t78,t79,t80,t81,t82,t83,t84,t85,t86,t87,t88,t89,t90,t91,t92,t93,t94,t95,t96,t97,t98,t99,t100,t101,t102,t103,t104,t105,t106,t107,t108,t109,t110,t111,t112,t113,t114,t115,t116,t117,t118,t119,t120,t121,t122,t123,t124,t125,t126,t127,t128,t129,t130,t131,t132,t133,t134,t135,t136,t137,t138,t139,t140,t141,t142,t143,t144,t145,t146,t147,t148,t149,t150,t151,t152,t153,t154,t155,t156,t157,t158,t159,t160,t161,t162,t163,t164,t165,t166,t167,t168,t169,t170,t171,t172,t173,t174,t175,t176,t177,t178,t179,t180,t181,t182,t183,t184,t185,t186,t187,t188,t189,t190,t191,t192,t193,t194,t195,t196,t197,t198,t199,t200,t201,t202,t203,t204,t205,t206,t207,t208,t209,t210,t211,t212,t213,t214,t215,t216,t217,t218,t219,t220,t221,t222,t223,t224,t225,t226,t227,t228,t229,t230,t231,t232,t233,t234,t235,t236,t237,t238,t239,t240,t241,t242,t243,t244,t245,t246,t247,t248,t249,t250,t251,t252,t253,t254,t255,t256,t00;
create table t1 (a int);
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 0
show status like "Qcache_inserts";
Variable_name Value
Qcache_inserts 41
show status like "Qcache_hits";
Variable_name Value
Qcache_hits 13
/**/ select * from t1;
a
/**/ select * from t1;
a
show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 1
show status like "Qcache_inserts";
Variable_name Value
Qcache_inserts 42
show status like "Qcache_hits";
Variable_name Value
Qcache_hits 14
drop table t1;

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,98 @@
delete from mysql.proc;
create procedure syntaxerror(t int);
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
create procedure syntaxerror(t int);
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
create procedure syntaxerror(t int);
ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
create procedure proc1()
set @x = 42;
create function func1() returns int
return 42;
create procedure foo()
create procedure bar() set @x=3;
ERROR HY000: Can't create a PROCEDURE from within another stored routine
create procedure foo()
create function bar() returns double return 2.3;
ERROR HY000: Can't create a FUNCTION from within another stored routine
create procedure proc1()
set @x = 42;
ERROR HY000: PROCEDURE proc1 already exists
create function func1() returns int
return 42;
ERROR HY000: FUNCTION func1 already exists
drop procedure proc1;
drop function func1;
alter procedure foo;
ERROR HY000: PROCEDURE foo does not exist
alter function foo;
ERROR HY000: FUNCTION foo does not exist
drop procedure foo;
ERROR HY000: PROCEDURE foo does not exist
drop function foo;
ERROR HY000: FUNCTION foo does not exist
call foo();
ERROR HY000: PROCEDURE foo does not exist
drop procedure if exists foo;
Warnings:
Warning 1272 PROCEDURE foo does not exist
create procedure foo()
foo: loop
leave bar;
end loop;
ERROR HY000: LEAVE with no matching label: bar
create procedure foo()
foo: loop
iterate bar;
end loop;
ERROR HY000: ITERATE with no matching label: bar
create procedure foo()
foo: loop
foo: loop
set @x=2;
end loop foo;
end loop foo;
ERROR HY000: Redefining label foo
create procedure foo()
foo: loop
set @x=2;
end loop bar;
ERROR HY000: End-label bar without match
create procedure foo(out x int)
begin
declare y int;
set x = y;
end;
ERROR HY000: Referring to uninitialized variable y
create procedure foo()
begin
select name from mysql.proc;
select type from mysql.proc;
end;
call foo();
ERROR HY000: SELECT in a stored procedure must have INTO
drop procedure foo;
create procedure foo()
return 42;
ERROR HY000: 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;
ERROR HY000: Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION
create procedure p(x int)
insert into test.t1 values (x);
create function f(x int) returns int
return x+42;
call p();
ERROR HY000: Wrong number of arguments for PROCEDURE p, expected 1, got 0
call p(1, 2);
ERROR HY000: Wrong number of arguments for PROCEDURE p, expected 1, got 2
select f();
ERROR HY000: Wrong number of arguments for FUNCTION f, expected 1, got 0
select f(1, 2);
ERROR HY000: Wrong number of arguments for FUNCTION f, expected 1, got 2
drop procedure p;
drop function f;

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

@ -0,0 +1,555 @@
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 c1(x int)
call c2("c", x);
create procedure c2(s char(16), x int)
call c3(x, s);
create procedure c3(x int, s char(16))
call c4("level", x, s);
create procedure c4(l char(8), x int, s char(16))
insert into t1 values (concat(l,s), x);
call c1(42);
select * from t1;
id data
levelc 42
delete from t1;
drop procedure c1;
drop procedure c2;
drop procedure c3;
drop procedure c4;
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;
insert into t2 values ("a", 1, 1.1), ("b", 2, 1.2), ("c", 3, 1.3);
create procedure sub1(id char(16), x int)
insert into test.t1 values (id, x);
create function sub3(i int) returns int
return i+1;
call sub1("sub1a", (select 7));
call sub1("sub1b", (select max(i) from t2));
call sub1("sub1c", (select i,d from t2 limit 1));
call sub1("sub1d", (select 1 from (select 1) a));
select * from t1;
id data
sub1a 7
sub1b 3
sub1c 1
sub1d 1
select sub3((select max(i) from t2));
sub3((select max(i) from t2))
4
drop procedure sub1;
drop function sub3;
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
sub1a 7
sub1b 3
sub1c 1
sub1d 1
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.t3 select * from test.t1;
insert into test.t3 values (concat(x, "2"), y+2);
end;
drop table if exists t3;
call create_select("cs", 90);
select * from t1, t3;
id data id data
cs 90 cs 90
cs 90 cs2 92
drop table if exists t3;
delete from t1;
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
a 1 1.1
b 2 1.2
c 3 1.3
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

@ -608,6 +608,14 @@ x
3
3
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
select * from t1;
x
1
2
3
3
11
11
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
ERROR 42S22: Unknown column 'x' in 'field list'
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));

View File

@ -1,8 +1,30 @@
drop table if exists t1,t2;
set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
select @test,@`select`,@TEST,@not_used;
@test @`select` @TEST @not_used
1 2 3 NULL
set @`test`=1;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
@test @`test` @TEST @`TEST` @"teSt"
1 1 1 1 1
set @TEST=2;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
@test @`test` @TEST @`TEST` @"teSt"
2 2 2 2 2
set @"tEST"=3;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
@test @`test` @TEST @`TEST` @"teSt"
3 3 3 3 3
set @`TeST`=4;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
@test @`test` @TEST @`TEST` @"teSt"
4 4 4 4 4
select @`teST`:=5;
@`teST`:=5
5
select @test, @`test`, @TEST, @`TEST`, @"teSt";
@test @`test` @TEST @`TEST` @"teSt"
5 5 5 5 5
set @select=2,@t5=1.23456;
select @`select`,@not_used;
@`select` @not_used
2 NULL
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
@test_int @test_double @test_string @test_string2 @select
@ -288,6 +310,8 @@ set sql_buffer_result=1;
set sql_log_bin=1;
set sql_log_off=1;
set sql_log_update=1;
Warnings:
Note 1282 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

@ -76,3 +76,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;

View File

@ -1015,3 +1015,15 @@ show status like "Qcache_hits";
delete from t256;
show status like "Qcache_queries_in_cache";
drop table t0,t1,t2,t3,t4,t5,t6,t7,t8,t9,t10,t11,t12,t13,t14,t15,t16,t17,t18,t19,t20,t21,t22,t23,t24,t25,t26,t27,t28,t29,t30,t31,t32,t33,t34,t35,t36,t37,t38,t39,t40,t41,t42,t43,t44,t45,t46,t47,t48,t49,t50,t51,t52,t53,t54,t55,t56,t57,t58,t59,t60,t61,t62,t63,t64,t65,t66,t67,t68,t69,t70,t71,t72,t73,t74,t75,t76,t77,t78,t79,t80,t81,t82,t83,t84,t85,t86,t87,t88,t89,t90,t91,t92,t93,t94,t95,t96,t97,t98,t99,t100,t101,t102,t103,t104,t105,t106,t107,t108,t109,t110,t111,t112,t113,t114,t115,t116,t117,t118,t119,t120,t121,t122,t123,t124,t125,t126,t127,t128,t129,t130,t131,t132,t133,t134,t135,t136,t137,t138,t139,t140,t141,t142,t143,t144,t145,t146,t147,t148,t149,t150,t151,t152,t153,t154,t155,t156,t157,t158,t159,t160,t161,t162,t163,t164,t165,t166,t167,t168,t169,t170,t171,t172,t173,t174,t175,t176,t177,t178,t179,t180,t181,t182,t183,t184,t185,t186,t187,t188,t189,t190,t191,t192,t193,t194,t195,t196,t197,t198,t199,t200,t201,t202,t203,t204,t205,t206,t207,t208,t209,t210,t211,t212,t213,t214,t215,t216,t217,t218,t219,t220,t221,t222,t223,t224,t225,t226,t227,t228,t229,t230,t231,t232,t233,t234,t235,t236,t237,t238,t239,t240,t241,t242,t243,t244,t245,t246,t247,t248,t249,t250,t251,t252,t253,t254,t255,t256,t00;
# comments before command
#
create table t1 (a int);
show status like "Qcache_queries_in_cache";
show status like "Qcache_inserts";
show status like "Qcache_hits";
/**/ select * from t1;
/**/ select * from t1;
show status like "Qcache_queries_in_cache";
show status like "Qcache_inserts";
show status like "Qcache_hits";
drop table t1;

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

@ -0,0 +1,145 @@
#
# Stored PROCEDURE error tests
#
# Make sure we don't have any procedures left.
delete from mysql.proc;
delimiter |;
# This should give three syntax errors (sometimes crashed; bug #643)
# (Unfortunately, this is not a 100% test, on some platforms this
# passed despite the bug.)
--error 1064
create procedure syntaxerror(t int)|
--error 1064
create procedure syntaxerror(t int)|
--error 1064
create procedure syntaxerror(t int)|
# 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 1270
create procedure foo()
create procedure bar() set @x=3|
--error 1270
create procedure foo()
create function bar() returns double return 2.3|
# Already exists
--error 1271
create procedure proc1()
set @x = 42|
--error 1271
create function func1() returns int
return 42|
drop procedure proc1|
drop function func1|
# Does not exist
--error 1272
alter procedure foo|
--error 1272
alter function foo|
--error 1272
drop procedure foo|
--error 1272
drop function foo|
--error 1272
call foo()|
drop procedure if exists foo|
# LEAVE/ITERATE with no match
--error 1275
create procedure foo()
foo: loop
leave bar;
end loop|
--error 1275
create procedure foo()
foo: loop
iterate bar;
end loop|
# Redefining label
--error 1276
create procedure foo()
foo: loop
foo: loop
set @x=2;
end loop foo;
end loop foo|
# End label mismatch
--error 1277
create procedure foo()
foo: loop
set @x=2;
end loop bar|
# Referring to undef variable
--error 1278
create procedure foo(out x int)
begin
declare y int;
set x = y;
end|
# We require INTO in SELECTs for some older clients (as mysql and mysqltest,
# for now).
create procedure foo()
begin
select name from mysql.proc;
select type from mysql.proc;
end|
--error 1279
call foo()|
drop procedure foo|
# RETURN in FUNCTION only
--error 1280
create procedure foo()
return 42|
# Doesn't allow queries in FUNCTIONs (for now :-( )
--error 1281
create function foo() returns int
begin
declare x int;
select max(c) into x from test.t;
return x;
end|
# Wrong number of arguments
create procedure p(x int)
insert into test.t1 values (x)|
create function f(x int) returns int
return x+42|
--error 1285
call p()|
--error 1285
call p(1, 2)|
--error 1285
select f()|
--error 1285
select f(1, 2)|
drop procedure p|
drop function f|
delimiter ;|

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

@ -0,0 +1,640 @@
#
# 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|
# "Deep" calls...
create procedure c1(x int)
call c2("c", x)|
create procedure c2(s char(16), x int)
call c3(x, s)|
create procedure c3(x int, s char(16))
call c4("level", x, s)|
create procedure c4(l char(8), x int, s char(16))
insert into t1 values (concat(l,s), x)|
call c1(42)|
select * from t1|
delete from t1|
drop procedure c1|
drop procedure c2|
drop procedure c3|
drop procedure c4|
# 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|
# Subselect arguments
insert into t2 values ("a", 1, 1.1), ("b", 2, 1.2), ("c", 3, 1.3)|
create procedure sub1(id char(16), x int)
insert into test.t1 values (id, x)|
# QQ This doesn't work yet
#create procedure sub2(id char(16))
#begin
# declare x int;
# set x = (select sum(t.x) from test.t2 t);
# insert into test.t1 values (id, x);
#end|
create function sub3(i int) returns int
return i+1|
call sub1("sub1a", (select 7))|
call sub1("sub1b", (select max(i) from t2))|
call sub1("sub1c", (select i,d from t2 limit 1))|
call sub1("sub1d", (select 1 from (select 1) a))|
#call sub2("sub2");
select * from t1|
select sub3((select max(i) from t2))|
drop procedure sub1|
#drop procedure sub2|
drop function sub3|
# 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.t3 select * from test.t1;
insert into test.t3 values (concat(x, "2"), y+2);
end|
--disable_warnings
drop table if exists t3|
--enable_warnings
call create_select("cs", 90)|
select * from t1, t3|
--disable_warnings
drop table if exists t3|
--enable_warnings
delete from t1|
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 ambitious. ;-)
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

@ -351,7 +351,9 @@ 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;
# After this, only data based on old t1 records should have been added.
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
select * from t1;
-- error 1054
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
@ -506,6 +508,9 @@ select ROW(1, 1, 'a') IN (select b,a,c from t1 where c='b' or c='a');
select ROW(1, 1, 'a') IN (select b,a,c from t1 limit 2);
drop table t1;
#
# DO & SET
#
create table t1 (a int);
insert into t1 values (1);
do @a:=(SELECT a from t1);

View File

@ -5,8 +5,20 @@
drop table if exists t1,t2;
--enable_warnings
set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
select @test,@`select`,@TEST,@not_used;
# case insensitivity tests (new in 5.0)
set @`test`=1;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
set @TEST=2;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
set @"tEST"=3;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
set @`TeST`=4;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
select @`teST`:=5;
select @test, @`test`, @TEST, @`TEST`, @"teSt";
set @select=2,@t5=1.23456;
select @`select`,@not_used;
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
set @test_int="hello",@test_double="hello",@test_string="hello",@test_string2="hello";

View File

@ -29,7 +29,7 @@ rm -rf Makefile.in.bk
make clean bin-dist
# mark the build
for file in *.tar.gz
for file in *.tar.gz *.zip
do
if (expr "$file" : "mysql-[1-9].*" > /dev/null)
then

View File

@ -8,6 +8,7 @@ set -e
path=`dirname $0`
$path/compile-netware-src
$path/compile-netware-standard
$path/compile-netware-debug
#$path/compile-netware-max

View File

@ -0,0 +1,36 @@
#! /bin/sh
# debug
#set -x
# stop on errors
set -e
if test ! -r ./sql/mysqld.cc
then
echo "you must start from the top source directory"
exit 1
fi
path=`dirname $0`
# clean
if test -e "Makefile"; then
make -k clean;
make -k distclean;
fi
# remove other files
rm -f NEW-RPMS/*
rm -f */.deps/*.P
rm -rf Makefile.in.bk
# zip source
files=`pwd | sed -e "s/.*\\\(mysql-.*\)/\1/"`
file=`pwd | sed -e "s/.*\\mysql-\(.*\)/mysql-src-\1-pc-netware-i686/"`
cd ..
if test -e "$file.zip"; then rm -f $file.zip; fi
zip -r $file.zip $files -x \*.zip -x \*.tar.gz
if test -e "./$files/$file.zip"; then mv -f ./$files/$file.zip ./$files/$file.zip.old; fi
mv -f $file.zip ./$files/$file.zip

View File

@ -13,7 +13,7 @@ path=`dirname $0`
suffix="standard"
extra_configs=" \
--with-innodb
--with-innodb \
"
. $path/compile-netware-END

View File

@ -0,0 +1,2 @@
kYieldIfTimeSliceUp

View File

@ -7,8 +7,8 @@
export MYDEV="WINE_BUILD_DIR"
export MWCNWx86Includes="$MYDEV/libc/include;$MYDEV/zlib-1.1.4"
export MWNWx86Libraries="$MYDEV/libc/imports;$MYDEV/mw/lib;$MYDEV/zlib-1.1.4"
export MWNWx86LibraryFiles="libcpre.o;libc.imp;netware.imp;mwcrtl.lib;mwcpp.lib;libz.a"
export MWNWx86Libraries="$MYDEV/libc/imports;$MYDEV/mw/lib;$MYDEV/mysql-VERSION/netware/BUILD;$MYDEV/zlib-1.1.4"
export MWNWx86LibraryFiles="libcpre.o;libc.imp;netware.imp;mwcrtl.lib;mwcpp.lib;knetware.imp;libz.a"
export WINEPATH="$MYDEV/mw/bin"
@ -19,9 +19,9 @@ export AR='mwldnlm'
export AR_FLAGS='-type library -o'
export AS='mwasmnlm'
export CC='mwccnlm -gccincludes'
export CFLAGS='-dialect c -proc 686 -relax_pointers'
export CFLAGS='-align 8 -proc 686 -relax_pointers -dialect c'
export CXX='mwccnlm -gccincludes'
export CXXFLAGS='-dialect c++ -proc 686 -bool on -wchar_t on -relax_pointers -D_WCHAR_T'
export CXXFLAGS='-align 8 -proc 686 -relax_pointers -dialect c++ -bool on -wchar_t on -D_WCHAR_T'
export LD='mwldnlm'
export LDFLAGS='-entry _LibCPrelude -exit _LibCPostlude -flags pseudopreemption'
export RANLIB=:

View File

@ -147,10 +147,12 @@ then
fi
# make files writeable
echo "making files writable..."
cd $target_dir
chmod -R u+rw,g+rw .
# edit the mvenv file
echo "updating the mwenv environment file..."
mwenv="./netware/BUILD/mwenv"
mv -f $mwenv $mwenv.org
sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \
@ -158,6 +160,17 @@ sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \
-e "s;VERSION;$version;g" $mwenv.org > $mwenv
chmod +rwx $mwenv
#edit the def file versions
echo "updating *.def file versions..."
nlm_version=`echo "$version" | sed -e "s;\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*;\1, \2, \3;"`
for file in ./netware/*.def
do
mv -f $file $file.org
sed -e "s;VERSION.*;VERSION $nlm_version;g" $file.org > $file
rm $file.org
done
# build linux tools
echo "compiling linux tools..."
./netware/BUILD/compile-linux-tools

View File

@ -1,4 +1,4 @@
# Copyright (c) 2002 Novell, Inc. All Rights Reserved.
# Copyright (c) 2002 Novell, Inc. All Rights Reserved.
#
# 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
@ -11,38 +11,37 @@
# 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
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
INCLUDES = -I$(srcdir)/../include -I../include -I..
bin_PROGRAMS = mysqld_safe mysql_install_db mysql_test_run libmysql
mysqld_safe_SOURCES= mysqld_safe.c my_manage.c
mysql_install_db_SOURCES= mysql_install_db.c my_manage.c
mysql_test_run_SOURCES= mysql_test_run.c my_manage.c
libmysql_SOURCES= libmysqlmain.c
libmysql_LDADD = ../libmysql/.libs/libmysqlclient.a
INCLUDES= -I$(srcdir)/../include -I../include -I..
bin_PROGRAMS= mysqld_safe mysql_install_db mysql_test_run libmysql
mysqld_safe_SOURCES= mysqld_safe.c my_manage.c
mysql_install_db_SOURCES= mysql_install_db.c my_manage.c
mysql_test_run_SOURCES= mysql_test_run.c my_manage.c
libmysql_SOURCES= libmysqlmain.c
libmysql_LDADD= ../libmysql/.libs/libmysqlclient.a
netware_build_files = client/mysql.def client/mysqladmin.def \
client/mysqlbinlog.def client/mysqlcheck.def \
client/mysqldump.def client/mysqlimport.def \
client/mysqlshow.def client/mysqltest.def \
extra/mysql_install.def extra/my_print_defaults.def \
extra/perror.def extra/replace.def \
extra/resolveip.def extra/comp_err.def \
isam/isamchk.def \
isam/isamlog.def isam/pack_isam.def \
libmysqld/libmysqld.def myisam/myisamchk.def \
myisam/myisamlog.def myisam/myisampack.def \
sql/mysqld.def
netware_build_files = client/mysql.def client/mysqladmin.def \
client/mysqlbinlog.def client/mysqlcheck.def \
client/mysqldump.def client/mysqlimport.def \
client/mysqlshow.def client/mysqltest.def \
extra/mysql_install.def extra/my_print_defaults.def \
extra/perror.def extra/replace.def \
extra/resolveip.def extra/comp_err.def \
isam/isamchk.def \
isam/isamlog.def isam/pack_isam.def \
libmysqld/libmysqld.def myisam/myisamchk.def \
myisam/myisamlog.def myisam/myisampack.def \
sql/mysqld.def
link_sources:
set -x; \
for f in $(netware_build_files); do \
rm -f $(srcdir)/../$$f; \
org=`echo $$f | sed -e 's/.*\/\(.*\)/\1/g'`; \
@LN_CP_F@ $(srcdir)/$$org $(srcdir)/../$$f; \
done;
set -x; \
for f in $(netware_build_files); do \
rm -f $(srcdir)/../$$f; \
org=`echo $$f | sed -e 's/.*\/\(.*\)/\1/g'`; \
@LN_CP_F@ $(srcdir)/$$org $(srcdir)/../$$f; \
done;
# Don't update the files from bitkeeper
%::SCCS/s.%

View File

@ -1,90 +1,104 @@
/*
Copyright (c) 2003 Novell, Inc. All Rights Reserved.
Copyright (c) 2003 MySQL AB
Copyright (c) 2003 Novell, Inc.
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 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.
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
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 <stdio.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <screen.h>
#include <proc.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
/*****************************************************************************
** Utility functions for support programs
*****************************************************************************/
/* MySQL library headers */
#include <my_global.h>
#include <my_sys.h>
#include <my_dir.h>
#include <m_string.h>
/* These 'should' be POSIX or ANSI */
#include <assert.h> /* ASSERT */
#include <stdarg.h> /* vsprintf, va_* */
#include <sys/types.h> /* pid_t */
#ifndef __WIN__
#include <unistd.h> /* fork, rmdir, execve */
#endif
#include <stdio.h> /* freopen */
#include <stdlib.h> /* FILE */
#ifndef __WIN__
#include <dirent.h> /* opendir, readdir */
#endif
#if !defined(__NETWARE__) && !defined(__WIN__)
#include <sys/wait.h>
#endif
#if !defined(__NETWARE__)
#include <signal.h>
#endif
/* For ASSERT -- Not totally sure about this one: */
#if !defined(ASSERT)
#define ASSERT(A) assert(A)
#endif
#include "my_manage.h"
#define __STDC__ 1
#include "process.h"
/******************************************************************************
macros
******************************************************************************/
init_args()
/******************************************************************************
global variables
******************************************************************************/
/******************************************************************************
functions
******************************************************************************/
/******************************************************************************
init_args()
Init an argument list.
Init an argument list.
******************************************************************************/
void init_args(arg_list_t *al)
{
#ifndef __WIN__
ASSERT(al != NULL);
al->argc = 0;
al->size = ARG_BUF;
al->argv = malloc(al->size * sizeof(char *));
al->argv = (char **)my_malloc(al->size * sizeof(char *), MYF(MY_WME));
ASSERT(al->argv != NULL);
#else
win_args[0]= '\0';
skip_first_param= TRUE;
#endif
return;
}
/******************************************************************************
add_arg()
Add an argument to a list.
add_arg()
Add an argument to a list.
******************************************************************************/
void add_arg(arg_list_t *al, char *format, ...)
void add_arg(arg_list_t *al, const char *format, ...)
{
#ifndef __WIN__
va_list ap;
char temp[PATH_MAX];
ASSERT(al != NULL);
// increase size
if (al->argc >= al->size)
/* increase size */
if (al->argc >= (int)al->size)
{
al->size += ARG_BUF;
al->argv = realloc(al->argv, al->size * sizeof(char *));
al->argv = (char **)my_realloc((char *)al->argv, al->size * sizeof(char *), MYF(MY_WME));
ASSERT(al->argv != NULL);
}
@ -94,7 +108,7 @@ void add_arg(arg_list_t *al, char *format, ...)
vsprintf(temp, format, ap);
va_end(ap);
al->argv[al->argc] = malloc(strlen(temp)+1);
al->argv[al->argc] = my_malloc(strlen(temp)+1, MYF(MY_WME));
ASSERT(al->argv[al->argc] != NULL);
strcpy(al->argv[al->argc], temp);
@ -104,19 +118,36 @@ void add_arg(arg_list_t *al, char *format, ...)
{
al->argv[al->argc] = NULL;
}
#else
va_list ap;
char param[PATH_MAX];
if (!skip_first_param)
{
va_start(ap, format);
vsprintf(&param, format, ap);
va_end(ap);
strcat(win_args," ");
strcat(win_args,param);
}
else
{
skip_first_param= FALSE;
}
#endif
return;
}
/******************************************************************************
free_args()
Free an argument list.
free_args()
Free an argument list.
******************************************************************************/
void free_args(arg_list_t *al)
{
#ifndef __WIN__
int i;
ASSERT(al != NULL);
@ -124,74 +155,110 @@ void free_args(arg_list_t *al)
for(i = 0; i < al->argc; i++)
{
ASSERT(al->argv[i] != NULL);
free(al->argv[i]);
my_free(al->argv[i], MYF(MY_WME));
al->argv[i] = NULL;
}
free(al->argv);
my_free((char *)al->argv, MYF(MY_WME));
al->argc = 0;
al->argv = NULL;
#endif
return;
}
/******************************************************************************
sleep_until_file_deleted()
Sleep until the given file is no longer found.
sleep_until_file_deleted()
Sleep until the given file is no longer found.
******************************************************************************/
int sleep_until_file_deleted(char *pid_file)
{
struct stat buf;
int i, err;
for(i = 0; (i < TRY_MAX) && (err = !stat(pid_file, &buf)); i++) sleep(1);
if (err != 0) err = errno;
return err;
MY_STAT stat_info;
int i, err = 0;
#ifndef __WIN__
for(i = 0; i < TRY_MAX; i++)
{
if (my_stat(pid_file, &stat_info, MYF(0)) == (MY_STAT *) NULL)
{
err = errno;
break;
}
my_sleep(1);
}
#else
switch (pid_mode)
{
case MASTER_PID:
err= (WaitForSingleObject(master_server, TRY_MAX*1000) == WAIT_TIMEOUT);
pid_mode= 0;
break;
case SLAVE_PID:
err= (WaitForSingleObject(slave_server, TRY_MAX*1000) == WAIT_TIMEOUT);
pid_mode= 0;
break;
};
#endif
return err;
}
/******************************************************************************
sleep_until_file_exists()
Sleep until the given file exists.
sleep_until_file_exists()
Sleep until the given file exists.
******************************************************************************/
int sleep_until_file_exists(char *pid_file)
{
struct stat buf;
int i, err;
for(i = 0; (i < TRY_MAX) && (err = stat(pid_file, &buf)); i++) sleep(1);
if (err != 0) err = errno;
return err;
MY_STAT stat_info;
int i, err = 0;
#ifndef __WIN__
for(i = 0; i < TRY_MAX; i++)
{
if (my_stat(pid_file, &stat_info, MYF(0)) == (MY_STAT *) NULL)
{
err = errno;
break;
}
my_sleep(1);
}
#else
switch (pid_mode)
{
case MASTER_PID:
WaitForSingleObject(master_server, TRY_MAX*1000);
pid_mode= 0;
break;
case SLAVE_PID:
WaitForSingleObject(slave_server, TRY_MAX*1000);
pid_mode= 0;
break;
};
#endif
return err;
}
/******************************************************************************
wait_for_server_start()
Wait for the server on the given port to start.
wait_for_server_start()
Wait for the server on the given port to start.
******************************************************************************/
int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
{
arg_list_t al;
int err, i;
char mysqladmin_file[PATH_MAX];
int err = 0, i;
char trash[PATH_MAX];
// mysqladmin file
snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
snprintf(trash, PATH_MAX, "/tmp/trash.out");
// args
/* mysqladmin file */
my_snprintf(trash, PATH_MAX, "/tmp/trash.out");
/* args */
init_args(&al);
add_arg(&al, "%s", mysqladmin_file);
add_arg(&al, "--no-defaults");
@ -205,13 +272,13 @@ int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
add_arg(&al, "--host=localhost");
add_arg(&al, "ping");
// NetWare does not support the connect timeout in the TCP/IP stack
// -- we will try the ping multiple times
for(i = 0; (i < TRY_MAX)
&& (err = spawn(mysqladmin_file, &al, TRUE, NULL,
trash, NULL)); i++) sleep(1);
// free args
/* NetWare does not support the connect timeout in the TCP/IP stack
-- we will try the ping multiple times */
for(i = 0; (i < TRY_MAX)
&& (err = spawn(mysqladmin_file, &al, TRUE, NULL,
trash, NULL, NOT_NEED_PID)); i++) sleep(1);
/* free args */
free_args(&al);
return err;
@ -219,20 +286,23 @@ int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
/******************************************************************************
spawn()
Spawn the given path with the given arguments.
spawn()
Spawn the executable at the given path with the given arguments.
******************************************************************************/
#ifdef __NETWARE__
int spawn(char *path, arg_list_t *al, int join, char *input,
char *output, char *error)
{
pid_t pid;
pid_t pid;
int result = 0;
wiring_t wiring = { FD_UNUSED, FD_UNUSED, FD_UNUSED };
unsigned long flags = PROC_CURRENT_SPACE | PROC_INHERIT_CWD;
// open wiring
/* open wiring */
if (input)
wiring.infd = open(input, O_RDONLY);
@ -242,14 +312,14 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
if (error)
wiring.errfd = open(error, O_WRONLY | O_CREAT | O_TRUNC);
// procve requires a NULL
/* procve requires a NULL */
add_arg(al, NULL);
// go
/* go */
pid = procve(path, flags, NULL, &wiring, NULL, NULL, 0,
NULL, (const char **)al->argv);
if (pid == -1)
if (pid == -1)
{
result = -1;
}
@ -257,8 +327,8 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
{
waitpid(pid, &result, 0);
}
// close wiring
/* close wiring */
if (wiring.infd != -1)
close(wiring.infd);
@ -268,207 +338,411 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
if (wiring.errfd != -1)
close(wiring.errfd);
return result;
return result;
}
#else /* NOT __NETWARE__ */
#ifdef __WIN__
int my_vsnprintf_(char *to, size_t n, const char* value, ...)
{
char *start=to, *end=to+n-1;
uint length, num_state, pre_zero;
reg2 char *par;// = value;
va_list args;
va_start(args,value);
par = va_arg(args, char *);
while (par != NULL)
{
uint plen,left_len = (uint)(end-to)+1;
if (!par) par = (char*)"(null)";
plen = (uint) strlen(par);
if (left_len <= plen)
plen = left_len - 1;
to=strnmov(to+strlen(to),par,plen);
par = va_arg(args, char *);
}
va_end(args);
DBUG_ASSERT(to <= end);
*to='\0';
return (uint) (to - start);
}
int spawn(char *path, arg_list_t *al, int join, char *input,
char *output, char *error)
{
char *cl;
char *arg;
intptr_t result;
int j;
int err;
STARTUPINFO startup_info;
PROCESS_INFORMATION process_information;
ULONG dosretval;
int retval;
DWORD exit_code;
SECURITY_ATTRIBUTES process_attributes, thread_attributes;
char command_line[1024]= "";
memset(&startup_info,0,sizeof(STARTUPINFO));
startup_info.cb = sizeof(STARTUPINFO);
if (input)
freopen(input, "rb", stdin);
if (output)
freopen(output, "wb", stdout);
if (error)
freopen(error, "wb", stderr);
result= CreateProcess(
path,
&win_args,
NULL,
NULL,
TRUE,
0,
NULL,
NULL,
&startup_info,
&process_information
);
if (process_information.hProcess)
{
if (join)
{
if (WaitForSingleObject(process_information.hProcess, mysqld_timeout) == WAIT_TIMEOUT)
{
exit_code= -1;
}
else
{
GetExitCodeProcess(process_information.hProcess, &exit_code);
}
CloseHandle(process_information.hProcess);
}
else
{
exit_code= 0;
}
if (run_server)
{
switch (pid_mode)
{
case MASTER_PID:
master_server= process_information.hProcess;
break;
case SLAVE_PID:
slave_server= process_information.hProcess;
break;
};
pid_mode= 0;
run_server= FALSE;
};
}
else
{
exit_code= -1;
}
if (input)
freopen("CONIN$","rb",stdin);
if (output)
freopen("CONOUT$","wb",stdout);
if (error)
freopen("CONOUT$","wb",stderr);
return exit_code;
}
#else /* NOT __NETWARE__, NOT __WIN__ */
/* This assumes full POSIX.1 compliance */
int spawn(char *path, arg_list_t *al, int join, char *input,
char *output, char *error)
{
int result = 0;
pid_t pid;
if ((pid = fork()))
{
/* Remains in parent process */
if (join && (pid != -1))
waitpid(pid, &result, 0);
}
else
{
/* Child process */
/* Reassign streams */
if (input)
freopen(input, "r", stdin);
if (output)
freopen(output, "w", stdout);
if (error)
freopen(error, "w", stderr);
/* Spawn the process */
execve(path, al->argv, environ);
}
return result;
}
#endif /* __WIN__ */
#endif /* __NETWARE__ */
/******************************************************************************
stop_server()
Stop the server with the given port and pid file.
stop_server()
Stop the server with the given port and pid file.
******************************************************************************/
int stop_server(char *bin_dir, char *user, char *password, int port,
char *pid_file)
{
arg_list_t al;
int err, i, argc = 0;
char mysqladmin_file[PATH_MAX];
arg_list_t al;
int err;
char trash[PATH_MAX];
// mysqladmin file
snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
snprintf(trash, PATH_MAX, "/tmp/trash.out");
// args
init_args(&al);
add_arg(&al, "%s", mysqladmin_file);
add_arg(&al, "--no-defaults");
add_arg(&al, "--port=%u", port);
add_arg(&al, "--user=%s", user);
add_arg(&al, "--password=%s", password);
add_arg(&al, "-O");
add_arg(&al, "shutdown_timeout=20");
add_arg(&al, "shutdown");
my_snprintf(trash, PATH_MAX, "/tmp/trash.out");
// spawn
if ((err = spawn(mysqladmin_file, &al, TRUE, NULL,
trash, NULL)) == 0)
{
sleep_until_file_deleted(pid_file);
}
else
{
/* args */
init_args(&al);
add_arg(&al, "%s", mysqladmin_file);
add_arg(&al, "--no-defaults");
add_arg(&al, "--port=%u", port);
add_arg(&al, "--user=%s", user);
add_arg(&al, "--password=%s", password);
add_arg(&al, "-O");
add_arg(&al, "shutdown_timeout=20");
add_arg(&al, "shutdown");
/* spawn */
if ((err = spawn(mysqladmin_file, &al, TRUE, NULL,
trash, NULL)) == 0)
{
sleep_until_file_deleted(pid_file);
}
else
{
pid_t pid = get_server_pid(pid_file);
// shutdown failed - kill server
kill_server(pid);
sleep(TRY_MAX);
// remove pid file if possible
err = remove(pid_file);
/* shutdown failed - kill server */
kill_server(pid);
sleep(TRY_MAX);
/* remove pid file if possible */
err = my_delete(pid_file, MYF(MY_WME));
}
// free args
/* free args */
free_args(&al);
return err;
return err;
}
/******************************************************************************
get_server_pid()
Get the VM id with the given pid file.
get_server_pid()
Get the VM id with the given pid file.
******************************************************************************/
pid_t get_server_pid(char *pid_file)
{
char buf[PATH_MAX];
int fd, err;
char *p;
pid_t id;
// discover id
fd = open(pid_file, O_RDONLY);
err = read(fd, buf, PATH_MAX);
close(fd);
if (err > 0)
{
// terminate string
if ((p = strchr(buf, '\n')) != NULL)
{
*p = NULL;
// check for a '\r'
if ((p = strchr(buf, '\r')) != NULL)
{
*p = NULL;
}
}
else
{
buf[err] = NULL;
}
id = strtol(buf, NULL, 0);
}
char buf[PATH_MAX];
int err;
File fd;
char *p;
pid_t id = 0;
/* discover id */
fd = my_open(pid_file, O_RDONLY, MYF(MY_WME));
err = my_read(fd, buf, PATH_MAX, MYF(MY_WME));
my_close(fd, MYF(MY_WME));
if (err > 0)
{
/* terminate string */
if ((p = strchr(buf, '\n')) != NULL)
{
*p = '\0';
/* check for a '\r' */
if ((p = strchr(buf, '\r')) != NULL)
{
*p = '\0';
}
}
else
{
buf[err] = '\0';
}
id = strtol(buf, NULL, 0);
}
return id;
}
/******************************************************************************
kill_server()
Force a kill of the server with the given pid.
kill_server()
Force a kill of the server with the given pid.
******************************************************************************/
void kill_server(pid_t pid)
{
if (pid > 0)
{
// destroy vm
#if !defined(__NETWARE__)
/* Send SIGTERM to pid */
kill(pid, SIGTERM);
#else /* __NETWARE__ */
/* destroy vm */
NXVmDestroy(pid);
#endif
}
}
/******************************************************************************
del_tree()
Delete the directory and subdirectories.
del_tree()
Delete the directory and subdirectories.
******************************************************************************/
void del_tree(char *dir)
{
DIR *parent = opendir(dir);
DIR *entry;
char temp[PATH_MAX];
if (parent == NULL)
{
return;
}
while((entry = readdir(parent)) != NULL)
{
// create long name
snprintf(temp, PATH_MAX, "%s/%s", dir, entry->d_name);
if (entry->d_name[0] == '.')
{
// Skip
}
else if (S_ISDIR(entry->d_type))
{
// delete subdirectory
del_tree(temp);
}
else
{
// remove file
remove(temp);
}
}
// remove directory
rmdir(dir);
}
/******************************************************************************
removef()
******************************************************************************/
int removef(char *format, ...)
{
va_list ap;
char path[PATH_MAX];
va_start(ap, format);
vsnprintf(path, PATH_MAX, format, ap);
va_end(ap);
MY_DIR *current;
uint i;
char temp[PATH_MAX];
return remove(path);
current = my_dir(dir, MYF(MY_WME | MY_WANT_STAT));
/* current is NULL if dir does not exist */
if (current == NULL)
return;
for (i = 0; i < current->number_off_files; i++)
{
/* create long name */
my_snprintf(temp, PATH_MAX, "%s/%s", dir, current->dir_entry[i].name);
if (current->dir_entry[i].name[0] == '.')
{
/* Skip */
}
else if (MY_S_ISDIR(current->dir_entry[i].mystat.st_mode))
{
/* delete subdirectory */
del_tree(temp);
}
else
{
/* remove file */
my_delete(temp, MYF(MY_WME));
}
}
my_dirend(current);
/* remove directory */
rmdir(dir);
}
/******************************************************************************
get_basedir()
removef()
******************************************************************************/
int removef(const char *format, ...)
{
va_list ap;
char path[PATH_MAX];
va_start(ap, format);
my_vsnprintf(path, PATH_MAX, format, ap);
va_end(ap);
#ifdef __WIN__
{
MY_DIR *current;
uint i;
struct _finddata_t find;
char temp[PATH_MAX];
#ifdef _WIN64
__int64 handle;
#else
long handle;
#endif
char *p;
p= strrchr(path,'\\');
if (p == NULL)
{
p= strrchr(path,'/');
if (p == NULL)
p= &path;
else
p++;
}
else
p++;
if ((handle=_findfirst(path,&find)) == -1L)
return 0;
do
{
strcpy(p,find.name);
my_delete(path, MYF(MY_WME));
} while (!_findnext(handle,&find));
_findclose(handle);
}
#else
return my_delete(path, MYF(MY_WME));
#endif
}
/******************************************************************************
get_basedir()
******************************************************************************/
void get_basedir(char *argv0, char *basedir)
{
char temp[PATH_MAX];
char *p;
ASSERT(argv0 != NULL);
char temp[PATH_MAX];
char *p;
ASSERT(argv0 != NULL);
ASSERT(basedir != NULL);
strcpy(temp, strlwr(argv0));
while((p = strchr(temp, '\\')) != NULL) *p = '/';
if ((p = strindex(temp, "/bin/")) != NULL)
{
*p = NULL;
strcpy(basedir, temp);
}
strcpy(temp, argv0);
#ifndef __WIN__
casedn_str(temp);
#endif
while((p = strchr(temp, '\\')) != NULL) *p = '/';
if ((p = strstr(temp, "/bin/")) != NullS)
{
*p = '\0';
strcpy(basedir, temp);
}
}

View File

@ -26,17 +26,41 @@
******************************************************************************/
#include <stdlib.h>
#ifndef __WIN__
#include <unistd.h>
#endif
/******************************************************************************
macros
******************************************************************************/
#ifdef __WIN__
#define PATH_MAX _MAX_PATH
#define NAME_MAX _MAX_FNAME
#define kill(A,B) TerminateProcess((HANDLE)A,0)
#define NOT_NEED_PID 0
#define MASTER_PID 1
#define SLAVE_PID 2
#define mysqld_timeout 60000
intptr_t master_server;
intptr_t slave_server;
int pid_mode;
bool run_server;
char win_args[1024];
bool skip_first_param;
#endif
#define ARG_BUF 10
#define TRY_MAX 5
#ifdef __NETWARE__
#define strstr(A,B) strindex(A,B)
#endif
/******************************************************************************
structures
@ -53,6 +77,8 @@ typedef struct
} arg_list_t;
typedef int pid_t;
/******************************************************************************
global variables
@ -66,7 +92,7 @@ typedef struct
******************************************************************************/
void init_args(arg_list_t *);
void add_arg(arg_list_t *, char *, ...);
void add_arg(arg_list_t *, const char *, ...);
void free_args(arg_list_t *);
int sleep_until_file_exists(char *);
@ -80,8 +106,12 @@ pid_t get_server_pid(char *);
void kill_server(pid_t pid);
void del_tree(char *);
int removef(char *, ...);
int removef(const char *, ...);
void get_basedir(char *, char *);
char mysqladmin_file[PATH_MAX];
#endif /* _MY_MANAGE */

File diff suppressed because it is too large Load Diff

View File

@ -73,9 +73,25 @@ if [ $BASE_SYSTEM != "netware" ] ; then
chmod o-rwx $BASE/data $BASE/data/*
fi
for i in ChangeLog COPYING COPYING.LIB README Docs/INSTALL-BINARY \
MySQLEULA.txt Docs/manual.html Docs/manual.txt Docs/manual_toc.html \
LICENSE.doc README.NW Docs/mysqlbug.txt
# Non platform-specific doc files:
DOC_FILES=" \
ChangeLog COPYING COPYING.LIB README \
Docs/manual.html Docs/manual.txt Docs/manual_toc.html \
Docs/mysqlbug.txt \
";
# Platform-specific doc files:
if [ $BASE_SYSTEM = "netware" ] ; then
DOC_FILES="$DOC_FILES \
";
# For all other platforms:
else
DOC_FILES="$DOC_FILES \
Docs/INSTALL-BINARY MySQLEULA.txt \
";
fi
for i in $DOC_FILES
do
if [ -f $i ]
then
@ -83,7 +99,7 @@ do
fi
done
# Non platform-specific bin dir files:
# Non platform-specific bin files:
BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
extra/resolveip$BS extra/my_print_defaults$BS \
extra/resolve_stack_dump$BS extra/mysql_waitpid$BS \
@ -93,18 +109,18 @@ BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
client/mysql$BS client/mysqlshow$BS client/mysqladmin$BS \
client/mysqldump$BS client/mysqlimport$BS \
client/mysqltest$BS client/mysqlcheck$BS \
client/mysqlbinlog$BS
client/mysqlbinlog$BS \
";
# Platform-specific bin dir files:
# Platform-specific bin files:
if [ $BASE_SYSTEM = "netware" ] ; then
BIN_FILES="$BIN_FILES \
netware/mysqld_safe$BS netware/mysql_install_db$BS \
netware/init_db.sql netware/test_db.sql netware/mysql_explain_log$BS \
netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql
netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql \
";
# For all other platforms:
else
# For all other platforms:
BIN_FILES="$BIN_FILES \
client/mysqlmanagerc \
client/mysqlmanager-pwgen tools/mysqlmanager \
@ -224,7 +240,9 @@ rm -f $BASE/bin/Makefile* $BASE/bin/*.in $BASE/bin/*.sh $BASE/bin/mysql_install_
# Make safe_mysqld a symlink to mysqld_safe for backwards portability
# To be removed in MySQL 4.1
(cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
if [ $BASE_SYSTEM != "netware" ] ; then
(cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
fi
# Clean up if we did this from a bk tree
if [ -d $BASE/sql-bench/SCCS ] ; then
@ -283,29 +301,48 @@ which_1 ()
exit 1
}
#
# Create the result tar file
#
tar=`which_1 gnutar gtar`
if test "$?" = "1" -o "$tar" = ""
then
tar=tar
fi
echo "Using $tar to create archive"
cd $TMP
if [ $BASE_SYSTEM = "netware" ] ; then
#
# Create a zip file for NetWare users
#
if test -e "$SOURCE/$NEW_NAME.zip"; then rm $SOURCE/$NEW_NAME.zip; fi
zip -r $SOURCE/$NEW_NAME.zip $NEW_NAME
echo "$NEW_NAME.zip created"
else
#
# Create the result tar file
#
tar=`which_1 gnutar gtar`
if test "$?" = "1" -o "$tar" = ""
then
tar=tar
fi
echo "Using $tar to create archive"
OPT=cvf
if [ x$SILENT = x1 ] ; then
OPT=cf
fi
$tar $OPT $SOURCE/$NEW_NAME.tar $NEW_NAME
cd $SOURCE
echo "Compressing archive"
gzip -9 $NEW_NAME.tar
echo "$NEW_NAME.tar.gz created"
OPT=cvf
if [ x$SILENT = x1 ] ; then
OPT=cf
fi
$tar $OPT $SOURCE/$NEW_NAME.tar $NEW_NAME
cd $SOURCE
echo "Compressing archive"
gzip -9 $NEW_NAME.tar
echo "Removing temporary directory"
rm -r -f $BASE
echo "$NEW_NAME.tar.gz created"

View File

@ -39,6 +39,7 @@ c_hc=""
c_hr=""
c_hk=""
i_ht=""
c_p=""
# Check for old tables
if test ! -f $mdata/db.frm
@ -285,6 +286,22 @@ then
c_hr="$c_hr comment='keyword-topic relation';"
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 creator char(77) binary DEFAULT '' NOT NULL,"
c_p="$c_p modified timestamp,"
c_p="$c_p created timestamp,"
c_p="$c_p suid enum ('N', 'Y') DEFAULT 'Y' NOT NULL,"
c_p="$c_p comment char(64) binary DEFAULT '' NOT NULL,"
c_p="$c_p PRIMARY KEY (name,type)"
c_p="$c_p )"
c_p="$c_p comment='Stored Procedures';"
fi
cat << END_OF_DATA
use mysql;
$c_d
@ -306,5 +323,8 @@ $c_ht
$c_hc
$c_hr
$c_hk
$c_p
END_OF_DATA

View File

@ -58,6 +58,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
log_event.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
spatial.h gstream.h client_settings.h
sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.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 \
@ -86,7 +87,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\
gstream.cc spatial.cc sql_help.cc protocol_cursor.cc
gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)

View File

@ -331,7 +331,7 @@ static ha_rows find_all_keys(SORTPARAM *param, SQL_SELECT *select,
byte *ref_pos,*next_pos,ref_buff[MAX_REFLENGTH];
my_off_t record;
TABLE *sort_form;
volatile bool *killed= &current_thd->killed;
volatile THD::killed_state *killed= &current_thd->killed;
handler *file;
DBUG_ENTER("find_all_keys");
DBUG_PRINT("info",("using: %s",(select?select->quick?"ranges":"where":"every row")));
@ -775,15 +775,15 @@ int merge_buffers(SORTPARAM *param, IO_CACHE *from_file,
BUFFPEK *buffpek,**refpek;
QUEUE queue;
qsort2_cmp cmp;
volatile bool *killed= &current_thd->killed;
bool not_killable;
volatile THD::killed_state *killed= &current_thd->killed;
THD::killed_state not_killable;
DBUG_ENTER("merge_buffers");
statistic_increment(filesort_merge_passes, &LOCK_status);
if (param->not_killable)
{
killed= &not_killable;
not_killable= 0;
not_killable=THD::NOT_KILLED;
}
error=0;
@ -1129,7 +1129,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
The fact is the filter 'field->query_id != thd->query_id'
doesn't work for alter table
*/
if (thd->lex.sql_command != SQLCOM_SELECT)
if (thd->lex->sql_command != SQLCOM_SELECT)
return 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{

View File

@ -2103,7 +2103,7 @@ static void print_msg(THD *thd, const char *table_name, const char *op_name,
protocol->store(msg_type);
protocol->store(msgbuf);
if (protocol->write())
thd->killed=1;
thd->killed=THD::KILL_CONNECTION;
}
#endif

View File

@ -2226,8 +2226,8 @@ ha_innobase::write_row(
skip_auto_inc_decr = FALSE;
if (error == DB_DUPLICATE_KEY
&& (user_thd->lex.sql_command == SQLCOM_REPLACE
|| user_thd->lex.sql_command
&& (user_thd->lex->sql_command == SQLCOM_REPLACE
|| user_thd->lex->sql_command
== SQLCOM_REPLACE_SELECT)) {
skip_auto_inc_decr= TRUE;

View File

@ -89,9 +89,9 @@ static void mi_check_print_msg(MI_CHECK *param, const char* msg_type,
extern "C" {
volatile bool *killed_ptr(MI_CHECK *param)
int *killed_ptr(void *thd)
{
return &(((THD *)(param->thd))->killed);
return (int*)&((THD *)thd)->killed;
}
void mi_check_print_error(MI_CHECK *param, const char *fmt,...)
@ -388,7 +388,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
{
HA_CHECK_OPT tmp_check_opt;
char* backup_dir = thd->lex.backup_dir;
char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;
@ -428,7 +428,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
char* backup_dir = thd->lex.backup_dir;
char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;

View File

@ -22,6 +22,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
#include "sp_rcontext.h"
static void mark_as_dependent(SELECT_LEX *last, SELECT_LEX *current,
Item_ident *item);
@ -56,10 +57,10 @@ Item::Item():
if (thd->lex.current_select)
{
SELECT_LEX_NODE::enum_parsing_place place=
thd->lex.current_select->parsing_place;
thd->lex->current_select->parsing_place;
if (place == SELECT_LEX_NODE::SELECT_LIST ||
place == SELECT_LEX_NODE::IN_HAVING)
thd->lex.current_select->select_n_having_items++;
thd->lex->current_select->select_n_having_items++;
}
}
@ -204,6 +205,23 @@ CHARSET_INFO * Item::default_charset() const
return current_thd->variables.collation_connection;
}
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 DTCollation::aggregate(DTCollation &dt)
{
if (!my_charset_same(collation, dt.collation))
@ -791,7 +809,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
Item **refer= (Item **)not_found_item;
uint counter;
// Prevent using outer fields in subselects, that is not supported now
SELECT_LEX *cursel=(SELECT_LEX *) thd->lex.current_select;
SELECT_LEX *cursel=(SELECT_LEX *) thd->lex->current_select;
if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE)
for (SELECT_LEX *sl= cursel->outer_select();
sl;
@ -1252,17 +1270,17 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
if (!ref)
{
TABLE_LIST *where= 0, *table_list;
SELECT_LEX *sl= thd->lex.current_select->outer_select();
SELECT_LEX *sl= thd->lex->current_select->outer_select();
/*
Finding only in current select will be performed for selects that have
not outer one and for derived tables (which not support using outer
fields for now)
*/
if ((ref= find_item_in_list(this,
*(thd->lex.current_select->get_item_list()),
*(thd->lex->current_select->get_item_list()),
&counter,
((sl &&
thd->lex.current_select->master_unit()->
thd->lex->current_select->master_unit()->
first_select()->linkage !=
DERIVED_TABLE_TYPE) ?
REPORT_EXCEPT_NOT_FOUND :
@ -1312,7 +1330,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
// Call to report error
find_item_in_list(this,
*(thd->lex.current_select->get_item_list()),
*(thd->lex->current_select->get_item_list()),
&counter,
REPORT_ALL_ERRORS);
ref= 0;
@ -1324,7 +1342,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
Item_field* fld;
if (!((*reference)= fld= new Item_field(tmp)))
return 1;
mark_as_dependent(last, thd->lex.current_select, fld);
mark_as_dependent(last, thd->lex->current_select, fld);
return 0;
}
else
@ -1335,7 +1353,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
mark_as_dependent(last, thd->lex.current_select,
mark_as_dependent(last, thd->lex->current_select,
this);
ref= last->ref_pointer_array + counter;
}
@ -1350,7 +1368,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
ref= thd->lex.current_select->ref_pointer_array + counter;
ref= thd->lex->current_select->ref_pointer_array + counter;
}
}
@ -1363,8 +1381,8 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
*/
if (((*ref)->with_sum_func && name &&
(depended_from ||
!(thd->lex.current_select->linkage != GLOBAL_OPTIONS_TYPE &&
thd->lex.current_select->having_fix_field))) ||
!(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE &&
thd->lex->current_select->having_fix_field))) ||
!(*ref)->fixed)
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,

View File

@ -196,6 +196,9 @@ public:
virtual bool remove_dependence_processor(byte * arg) { return 0; }
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; }
virtual Item* el(uint i) { return this; }
@ -208,6 +211,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

@ -1583,7 +1583,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
maybe_null=1;
}
if (thd)
thd->lex.current_select->cond_count+=list.elements;
thd->lex->current_select->cond_count+=list.elements;
fix_length_and_dec();
fixed= 1;
return 0;

View File

@ -83,7 +83,7 @@ Item *create_func_ceiling(Item* a)
Item *create_func_connection_id(void)
{
THD *thd=current_thd;
thd->lex.safe_to_cache_query=0;
thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong)
((thd->slave_thread) ?
thd->variables.pseudo_thread_id :
@ -163,7 +163,7 @@ Item *create_func_floor(Item* a)
Item *create_func_found_rows(void)
{
THD *thd=current_thd;
thd->lex.safe_to_cache_query=0;
thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong) thd->found_rows(),21);
}
@ -174,7 +174,7 @@ Item *create_func_from_days(Item* a)
Item *create_func_get_lock(Item* a, Item *b)
{
current_thd->lex.uncacheable();
current_thd->lex->uncacheable();
return new Item_func_get_lock(a, b);
}
@ -344,7 +344,7 @@ Item *create_func_radians(Item *a)
Item *create_func_release_lock(Item* a)
{
current_thd->lex.uncacheable();
current_thd->lex->uncacheable();
return new Item_func_release_lock(a);
}
@ -465,7 +465,7 @@ Item *create_func_year(Item* a)
Item *create_load_file(Item* a)
{
current_thd->lex.uncacheable();
current_thd->lex->uncacheable();
return new Item_load_file(a);
}
@ -491,13 +491,13 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs
Item *create_func_is_free_lock(Item* a)
{
current_thd->lex.uncacheable();
current_thd->lex->uncacheable();
return new Item_func_is_free_lock(a);
}
Item *create_func_is_used_lock(Item* a)
{
current_thd->lex.uncacheable();
current_thd->lex->uncacheable();
return new Item_func_is_used_lock(a);
}

View File

@ -31,6 +31,9 @@
#include <zlib.h>
#endif
#include "sp_head.h"
#include "sp_rcontext.h"
#include "sp.h"
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
const char *fname)
@ -2363,7 +2366,7 @@ void Item_func_get_user_var::fix_length_and_dec()
if ((var_entry= get_variable(&thd->user_vars, name, 0)))
{
if (opt_bin_log && is_update_query(thd->lex.sql_command) &&
if (opt_bin_log && is_update_query(thd->lex->sql_command) &&
var_entry->used_query_id != thd->query_id)
{
uint size;
@ -2783,7 +2786,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
if (!(item=var->item(thd, var_type, component_name)))
return 0; // Impossible
thd->lex.uncacheable();
thd->lex->uncacheable();
buff[0]='@';
buff[1]='@';
pos=buff+2;
@ -2823,7 +2826,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name,
DBUG_ASSERT(var != 0);
if (!(item=var->item(thd, var_type, &null_lex_string)))
return 0; // Impossible
thd->lex.uncacheable();
thd->lex->uncacheable();
item->set_name(item_name, 0, system_charset_info); // Will use original name
return item;
}
@ -2884,3 +2887,74 @@ longlong Item_func_is_used_lock::val_int()
null_value=0;
return ull->thread_id;
}
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

@ -1076,3 +1076,69 @@ enum Cast_target
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

@ -191,7 +191,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
{
have_to_be_excluded= 1;
if (join->thd->lex.describe)
if (join->thd->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
@ -503,14 +503,14 @@ Item_in_subselect::single_value_transformer(JOIN *join,
SELECT_LEX *current= thd->lex.current_select, *up;
thd->lex.current_select= up= current->return_after_parsing();
thd->lex->current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0))
{
thd->lex.current_select= current;
thd->lex->current_select= current;
DBUG_RETURN(ERROR);
}
thd->lex.current_select= current;
thd->lex->current_select= current;
/*
As far as Item_ref_in_optimizer do not substitude itself on fix_fields
@ -749,8 +749,8 @@ int subselect_single_select_engine::prepare()
if (prepared)
return 0;
prepared= 1;
SELECT_LEX *save_select= thd->lex.current_select;
thd->lex.current_select= select_lex;
SELECT_LEX *save_select= thd->lex->current_select;
thd->lex->current_select= select_lex;
if (join->prepare(&select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
select_lex->with_wild,
@ -763,7 +763,7 @@ int subselect_single_select_engine::prepare()
(ORDER*) 0, select_lex,
select_lex->master_unit(), 0))
return 1;
thd->lex.current_select= save_select;
thd->lex->current_select= save_select;
return 0;
}
@ -869,8 +869,8 @@ int subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= join->thd->where;
SELECT_LEX *save_select= join->thd->lex.current_select;
join->thd->lex.current_select= select_lex;
SELECT_LEX *save_select= join->thd->lex->current_select;
join->thd->lex->current_select= select_lex;
if (!optimized)
{
optimized=1;
@ -878,7 +878,7 @@ int subselect_single_select_engine::exec()
{
join->thd->where= save_where;
executed= 1;
join->thd->lex.current_select= save_select;
join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error?join->error:1);
}
if (item->engine_changed)
@ -891,7 +891,7 @@ int subselect_single_select_engine::exec()
if (join->reinit())
{
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
join->thd->lex->current_select= save_select;
DBUG_RETURN(1);
}
item->reset();
@ -902,11 +902,11 @@ int subselect_single_select_engine::exec()
join->exec();
executed= 1;
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error||thd->is_fatal_error);
}
join->thd->where= save_where;
join->thd->lex.current_select= save_select;
join->thd->lex->current_select= save_select;
DBUG_RETURN(0);
}

View File

@ -57,7 +57,7 @@ Item_sum::Item_sum(THD *thd, Item_sum &item):
void Item_sum::mark_as_sum_func()
{
current_thd->lex.current_select->with_sum_func= 1;
current_thd->lex->current_select->with_sum_func= 1;
with_sum_func= 1;
}
@ -1117,7 +1117,7 @@ void Item_sum_count_distinct::make_unique()
bool Item_sum_count_distinct::setup(THD *thd)
{
List<Item> list;
SELECT_LEX *select_lex= thd->lex.current_select;
SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
return 1;
@ -1613,7 +1613,7 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct,
quick_group= 0;
mark_as_sum_func();
item_thd= current_thd;
SELECT_LEX *select_lex= item_thd->lex.current_select;
SELECT_LEX *select_lex= item_thd->lex->current_select;
order= 0;
group_concat_max_len= item_thd->variables.group_concat_max_len;
@ -1795,7 +1795,7 @@ bool Item_func_group_concat::setup(THD *thd)
{
DBUG_ENTER("Item_func_group_concat::setup");
List<Item> list;
SELECT_LEX *select_lex= thd->lex.current_select;
SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
DBUG_RETURN(1);

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},
@ -102,6 +104,7 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM),0,0},
{ "COMPRESSED", SYM(COMPRESSED_SYM),0,0},
{ "CONCURRENT", SYM(CONCURRENT),0,0},
{ "CONNECTION", SYM(CONNECTION_SYM),0,0},
{ "CONSTRAINT", SYM(CONSTRAINT),0,0},
{ "CREATE", SYM(CREATE),0,0},
{ "CROSS", SYM(CROSS),0,0},
@ -109,6 +112,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},
@ -121,6 +125,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},
@ -143,6 +148,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},
@ -173,7 +179,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},
@ -200,6 +206,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},
@ -217,12 +225,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},
{ "LEAVES", SYM(LEAVES),0,0},
{ "LEFT", SYM(LEFT),0,0},
{ "LEVEL", SYM(LEVEL_SYM),0,0},
@ -239,6 +249,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},
@ -295,6 +306,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},
@ -325,13 +337,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 */
@ -345,6 +359,7 @@ static SYMBOL symbols[] = {
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_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},
@ -359,6 +374,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},
@ -401,6 +417,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},
{ "USER", SYM(USER),0,0},
@ -423,6 +440,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},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG),0,0},
{ "XOR", SYM(XOR),0,0},
{ "X509", SYM(X509_SYM),0,0},
@ -611,7 +629,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

@ -154,7 +154,7 @@ retry:
thd->proc_info=0;
if (thd->killed)
{
my_error(ER_SERVER_SHUTDOWN,MYF(0));
thd->send_kill_message();
if (sql_lock)
{
mysql_unlock_tables(thd,sql_lock);

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,
@ -1068,7 +1068,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,
@ -1511,8 +1511,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,
@ -1528,12 +1527,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

@ -798,7 +798,7 @@ Query_log_event::Query_log_event(THD* thd_arg, const char* query_arg,
0 : LOG_EVENT_THREAD_SPECIFIC_F, using_trans),
data_buf(0), query(query_arg),
db(thd_arg->db), q_len((uint32) query_length),
error_code(thd_arg->killed ? ER_SERVER_SHUTDOWN: thd_arg->net.last_errno),
error_code(thd_arg->killed != THD::NOT_KILLED ? thd->killed_errno() : thd_arg->net.last_errno),
thread_id(thd_arg->thread_id)
{
time_t end_time;

View File

@ -49,7 +49,7 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length,
CHARSET_INFO *from_cs,
uint32 max_res_length,
CHARSET_INFO *to_cs, uint32 *result_length);
void kill_one_thread(THD *thd, ulong id);
void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
bool net_request_file(NET* net, const char* fname);
char* query_table_status(THD *thd,const char *db,const char *table_name);
@ -374,7 +374,7 @@ bool is_update_query(enum enum_sql_command command);
void free_items(Item *item);
bool alloc_query(THD *thd, char *packet, ulong packet_length);
void mysql_init_select(LEX *lex);
void mysql_init_query(THD *thd);
void mysql_init_query(THD *thd, bool lexonly=0);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex);
@ -385,7 +385,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);
@ -938,22 +938,22 @@ void free_list(I_List <i_string> *list);
inline bool add_item_to_list(THD *thd, Item *item)
{
return thd->lex.current_select->add_item_to_list(thd, item);
return thd->lex->current_select->add_item_to_list(thd, item);
}
inline bool add_value_to_list(THD *thd, Item *value)
{
return thd->lex.value_list.push_back(value);
return thd->lex->value_list.push_back(value);
}
inline bool add_order_to_list(THD *thd, Item *item, bool asc)
{
return thd->lex.current_select->add_order_to_list(thd, item, asc);
return thd->lex->current_select->add_order_to_list(thd, item, asc);
}
inline bool add_group_to_list(THD *thd, Item *item, bool asc)
{
return thd->lex.current_select->add_group_to_list(thd, item, asc);
return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
inline void mark_as_null_row(TABLE *table)

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
@ -590,7 +591,7 @@ static void close_connections(void)
{
DBUG_PRINT("quit",("Informing thread %ld that it's time to die",
tmp->thread_id));
tmp->killed=1;
tmp->killed= THD::KILL_CONNECTION;
if (tmp->mysys_var)
{
tmp->mysys_var->abort=1;
@ -773,6 +774,7 @@ static void __cdecl kill_server(int sig_ptr)
unireg_abort(1); /* purecov: inspected */
else
unireg_end();
#ifdef __NETWARE__
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
@ -864,7 +866,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
@ -1229,12 +1230,12 @@ static void server_init(void)
void yyerror(const char *s)
{
THD *thd=current_thd;
char *yytext=(char*) thd->lex.tok_start;
char *yytext=(char*) thd->lex->tok_start;
/* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */
if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0)
s=ER(ER_SYNTAX_ERROR);
net_printf(thd,ER_PARSE_ERROR, s, yytext ? (char*) yytext : "",
thd->lex.yylineno);
thd->lex->yylineno);
}
@ -1364,7 +1365,7 @@ extern "C" sig_handler abort_thread(int sig __attribute__((unused)))
THD *thd=current_thd;
DBUG_ENTER("abort_thread");
if (thd)
thd->killed=1;
thd->killed= THD::KILL_CONNECTION;
DBUG_VOID_RETURN;
}
#endif
@ -1849,8 +1850,8 @@ extern "C" int my_message_sql(uint error, const char *str,
thd->lex.current_select equel to zero if lex structure is not inited
(not query command (COM_QUERY))
*/
if (thd->lex.current_select &&
thd->lex.current_select->no_error && !thd->is_fatal_error)
if (thd->lex->current_select &&
thd->lex->current_select->no_error && !thd->is_fatal_error)
{
DBUG_PRINT("error", ("above error converted to warning"));
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
@ -2177,9 +2178,55 @@ static int init_server_components()
LOG_NORMAL, 0, 0, 0);
if (opt_update_log)
{
open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
NullS, LOG_NEW, 0, 0, 0);
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",
@ -2862,7 +2909,7 @@ static void create_new_thread(THD *thd)
("Can't create thread to handle request (error %d)",
error));
thread_count--;
thd->killed=1; // Safety
thd->killed= THD::KILL_CONNECTION; // Safety
(void) pthread_mutex_unlock(&LOCK_thread_count);
statistic_increment(aborted_connects,&LOCK_status);
net_printf(thd,ER_CANT_CREATE_THREAD,error);
@ -3735,7 +3782,8 @@ Disable with --skip-bdb (will save memory).",
(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,
@ -4002,9 +4050,9 @@ replicating a LOAD DATA INFILE command.",
(gptr*) &mysqld_unix_port, (gptr*) &mysqld_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, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,

View File

@ -336,7 +336,7 @@ send_eof(THD *thd, bool no_flush)
uint tmp= min(thd->total_warn_count, 65535);
buff[0]=254;
int2store(buff+1, tmp);
int2store(buff+3, 0); // No flags yet
int2store(buff+3, thd->server_status);
VOID(my_net_write(net,(char*) buff,5));
VOID(net_flush(net));
}

View File

@ -175,7 +175,7 @@ static int rr_sequential(READ_RECORD *info)
{
if (info->thd->killed)
{
my_error(ER_SERVER_SHUTDOWN,MYF(0));
info->thd->send_kill_message();
return 1;
}
if (tmp != HA_ERR_RECORD_DELETED)

View File

@ -422,7 +422,7 @@ int show_new_master(THD* thd)
DBUG_ENTER("show_new_master");
List<Item> field_list;
char errmsg[SLAVE_ERRMSG_SIZE];
LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
LEX_MASTER_INFO* lex_mi = &thd->lex->mi;
errmsg[0]=0; // Safety
if (translate_master(thd, lex_mi, errmsg))

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);
@ -300,7 +301,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,
@ -1213,7 +1214,7 @@ byte *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type,
bool sys_var_thd_bit::update(THD *thd, set_var *var)
{
int res= (*update_func)(thd, var);
thd->lex.select_lex.options=thd->options;
thd->lex->select_lex.options=thd->options;
return res;
}
@ -1730,6 +1731,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 |
@ -1738,6 +1763,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

@ -278,3 +278,19 @@ v/*
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -272,3 +272,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -280,3 +280,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -274,3 +274,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -274,3 +274,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -278,3 +278,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -271,3 +271,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -271,3 +271,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -271,3 +271,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -271,3 +271,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -273,3 +273,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -273,3 +273,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -271,3 +271,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -264,3 +264,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -277,3 +277,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -270,3 +270,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -269,3 +269,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -274,3 +274,19 @@
"Illegal mix of collations for operation '%s'",
"Variable '%-.64s' is not a variable component (Can't be used as XXXX.variable_name)",
"Unknown collation: '%-.64s'",
"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."
"Query execution was interrupted"
"Wrong number of arguments for %s %s, expected %u, got %u"

View File

@ -563,7 +563,7 @@ int start_slave_thread(pthread_handler h_func, pthread_mutex_t *start_lock,
if (thd->killed)
{
pthread_mutex_unlock(cond_lock);
DBUG_RETURN(ER_SERVER_SHUTDOWN);
DBUG_RETURN(thd->killed_errno());
}
}
}
@ -2096,7 +2096,7 @@ err:
pthread_mutex_unlock(&data_lock);
DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
improper_arguments: %d timed_out: %d",
(int) thd->killed,
thd->killed_errno(),
(int) (init_abort_pos_wait != abort_pos_wait),
(int) slave_running,
(int) (error == -2),

450
sql/sp.cc Normal file
View File

@ -0,0 +1,450 @@
/* 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"
#include "sp_cache.h"
/*
*
* DB storage of Stored PROCEDUREs and FUNCTIONs
*
*/
// *opened=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);
TABLE *table;
const char *defstr;
int ret;
bool opened;
const char *creator;
longlong created;
longlong modified;
bool suid= 1;
char *ptr;
uint length;
char buff[65];
String str(buff, sizeof(buff), &my_charset_bin);
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;
}
// Get additional information
if ((creator= get_field(&thd->mem_root, table->field[3])) == NULL)
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
modified= table->field[4]->val_int();
created= table->field[5]->val_int();
if ((ptr= get_field(&thd->mem_root, table->field[6])) == NULL)
{
ret= SP_GET_FIELD_FAILED;
goto done;
}
if (ptr[0] == 'N')
suid= 0;
table->field[7]->val_str(&str, &str);
ptr= 0;
if ((length= str.length()))
ptr= strmake_root(&thd->mem_root, str.ptr(), length);
if (opened)
{
close_thread_tables(thd, 0, 1);
table= NULL;
}
{
LEX *oldlex= thd->lex;
enum enum_sql_command oldcmd= thd->lex->sql_command;
lex_start(thd, (uchar*)defstr, strlen(defstr));
if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
{
LEX *newlex= thd->lex;
sp_head *sp= newlex->sphead;
if (sp)
{
if (oldlex != newlex)
sp->restore_lex(thd);
delete sp;
newlex->sphead= NULL;
}
ret= SP_PARSE_ERROR;
}
else
{
*sphp= thd->lex->sphead;
(*sphp)->sp_set_info((char *) creator, (uint) strlen(creator),
created, modified, suid,
ptr, length);
}
thd->lex->sql_command= oldcmd;
}
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,
char *comment, uint commentlen, bool suid)
{
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;
char creator[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
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, default_values); // Get default values for fields
strxmov(creator, thd->user, "@", thd->host_or_ip, NullS);
table->field[0]->store(name, namelen, system_charset_info);
table->field[1]->store((longlong)type);
table->field[2]->store(def, deflen, system_charset_info);
table->field[3]->store(creator, (uint)strlen(creator), system_charset_info);
((Field_timestamp *)table->field[5])->set_time();
if (suid)
table->field[6]->store((longlong)suid);
if (comment)
table->field[7]->store(comment, commentlen, 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));
sp= thd->sp_proc_cache->lookup(name->str, name->length);
if (! sp)
{
if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
name->str, name->length, &sp) == SP_OK)
{
thd->sp_proc_cache->insert(sp);
}
}
DBUG_RETURN(sp);
}
int
sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen,
char *comment, uint commentlen, bool suid)
{
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,
comment, commentlen, suid);
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));
sp_head *sp;
int ret;
sp= thd->sp_proc_cache->lookup(name, namelen);
if (sp)
{
thd->sp_proc_cache->remove(sp);
delete sp;
}
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= thd->sp_func_cache->lookup(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,
char *comment, uint commentlen, bool suid)
{
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,
comment, commentlen, suid);
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));
sp_head *sp;
int ret;
sp= thd->sp_func_cache->lookup(name, namelen);
if (sp)
{
thd->sp_func_cache->remove(sp);
delete sp;
}
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= FALSE;
if (thd->sp_func_cache->lookup(name->str, name->length) ||
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, 0, 1);
return ret;
}
byte *
sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
{
LEX_STRING *lsp= (LEX_STRING *)ptr;
*plen= lsp->length;
return (byte *)lsp->str;
}
void
sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
{
if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length))
{
LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
ls->str= sql_strmake(fun.str, fun.length);
ls->length= fun.length;
hash_insert(&lex->spfuns, (byte *)ls);
}
}
void
sp_merge_funs(LEX *dst, LEX *src)
{
for (uint i=0 ; i < src->spfuns.records ; i++)
{
LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);
if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
hash_insert(&dst->spfuns, (byte *)ls);
}
}
int
sp_cache_functions(THD *thd, LEX *lex)
{
HASH *h= &lex->spfuns;
int ret= 0;
for (uint i=0 ; i < h->records ; i++)
{
LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
if (! thd->sp_func_cache->lookup(ls->str, ls->length))
{
sp_head *sp;
LEX *oldlex= thd->lex;
LEX *newlex= new st_lex;
thd->lex= newlex;
if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp)
== SP_OK)
{
ret= sp_cache_functions(thd, newlex);
delete newlex;
thd->lex= oldlex;
if (ret)
break;
thd->sp_func_cache->insert(sp);
}
else
{
delete newlex;
thd->lex= oldlex;
net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
ret= 1;
break;
}
}
}
return ret;
}

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,
char *comment, uint commentlen, bool suid);
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,
char *comment, uint commentlen, bool suid);
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);
// 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);
#endif /* _SP_H_ */

53
sql/sp_cache.cc Normal file
View File

@ -0,0 +1,53 @@
/* 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_cache.h"
#include "sp_head.h"
static byte *
hash_get_key_for_sp_head(const byte *ptr, uint *plen,
my_bool first)
{
return ((sp_head*)ptr)->name(plen);
}
sp_cache::sp_cache()
{
init();
}
sp_cache::~sp_cache()
{
hash_free(&m_hashtable);
}
void
sp_cache::init()
{
hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
hash_get_key_for_sp_head, 0, 0);
}
void
sp_cache::cleanup()
{
hash_free(&m_hashtable);
}

65
sql/sp_cache.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_CACHE_H_
#define _SP_CACHE_H_
#ifdef __GNUC__
#pragma interface /* gcc class implementation */
#endif
class sp_head;
class sp_cache
{
public:
sp_cache();
~sp_cache();
void
init();
void
cleanup();
inline void
insert(sp_head *sp)
{
hash_insert(&m_hashtable, (const byte *)sp);
}
inline sp_head *
lookup(char *name, uint namelen)
{
return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
}
inline void
remove(sp_head *sp)
{
hash_delete(&m_hashtable, (byte *)sp);
}
private:
HASH m_hashtable;
}; // class sp_cache
#endif /* _SP_CACHE_H_ */

645
sql/sp_head.cc Normal file
View File

@ -0,0 +1,645 @@
/* 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, &it))
{
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(thd->strmake(s->c_ptr_quick(), s->length()),
s->length(), it->charset());
break;
}
}
}
DBUG_RETURN(it);
}
void *
sp_head::operator new(size_t size)
{
DBUG_ENTER("sp_head::operator new");
MEM_ROOT own_root;
sp_head *sp;
bzero((char *)&own_root, sizeof(own_root));
init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
sp= (sp_head *)alloc_root(&own_root, size);
sp->m_mem_root= own_root;
DBUG_RETURN(sp);
}
void
sp_head::operator delete(void *ptr, size_t size)
{
DBUG_ENTER("sp_head::operator delete");
MEM_ROOT own_root;
sp_head *sp= (sp_head *)ptr;
memcpy(&own_root, (const void *)&sp->m_mem_root, sizeof(MEM_ROOT));
free_root(&own_root, MYF(0));
DBUG_VOID_RETURN;
}
sp_head::sp_head()
: Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE), m_free_list(NULL)
{
DBUG_ENTER("sp_head::sp_head");
m_backpatch.empty();
m_lex.empty();
DBUG_VOID_RETURN;
}
void
sp_head::init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
{
DBUG_ENTER("sp_head::init");
const char *dstr = (const char*)lex->buf;
DBUG_PRINT("info", ("name: %*s", name->length, name->str));
m_name.length= name->length;
m_name.str= lex->thd->strmake(name->str, name->length);
m_defstr.length= lex->end_of_query - lex->buf;
m_defstr.str= lex->thd->strmake(dstr, m_defstr.length);
m_comment.length= 0;
m_comment.str= 0;
if (comment)
{
m_comment.length= comment->length;
m_comment.str= comment->str;
}
m_suid= suid;
lex->spcont= m_pcont= new sp_pcontext();
my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
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,
m_comment.str, m_comment.length,
m_suid);
else
ret= sp_create_procedure(thd,
m_name.str, m_name.length,
m_defstr.str, m_defstr.length,
m_comment.str, m_comment.length,
m_suid);
DBUG_RETURN(ret);
}
sp_head::~sp_head()
{
destroy();
if (m_thd)
restore_thd_mem_root(m_thd);
}
void
sp_head::destroy()
{
DBUG_ENTER("sp_head::destroy");
DBUG_PRINT("info", ("name: %s", m_name.str));
sp_instr *i;
LEX *lex;
for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
delete i;
delete_dynamic(&m_instr);
m_pcont->destroy();
free_items(m_free_list);
while ((lex= (LEX *)m_lex.pop()))
{
if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left
delete lex;
}
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;
if (argcount != params)
{
// Need to use my_printf_error here, or it will not terminate the
// invoking query properly.
my_printf_error(ER_SP_WRONG_NO_OF_ARGS, ER(ER_SP_WRONG_NO_OF_ARGS), MYF(0),
"FUNCTION", m_name.str, params, argcount);
DBUG_RETURN(-1);
}
// QQ Should have some error checking here? (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));
}
// Close tables opened for subselect in argument list
close_thread_tables(thd);
// 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 (args->elements != params)
{
net_printf(thd, ER_SP_WRONG_NO_OF_ARGS, "PROCEDURE", m_name.str,
params, args->elements);
DBUG_RETURN(-1);
}
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: 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());
}
}
// Close tables opened for subselect in argument list
close_thread_tables(thd);
// 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 NOT_USED_NOW
// 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)
{
DBUG_ENTER("sp_head::reset_lex");
LEX *sublex;
LEX *oldlex= thd->lex;
(void)m_lex.push_front(oldlex);
thd->lex= sublex= new st_lex;
sublex->yylineno= oldlex->yylineno;
/* Reset most stuff. The length arguments doesn't matter here. */
lex_start(thd, oldlex->buf, oldlex->end_of_query - oldlex->ptr);
/* We must reset ptr and end_of_query again */
sublex->ptr= oldlex->ptr;
sublex->end_of_query= oldlex->end_of_query;
sublex->tok_start= oldlex->tok_start;
/* And keep the SP stuff too */
sublex->sphead= oldlex->sphead;
sublex->spcont= oldlex->spcont;
mysql_init_query(thd, true); // Only init lex
sublex->sp_lex_in_use= FALSE;
DBUG_VOID_RETURN;
}
// Restore lex during parsing, after we have parsed a sub statement.
void
sp_head::restore_lex(THD *thd)
{
DBUG_ENTER("sp_head::restore_lex");
LEX *sublex= thd->lex;
LEX *oldlex= (LEX *)m_lex.pop();
if (! oldlex)
return; // Nothing to restore
// Update some state in the old one first
oldlex->ptr= sublex->ptr;
oldlex->next_state= sublex->next_state;
// Collect some data from the sub statement lex.
sp_merge_funs(oldlex, sublex);
#ifdef NOT_USED_NOW
// QQ We're not using this at the moment.
if (sublex.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= sublex.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= sublex.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
if (! sublex->sp_lex_in_use)
delete sublex;
thd->lex= oldlex;
DBUG_VOID_RETURN;
}
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
//
sp_instr_stmt::~sp_instr_stmt()
{
if (m_lex)
delete m_lex;
}
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;
olex= thd->lex; // Save the other lex
thd->lex= m_lex; // Use my own lex
thd->lex->thd = thd; // QQ Not reentrant!
thd->lex->unit.thd= thd; // QQ Not reentrant
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 */
}
thd->lex= olex; // 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);
}

411
sql/sp_head.h Normal file
View File

@ -0,0 +1,411 @@
/* -*- 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
my_bool m_multi_query; // TRUE if a procedure with SELECT(s)
uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
#if NOT_USED_NOW
// QQ We're not using this at the moment.
List<char *> m_calls; // Called procedures.
List<char *> m_tables; // Used tables.
#endif
static void *
operator new(size_t size);
static void
operator delete(void *ptr, size_t size);
sp_head();
// Initialize after we have reset mem_root
void
init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
int
create(THD *thd);
virtual ~sp_head();
// 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);
}
void sp_set_info(char *creator, uint creatorlen,
longlong created, longlong modified,
bool suid, char *comment, uint commentlen)
{
m_creator= creator;
m_creatorlen= creatorlen;
m_created= created;
m_modified= modified;
m_comment.length= commentlen;
m_comment.str= comment;
m_suid= suid;
}
inline void reset_thd_mem_root(THD *thd)
{
m_thd_root= thd->mem_root;
thd->mem_root= m_mem_root;
m_free_list= thd->free_list; // Keep the old list
thd->free_list= NULL; // Start a new one
m_thd= thd;
}
inline void restore_thd_mem_root(THD *thd)
{
Item *flist= m_free_list; // The old list
m_free_list= thd->free_list; // Get the new one
thd->free_list= flist; // Restore the old one
m_mem_root= thd->mem_root;
thd->mem_root= m_thd_root;
m_thd= NULL;
}
private:
MEM_ROOT m_mem_root; // My own mem_root
MEM_ROOT m_thd_root; // Temp. store for thd's mem_root
Item *m_free_list; // Where the items go
THD *m_thd; // Set if we have reset mem_root
LEX_STRING m_name;
LEX_STRING m_defstr;
LEX_STRING m_comment;
char *m_creator;
uint m_creatorlen;
longlong m_created;
longlong m_modified;
bool m_suid;
sp_pcontext *m_pcont; // Parse context
List<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), m_lex(NULL)
{}
virtual ~sp_instr_stmt();
virtual int execute(THD *thd, uint *nextp);
inline void
set_lex(LEX *lex)
{
m_lex= 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_ */

115
sql/sp_pcontext.cc Normal file
View File

@ -0,0 +1,115 @@
/* 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);
if (my_strnncoll(system_charset_info,
(const uchar *)name->str, name->length,
(const uchar *)p->name.str, p->name.length) == 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;
}

Some files were not shown because too many files have changed in this diff Show More