summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore3
-rw-r--r--BitKeeper/etc/logging_ok2
-rwxr-xr-xBitKeeper/triggers/post-commit19
-rw-r--r--Docs/sp-imp-spec.txt622
-rw-r--r--Docs/sp-implemented.txt99
-rw-r--r--client/mysql.cc33
-rw-r--r--client/mysqltest.c27
-rw-r--r--configure.in2
-rw-r--r--include/mysql_com.h2
-rw-r--r--include/mysqld_error.h19
-rw-r--r--libmysql/libmysql.c5
-rw-r--r--libmysqld/Makefile.am3
-rw-r--r--myisam/mi_check.c2
-rw-r--r--myisam/myisamchk.c4
-rw-r--r--myisam/myisamdef.h2
-rw-r--r--myisam/sort.c3
-rw-r--r--mysql-test/install_test_db.sh15
-rw-r--r--mysql-test/r/connect.result3
-rw-r--r--mysql-test/r/insert.result13
-rw-r--r--mysql-test/r/show_check.result1
-rw-r--r--mysql-test/r/sp-error.result92
-rw-r--r--mysql-test/r/sp.result538
-rw-r--r--mysql-test/r/status.result2
-rw-r--r--mysql-test/r/subselect.result3
-rw-r--r--mysql-test/r/variables.result2
-rw-r--r--mysql-test/t/insert.test31
-rw-r--r--mysql-test/t/sp-error.test135
-rw-r--r--mysql-test/t/sp.test622
-rw-r--r--mysql-test/t/subselect.test5
-rw-r--r--scripts/mysql_install_db.sh14
-rw-r--r--sql/Makefile.am6
-rw-r--r--sql/filesort.cc8
-rw-r--r--sql/ha_berkeley.cc2
-rw-r--r--sql/ha_myisam.cc4
-rw-r--r--sql/item.cc18
-rw-r--r--sql/item.h53
-rw-r--r--sql/item_func.cc74
-rw-r--r--sql/item_func.h66
-rw-r--r--sql/lex.h23
-rw-r--r--sql/lock.cc2
-rw-r--r--sql/log.cc13
-rw-r--r--sql/log_event.cc2
-rw-r--r--sql/mysql_priv.h4
-rw-r--r--sql/mysqld.cc71
-rw-r--r--sql/protocol.cc2
-rw-r--r--sql/records.cc2
-rw-r--r--sql/set_var.cc28
-rw-r--r--sql/share/czech/errmsg.txt16
-rw-r--r--sql/share/danish/errmsg.txt18
-rw-r--r--sql/share/dutch/errmsg.txt18
-rw-r--r--sql/share/english/errmsg.txt18
-rw-r--r--sql/share/estonian/errmsg.txt18
-rw-r--r--sql/share/french/errmsg.txt18
-rw-r--r--sql/share/german/errmsg.txt18
-rw-r--r--sql/share/greek/errmsg.txt18
-rw-r--r--sql/share/hungarian/errmsg.txt18
-rw-r--r--sql/share/italian/errmsg.txt18
-rw-r--r--sql/share/japanese/errmsg.txt18
-rw-r--r--sql/share/korean/errmsg.txt18
-rw-r--r--sql/share/norwegian-ny/errmsg.txt18
-rw-r--r--sql/share/norwegian/errmsg.txt18
-rw-r--r--sql/share/polish/errmsg.txt18
-rw-r--r--sql/share/portuguese/errmsg.txt18
-rw-r--r--sql/share/romanian/errmsg.txt16
-rw-r--r--sql/share/russian/errmsg.txt18
-rw-r--r--sql/share/serbian/errmsg.txt18
-rw-r--r--sql/share/slovak/errmsg.txt18
-rw-r--r--sql/share/spanish/errmsg.txt18
-rw-r--r--sql/share/swedish/errmsg.txt18
-rw-r--r--sql/share/ukrainian/errmsg.txt18
-rw-r--r--sql/slave.cc4
-rw-r--r--sql/sp.cc405
-rw-r--r--sql/sp.h65
-rw-r--r--sql/sp_head.cc571
-rw-r--r--sql/sp_head.h356
-rw-r--r--sql/sp_pcontext.cc115
-rw-r--r--sql/sp_pcontext.h162
-rw-r--r--sql/sp_rcontext.h95
-rw-r--r--sql/sql_acl.cc1
-rw-r--r--sql/sql_base.cc2
-rw-r--r--sql/sql_cache.cc2
-rw-r--r--sql/sql_class.cc78
-rw-r--r--sql/sql_class.h31
-rw-r--r--sql/sql_db.cc3
-rw-r--r--sql/sql_delete.cc5
-rw-r--r--sql/sql_derived.cc8
-rw-r--r--sql/sql_insert.cc52
-rw-r--r--sql/sql_lex.cc17
-rw-r--r--sql/sql_lex.h10
-rw-r--r--sql/sql_load.cc7
-rw-r--r--sql/sql_parse.cc313
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_rename.cc1
-rw-r--r--sql/sql_repl.cc2
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc21
-rw-r--r--sql/sql_show.cc2
-rw-r--r--sql/sql_table.cc33
-rw-r--r--sql/sql_update.cc4
-rw-r--r--sql/sql_yacc.yy732
100 files changed, 5925 insertions, 287 deletions
diff --git a/.bzrignore b/.bzrignore
index 82cde49001c..b02c074ca9e 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -366,6 +366,9 @@ libmysqld/repl_failsafe.cc
libmysqld/set_var.cc
libmysqld/simple-test
libmysqld/slave.cc
+libmysqld/sp.cc
+libmysqld/sp_head.cc
+libmysqld/sp_pcontext.cc
libmysqld/spatial.cc
libmysqld/sql_acl.cc
libmysqld/sql_analyse.cc
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 085c67a176c..131ce111d1a 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -73,9 +73,11 @@ papa@gbichot.local
paul@central.snake.net
paul@teton.kitebird.com
pem@mysql.com
+pem@per-erik-martins-dator.local
peter@linux.local
peter@mysql.com
pgulutzan@linux.local
+pmartin@build.mysql2.com
ram@gw.udmsearch.izhnet.ru
ram@mysql.r18.ru
ram@ram.(none)
diff --git a/BitKeeper/triggers/post-commit b/BitKeeper/triggers/post-commit
index a2a0ecb2701..83c6195154f 100755
--- a/BitKeeper/triggers/post-commit
+++ b/BitKeeper/triggers/post-commit
@@ -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
diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt
new file mode 100644
index 00000000000..385625464f1
--- /dev/null
+++ b/Docs/sp-imp-spec.txt
@@ -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);
+
+--
diff --git a/Docs/sp-implemented.txt b/Docs/sp-implemented.txt
new file mode 100644
index 00000000000..41e7c4b2923
--- /dev/null
+++ b/Docs/sp-implemented.txt
@@ -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.
diff --git a/client/mysql.cc b/client/mysql.cc
index 2623660033b..9c41f4afc30 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -1568,7 +1568,7 @@ com_help(String *buffer __attribute__((unused)),
for (i = 0; commands[i].name; i++)
{
if (commands[i].func)
- tee_fprintf(stdout, "%s\t(\\%c)\t%s\n", commands[i].name,
+ tee_fprintf(stdout, "%-10s(\\%c)\t%s\n", commands[i].name,
commands[i].cmd_char, commands[i].doc);
}
}
@@ -2380,6 +2380,37 @@ static int com_source(String *buffer, char *line)
return error;
}
+ /* ARGSUSED */
+static int
+com_delimiter(String *buffer __attribute__((unused)), char *line)
+{
+ char *tmp;
+ char buff[256];
+
+ if (strlen(line)> 255)
+ {
+ put_info("'DELIMITER' command was too long.", INFO_ERROR);
+ return 0;
+ }
+ bzero(buff, sizeof(buff));
+ strmov(buff, line);
+ tmp= get_arg(buff, 0);
+
+ if (!tmp || !*tmp)
+ {
+ put_info("DELIMITER must be followed by a 'delimiter' char", INFO_ERROR);
+ return 0;
+ }
+
+ if (strlen(tmp)> 1)
+ {
+ put_info("Argument must be one char", INFO_ERROR);
+ return 0;
+ }
+
+ delimiter= *tmp;
+ return 0;
+}
/* ARGSUSED */
static int
diff --git a/client/mysqltest.c b/client/mysqltest.c
index fb2104f43f4..b8c81de66ec 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -89,6 +89,7 @@
#define SLAVE_POLL_INTERVAL 300000 /* 0.3 of a sec */
+#define DEFAULT_DELIMITER ';'
enum {OPT_MANAGER_USER=256,OPT_MANAGER_HOST,OPT_MANAGER_PASSWD,
OPT_MANAGER_PORT,OPT_MANAGER_WAIT_TIMEOUT, OPT_SKIP_SAFEMALLOC,
@@ -126,6 +127,8 @@ static int block_ok_stack[BLOCK_STACK_DEPTH];
static uint global_expected_errno[MAX_EXPECTED_ERRORS], global_expected_errors;
static CHARSET_INFO *charset_info= &my_charset_latin1;
+static char delimiter= DEFAULT_DELIMITER;
+
DYNAMIC_ARRAY q_lines;
#include "sslopt-vars.h"
@@ -202,7 +205,7 @@ Q_SERVER_START, Q_SERVER_STOP,Q_REQUIRE_MANAGER,
Q_WAIT_FOR_SLAVE_TO_STOP,
Q_REQUIRE_VERSION,
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
-Q_ENABLE_INFO, Q_DISABLE_INFO,
+Q_ENABLE_INFO, Q_DISABLE_INFO, Q_DELIMITER,
Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
Q_COMMENT_WITH_COMMAND
@@ -265,6 +268,7 @@ const char *command_names[]=
"disable_warnings",
"enable_info",
"disable_info",
+ "delimiter",
0
};
@@ -1532,6 +1536,16 @@ int do_while(struct st_query* q)
return 0;
}
+int do_delimiter(char *p)
+{
+ while (*p && my_isspace(charset_info,*p))
+ p++;
+ if (!*p)
+ die("Missing delimiter character\n");
+ delimiter=*p;
+
+ return 0;
+}
int safe_copy_unescape(char* dest, char* src, int size)
{
@@ -1611,7 +1625,7 @@ int read_line(char* buf, int size)
switch(state) {
case R_NORMAL:
/* Only accept '{' in the beginning of a line */
- if (c == ';')
+ if (c == delimiter)
{
*p = 0;
return 0;
@@ -1651,7 +1665,7 @@ int read_line(char* buf, int size)
*buf = 0;
return 0;
}
- else if (c == ';' || c == '{')
+ else if (c == delimiter || c == '{')
{
*p = 0;
return 0;
@@ -1671,7 +1685,7 @@ int read_line(char* buf, int size)
state = R_ESC_SLASH_Q1;
break;
case R_ESC_Q_Q1:
- if (c == ';')
+ if (c == delimiter)
{
*p = 0;
return 0;
@@ -1692,7 +1706,7 @@ int read_line(char* buf, int size)
state = R_ESC_SLASH_Q2;
break;
case R_ESC_Q_Q2:
- if (c == ';')
+ if (c == delimiter)
{
*p = 0;
return 0;
@@ -2545,6 +2559,9 @@ int main(int argc, char** argv)
do_sync_with_master2("");
break;
}
+ case Q_DELIMITER:
+ do_delimiter(q->first_argument);
+ break;
case Q_COMMENT: /* Ignore row */
case Q_COMMENT_WITH_COMMAND:
break;
diff --git a/configure.in b/configure.in
index 77ae969b5eb..4603e1fc9c4 100644
--- a/configure.in
+++ b/configure.in
@@ -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
diff --git a/include/mysql_com.h b/include/mysql_com.h
index bbfb869927b..0cc5c1f65a9 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -118,6 +118,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;
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index f42ecc5015a..623e665703e 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -275,5 +275,20 @@
#define ER_ZLIB_Z_BUF_ERROR 1256
#define ER_ZLIB_Z_DATA_ERROR 1257
#define ER_CUT_VALUE_GROUP_CONCAT 1258
-#define ER_ERROR_MESSAGES 259
-
+#define ER_SP_NO_RECURSIVE_CREATE 1259
+#define ER_SP_ALREADY_EXISTS 1260
+#define ER_SP_DOES_NOT_EXIST 1261
+#define ER_SP_DROP_FAILED 1262
+#define ER_SP_STORE_FAILED 1263
+#define ER_SP_LILABEL_MISMATCH 1264
+#define ER_SP_LABEL_REDEFINE 1265
+#define ER_SP_LABEL_MISMATCH 1266
+#define ER_SP_UNINIT_VAR 1267
+#define ER_SP_BADSELECT 1268
+#define ER_SP_BADRETURN 1269
+#define ER_SP_BADQUERY 1270
+#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1271
+#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1272
+#define ER_QUERY_INTERRUPTED 1273
+#define ER_SP_WRONG_NO_OF_ARGS 1274
+#define ER_ERROR_MESSAGES 275
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 2f64bb3bd44..e59856392f4 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -1305,6 +1305,7 @@ static MYSQL_DATA *read_rows(MYSQL *mysql,MYSQL_FIELD *mysql_fields,
if (pkt_len > 1) /* MySQL 4.1 protocol */
{
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));
@@ -1331,7 +1332,10 @@ read_one_row(MYSQL *mysql,uint fields,MYSQL_ROW row, ulong *lengths)
if (pkt_len <= 8 && mysql->net.read_pos[0] == 254)
{
if (pkt_len > 1) /* MySQL 4.1 protocol */
+ {
mysql->warning_count= uint2korr(mysql->net.read_pos+1);
+ mysql->server_status= uint2korr(mysql->net.read_pos+3);
+ }
return 1; /* End of data */
}
prev_pos= 0; /* allowed to write at packet[-1] */
@@ -5306,6 +5310,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));
diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am
index b36f8d92490..13e389b4dca 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -55,7 +55,8 @@ sqlsources = convert.cc derror.cc field.cc field_conv.cc filesort.cc \
sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \
sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
- spatial.cc gstream.cc sql_help.cc
+ spatial.cc gstream.cc sql_help.cc \
+ sp_head.cc sp_pcontext.cc sp.cc
EXTRA_DIST = lib_vio.c
diff --git a/myisam/mi_check.c b/myisam/mi_check.c
index 73bdcea9cc3..62362a91d33 100644
--- a/myisam/mi_check.c
+++ b/myisam/mi_check.c
@@ -2589,7 +2589,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) {
diff --git a/myisam/myisamchk.c b/myisam/myisamchk.c
index 104b344a9e2..b0eade99a3d 100644
--- a/myisam/myisamchk.c
+++ b/myisam/myisamchk.c
@@ -1644,9 +1644,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 */
diff --git a/myisam/myisamdef.h b/myisam/myisamdef.h
index 25f2969a973..6886554d94e 100644
--- a/myisam/myisamdef.h
+++ b/myisam/myisamdef.h
@@ -693,7 +693,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,...));
diff --git a/myisam/sort.c b/myisam/sort.c
index 09e487e1165..df739bcf091 100644
--- a/myisam/sort.c
+++ b/myisam/sort.c
@@ -846,7 +846,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;
diff --git a/mysql-test/install_test_db.sh b/mysql-test/install_test_db.sh
index fc3e00d8501..ed3b0fd683e 100644
--- a/mysql-test/install_test_db.sh
+++ b/mysql-test/install_test_db.sh
@@ -69,6 +69,7 @@ c_t="" c_c=""
c_hl="" c_hl=""
c_hc="" c_hc=""
c_clr="" c_clr=""
+c_p=""
# Check for old tables
if test ! -f $mdata/db.frm
@@ -246,6 +247,17 @@ then
c_hc="$c_hc comment='categories of help topics';"
fi
+if test ! -f $mdata/proc.frm
+then
+ c_p="$c_p CREATE TABLE proc ("
+ c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p type enum('function','procedure') NOT NULL,"
+ c_p="$c_p body blob DEFAULT '' NOT NULL,"
+ c_p="$c_p PRIMARY KEY (name,type)"
+ c_p="$c_p )"
+ c_p="$c_p comment='Stored Procedures';"
+fi
+
mysqld_boot=" $execdir/mysqld --no-defaults --bootstrap --skip-grant-tables \
--basedir=$basedir --datadir=$ldata --skip-innodb --skip-bdb $EXTRA_ARG"
echo "running $mysqld_boot"
@@ -270,6 +282,9 @@ $c_c
$c_hl
$c_hc
$c_clr
+
+$c_p
+
END_OF_DATA
then
exit 0
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index b7243ac5d0b..8c2e2d1def7 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -7,6 +7,7 @@ help_category
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
@@ -22,6 +23,7 @@ help_category
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
@@ -37,6 +39,7 @@ help_category
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result
index ebd34dd7668..3be04584749 100644
--- a/mysql-test/r/insert.result
+++ b/mysql-test/r/insert.result
@@ -64,3 +64,16 @@ use test_$1;
create table t1 (c int);
insert into test_$1.t1 set test_$1.t1.c = '1';
drop database test_$1;
+use test;
+drop table if exists t1,t2,t3;
+create table t1(id1 int not null auto_increment primary key, t char(12));
+create table t2(id2 int not null, t char(12));
+create table t3(id3 int not null, t char(12), index(id3));
+select count(*) from t2;
+count(*)
+500
+insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
+select count(*) from t2;
+count(*)
+25500
+drop table if exists t1,t2,t3;
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index b1072fd41ce..a644c567e93 100644
--- a/mysql-test/r/show_check.result
+++ b/mysql-test/r/show_check.result
@@ -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;
diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result
new file mode 100644
index 00000000000..99bdfc25ba8
--- /dev/null
+++ b/mysql-test/r/sp-error.result
@@ -0,0 +1,92 @@
+delete from mysql.proc;
+create procedure proc1()
+set @x = 42;
+create function func1() returns int
+return 42;
+create procedure foo()
+create procedure bar() set @x=3;
+Can't create a PROCEDURE from within another stored routine
+create procedure foo()
+create function bar() returns double return 2.3;
+Can't create a FUNCTION from within another stored routine
+create procedure proc1()
+set @x = 42;
+PROCEDURE proc1 already exists
+create function func1() returns int
+return 42;
+FUNCTION func1 already exists
+drop procedure proc1;
+drop function func1;
+alter procedure foo;
+PROCEDURE foo does not exist
+alter function foo;
+FUNCTION foo does not exist
+drop procedure foo;
+PROCEDURE foo does not exist
+drop function foo;
+FUNCTION foo does not exist
+call foo();
+PROCEDURE foo does not exist
+drop procedure if exists foo;
+Warnings:
+Warning 1261 PROCEDURE foo does not exist
+create procedure foo()
+foo: loop
+leave bar;
+end loop;
+LEAVE with no matching label: bar
+create procedure foo()
+foo: loop
+iterate bar;
+end loop;
+ITERATE with no matching label: bar
+create procedure foo()
+foo: loop
+foo: loop
+set @x=2;
+end loop foo;
+end loop foo;
+Redefining label foo
+create procedure foo()
+foo: loop
+set @x=2;
+end loop bar;
+End-label bar without match
+create procedure foo(out x int)
+begin
+declare y int;
+set x = y;
+end;
+Referring to uninitialized variable y
+create procedure foo()
+begin
+select name from mysql.proc;
+select type from mysql.proc;
+end;
+call foo();
+SELECT in a stored procedure must have INTO
+drop procedure foo;
+create procedure foo()
+return 42;
+RETURN is only allowed in a FUNCTION
+create function foo() returns int
+begin
+declare x int;
+select max(c) into x from test.t;
+return x;
+end;
+Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION
+create procedure p(x int)
+insert into test.t1 values (x);
+create function f(x int) returns int
+return x+42;
+call p();
+Wrong number of arguments for PROCEDURE p, expected 1, got 0
+call p(1, 2);
+Wrong number of arguments for PROCEDURE p, expected 1, got 2
+select f();
+Wrong number of arguments for FUNCTION f, expected 1, got 0
+select f(1, 2);
+Wrong number of arguments for FUNCTION f, expected 1, got 2
+drop procedure p;
+drop function f;
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
new file mode 100644
index 00000000000..29200a43975
--- /dev/null
+++ b/mysql-test/r/sp.result
@@ -0,0 +1,538 @@
+use test;
+drop table if exists t1;
+drop table if exists t2;
+create table t1 (
+id char(16) not null,
+data int not null
+);
+create table t2 (
+s char(16) not null,
+i int not null,
+d double not null
+);
+create procedure foo42()
+insert into test.t1 values ("foo", 42);
+call foo42();
+select * from t1;
+id data
+foo 42
+delete from t1;
+drop procedure foo42;
+create procedure u()
+use sptmp;
+drop database if exists sptmp;
+create database sptmp;
+use test;
+call u();
+select database();
+database()
+test
+drop database sptmp;
+drop procedure u;
+create procedure bar(x char(16), y int)
+insert into test.t1 values (x, y);
+call bar("bar", 666);
+select * from t1;
+id data
+bar 666
+delete from t1;
+create procedure two(x1 char(16), x2 char(16), y int)
+begin
+insert into test.t1 values (x1, y);
+insert into test.t1 values (x2, y);
+end;
+call two("one", "two", 3);
+select * from t1;
+id data
+one 3
+two 3
+delete from t1;
+drop procedure two;
+create procedure locset(x char(16), y int)
+begin
+declare z1, z2 int;
+set z1 = y;
+set z2 = z1+2;
+insert into test.t1 values (x, z2);
+end;
+call locset("locset", 19);
+select * from t1;
+id data
+locset 21
+delete from t1;
+drop procedure locset;
+create procedure mixset(x char(16), y int)
+begin
+declare z int;
+set @z = y, z = 666, max_join_size = 100;
+insert into test.t1 values (x, z);
+end;
+call mixset("mixset", 19);
+show variables like 'max_join_size';
+Variable_name Value
+max_join_size 100
+select id,data,@z from t1;
+id data @z
+mixset 666 19
+delete from t1;
+drop procedure mixset;
+create procedure zip(x char(16), y int)
+begin
+declare z int;
+call zap(y, z);
+call bar(x, z);
+end;
+create procedure zap(x int, out y int)
+begin
+declare z int;
+set z = x+1, y = z;
+end;
+call zip("zip", 99);
+select * from t1;
+id data
+zip 100
+delete from t1;
+drop procedure zip;
+drop procedure zap;
+drop procedure bar;
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+call inc2(x2, y);
+insert into test.t1 values (x1, y);
+end;
+create procedure inc2(x char(16), y int)
+begin
+call inc(y);
+insert into test.t1 values (x, y);
+end;
+create procedure inc(inout io int)
+set io = io + 1;
+call iotest("io1", "io2", 1);
+select * from t1;
+id data
+io2 2
+io1 1
+delete from t1;
+drop procedure iotest;
+drop procedure inc2;
+drop procedure inc;
+create procedure cbv1()
+begin
+declare y int default 3;
+call cbv2(y+1, y);
+insert into test.t1 values ("cbv1", y);
+end;
+create procedure cbv2(y1 int, inout y2 int)
+begin
+set y2 = 4711;
+insert into test.t1 values ("cbv2", y1);
+end;
+call cbv1();
+select * from t1;
+id data
+cbv2 4
+cbv1 4711
+delete from t1;
+drop procedure cbv1;
+drop procedure cbv2;
+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;
diff --git a/mysql-test/r/status.result b/mysql-test/r/status.result
index 3ef6cec32b3..f93147d00c5 100644
--- a/mysql-test/r/status.result
+++ b/mysql-test/r/status.result
@@ -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;
diff --git a/mysql-test/r/subselect.result b/mysql-test/r/subselect.result
index 564e102c874..d8e94d6dbbe 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -601,7 +601,6 @@ x
3
3
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
-You can't specify target table 't1' for update in FROM clause
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
select * from t1;
x
@@ -609,6 +608,8 @@ x
2
3
3
+11
+11
0
drop table t1, t2, t3;
CREATE TABLE t1 (x int not null, y int, primary key (x));
diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index 8d45c5cf5e9..8dff27bdd6f 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -285,6 +285,8 @@ set sql_buffer_result=1;
set sql_log_bin=1;
set sql_log_off=1;
set sql_log_update=1;
+Warnings:
+Note 1271 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;
diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test
index bfa8aac7a1f..34302cdbc60 100644
--- a/mysql-test/t/insert.test
+++ b/mysql-test/t/insert.test
@@ -65,3 +65,34 @@ use test_$1;
create table t1 (c int);
insert into test_$1.t1 set test_$1.t1.c = '1';
drop database test_$1;
+use test;
+--disable_warnings
+drop table if exists t1,t2,t3;
+--enable_warnings
+create table t1(id1 int not null auto_increment primary key, t char(12));
+create table t2(id2 int not null, t char(12));
+create table t3(id3 int not null, t char(12), index(id3));
+disable_query_log;
+let $1 = 100;
+while ($1)
+ {
+ let $2 = 5;
+ eval insert into t1(t) values ('$1');
+ while ($2)
+ {
+ eval insert into t2(id2,t) values ($1,'$2');
+ let $3 = 10;
+ while ($3)
+ {
+ eval insert into t3(id3,t) values ($1,'$2');
+ dec $3;
+ }
+ dec $2;
+ }
+ dec $1;
+ }
+enable_query_log;
+select count(*) from t2;
+insert into t2 select t1.* from t1, t2 t, t3 where t1.id1 = t.id2 and t.id2 = t3.id3;
+select count(*) from t2;
+drop table if exists t1,t2,t3;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
new file mode 100644
index 00000000000..93852b2219d
--- /dev/null
+++ b/mysql-test/t/sp-error.test
@@ -0,0 +1,135 @@
+#
+# Stored PROCEDURE error tests
+#
+
+# Make sure we don't have any procedures left.
+delete from mysql.proc;
+
+delimiter |;
+
+# Check that we get the right error, i.e. UDF declaration parses correctly,
+# but foo.so doesn't exist.
+# QQ This generates an error message containing a misleading errno which
+# might vary between systems (it usually doesn't have anything to do with
+# the actual failing dlopen()).
+#--error 1126
+#create function foo returns real soname "foo.so"|
+
+create procedure proc1()
+ set @x = 42|
+
+create function func1() returns int
+ return 42|
+
+# Can't create recursively
+--error 1259
+create procedure foo()
+ create procedure bar() set @x=3|
+--error 1259
+create procedure foo()
+ create function bar() returns double return 2.3|
+
+# Already exists
+--error 1260
+create procedure proc1()
+ set @x = 42|
+--error 1260
+create function func1() returns int
+ return 42|
+
+drop procedure proc1|
+drop function func1|
+
+# Does not exist
+--error 1261
+alter procedure foo|
+--error 1261
+alter function foo|
+--error 1261
+drop procedure foo|
+--error 1261
+drop function foo|
+--error 1261
+call foo()|
+drop procedure if exists foo|
+
+# LEAVE/ITERATE with no match
+--error 1264
+create procedure foo()
+foo: loop
+ leave bar;
+end loop|
+--error 1264
+create procedure foo()
+foo: loop
+ iterate bar;
+end loop|
+
+# Redefining label
+--error 1265
+create procedure foo()
+foo: loop
+ foo: loop
+ set @x=2;
+ end loop foo;
+end loop foo|
+
+# End label mismatch
+--error 1266
+create procedure foo()
+foo: loop
+ set @x=2;
+end loop bar|
+
+# Referring to undef variable
+--error 1267
+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 1268
+call foo()|
+drop procedure foo|
+
+# RETURN in FUNCTION only
+--error 1269
+create procedure foo()
+ return 42|
+
+# Doesn't allow queries in FUNCTIONs (for now :-( )
+--error 1270
+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 1274
+call p()|
+--error 1274
+call p(1, 2)|
+--error 1274
+select f()|
+--error 1274
+select f(1, 2)|
+
+drop procedure p|
+drop function f|
+
+delimiter ;|
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
new file mode 100644
index 00000000000..c501501b82f
--- /dev/null
+++ b/mysql-test/t/sp.test
@@ -0,0 +1,622 @@
+#
+# Basic stored PROCEDURE tests
+#
+#
+
+use test;
+
+--disable_warnings
+drop table if exists t1;
+drop table if exists t2;
+--enable_warnings
+
+create table t1 (
+ id char(16) not null,
+ data int not null
+);
+create table t2 (
+ s char(16) not null,
+ i int not null,
+ d double not null
+);
+
+
+# Single statement, no params.
+create procedure foo42()
+ insert into test.t1 values ("foo", 42);
+
+call foo42();
+select * from t1;
+delete from t1;
+drop procedure foo42;
+
+
+# USE test: Make sure we remain in the same DB.
+create procedure u()
+ use sptmp;
+
+--disable_warnings
+drop database if exists sptmp;
+--enable_warnings
+create database sptmp;
+use test;
+call u();
+select database();
+drop database sptmp;
+drop procedure u;
+
+
+# Single statement, two IN params.
+create procedure bar(x char(16), y int)
+ insert into test.t1 values (x, y);
+
+call bar("bar", 666);
+select * from t1;
+delete from t1;
+# Don't drop procedure yet...
+
+
+# Now for multiple statements...
+delimiter |;
+
+# Two statements.
+create procedure two(x1 char(16), x2 char(16), y int)
+begin
+ insert into test.t1 values (x1, y);
+ insert into test.t1 values (x2, y);
+end|
+
+call two("one", "two", 3)|
+select * from t1|
+delete from t1|
+drop procedure two|
+
+
+# Simple test of local variables and SET.
+create procedure locset(x char(16), y int)
+begin
+ declare z1, z2 int;
+ set z1 = y;
+ set z2 = z1+2;
+ insert into test.t1 values (x, z2);
+end|
+
+call locset("locset", 19)|
+select * from t1|
+delete from t1|
+drop procedure locset|
+
+
+# The peculiar (non-standard) mixture of variables types in SET.
+create procedure mixset(x char(16), y int)
+begin
+ declare z int;
+
+ set @z = y, z = 666, max_join_size = 100;
+ insert into test.t1 values (x, z);
+end|
+
+call mixset("mixset", 19)|
+show variables like 'max_join_size'|
+select id,data,@z from t1|
+delete from t1|
+drop procedure mixset|
+
+
+# Multiple CALL statements, one with OUT parameter.
+create procedure zip(x char(16), y int)
+begin
+ declare z int;
+ call zap(y, z);
+ call bar(x, z);
+end|
+
+# SET local variables and OUT parameter.
+create procedure zap(x int, out y int)
+begin
+ declare z int;
+ set z = x+1, y = z;
+end|
+
+call zip("zip", 99)|
+select * from t1|
+delete from t1|
+drop procedure zip|
+drop procedure zap|
+drop procedure bar|
+
+
+# INOUT test
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+ call inc2(x2, y);
+ insert into test.t1 values (x1, y);
+end|
+
+create procedure inc2(x char(16), y int)
+begin
+ call inc(y);
+ insert into test.t1 values (x, y);
+end|
+
+create procedure inc(inout io int)
+ set io = io + 1|
+
+call iotest("io1", "io2", 1)|
+select * from t1|
+delete from t1|
+drop procedure iotest|
+drop procedure inc2|
+drop procedure inc|
+
+
+# Call-by-value test
+# The expected result is:
+# ("cbv2", 4)
+# ("cbv1", 4711)
+create procedure cbv1()
+begin
+ declare y int default 3;
+
+ call cbv2(y+1, y);
+ insert into test.t1 values ("cbv1", y);
+end|
+
+create procedure cbv2(y1 int, inout y2 int)
+begin
+ set y2 = 4711;
+ insert into test.t1 values ("cbv2", y1);
+end|
+
+call cbv1()|
+select * from t1|
+delete from t1|
+drop procedure cbv1|
+drop procedure cbv2|
+
+
+# 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;
diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test
index dffe6ec79c2..cf1db24d013 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -348,7 +348,6 @@ INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
select * from t1;
INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2;
select * from t1;
--- error 1093
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
-- sleep 1
@@ -492,6 +491,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);
@@ -505,6 +507,7 @@ do (SELECT a from t1);
-- error 1146
set @a:=(SELECT a from t1);
+
CREATE TABLE t1 (a int, KEY(a));
HANDLER t1 OPEN;
-- error 1149
diff --git a/scripts/mysql_install_db.sh b/scripts/mysql_install_db.sh
index 1a969895f7f..0ce45c8d114 100644
--- a/scripts/mysql_install_db.sh
+++ b/scripts/mysql_install_db.sh
@@ -353,6 +353,19 @@ then
c_c="$c_c comment='Column privileges';"
fi
+if test ! -f $mdata/proc.frm
+then
+ echo "Preparing proc table"
+
+ c_p="$c_p CREATE TABLE proc ("
+ c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p type enum('function','procedure') NOT NULL,"
+ c_p="$c_p body blob DEFAULT '' NOT NULL,"
+ c_p="$c_p PRIMARY KEY (name,type)"
+ c_p="$c_p )"
+ c_p="$c_p comment='Stored Procedures';"
+fi
+
echo "Installing privilege tables"
if (
cat << END_OF_DATA
@@ -371,6 +384,7 @@ $i_f
$c_t
$c_c
+$c_p
END_OF_DATA
if test -n "$fill_help_tables"
then
diff --git a/sql/Makefile.am b/sql/Makefile.am
index a4858ab2b38..74372e9ae2f 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -57,7 +57,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
lex.h lex_symbol.h sql_acl.h sql_crypt.h \
log_event.h mini_client.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
- spatial.h gstream.h
+ spatial.h gstream.h sp_head.h sp_pcontext.h \
+ sp_rcontext.h sp.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
@@ -85,7 +86,8 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
mini_client.cc mini_client_errors.c 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
gen_lex_hash_SOURCES = gen_lex_hash.cc
gen_lex_hash_LDADD = $(LDADD) $(CXXLDFLAGS)
diff --git a/sql/filesort.cc b/sql/filesort.cc
index 928138b8d48..ab645836b4b 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -315,7 +315,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")));
@@ -756,15 +756,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;
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index dbed955c0a9..9057e21d4c2 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -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
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 213f5baf388..90c5c4a0eb7 100644
--- a/sql/ha_myisam.cc
+++ b/sql/ha_myisam.cc
@@ -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,...)
diff --git a/sql/item.cc b/sql/item.cc
index 7dd8392b695..484c55a4dec 100644
--- a/sql/item.cc
+++ b/sql/item.cc
@@ -22,6 +22,7 @@
#include "mysql_priv.h"
#include <m_ctype.h>
#include "my_dir.h"
+#include "sp_rcontext.h"
/*****************************************************************************
** Item functions
@@ -175,6 +176,23 @@ CHARSET_INFO * Item::default_charset() const
return current_thd->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 Item::set_charset(CHARSET_INFO *cs1, enum coercion co1,
CHARSET_INFO *cs2, enum coercion co2)
{
diff --git a/sql/item.h b/sql/item.h
index 4862ad21fbe..e6330c57fa7 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -123,6 +123,8 @@ public:
bool set_charset(CHARSET_INFO *cs1, enum coercion co1,
CHARSET_INFO *cs2, enum coercion co2);
virtual void set_outer_resolving() {}
+ virtual Item *this_item() { return this; } /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
// Row emulation
virtual uint cols() { return 1; }
@@ -136,6 +138,57 @@ public:
};
+// A local SP variable (incl. parameters), used in runtime
+class Item_splocal : public Item
+{
+private:
+
+ uint m_offset;
+
+public:
+
+ Item_splocal(uint offset)
+ : m_offset(offset)
+ {}
+
+ Item *this_item();
+ Item *this_const_item() const;
+
+ inline uint get_offset()
+ {
+ return m_offset;
+ }
+
+ // Abstract methods inherited from Item. Just defer the call to
+ // the item in the frame
+ inline enum Type type() const
+ {
+ return this_const_item()->type();
+ }
+
+ inline double val()
+ {
+ return this_item()->val();
+ }
+
+ inline longlong val_int()
+ {
+ return this_item()->val_int();
+ }
+
+ inline String *val_str(String *sp)
+ {
+ return this_item()->val_str(sp);
+ }
+
+ inline void make_field(Send_field *field)
+ {
+ this_item()->make_field(field);
+ }
+
+};
+
+
class st_select_lex;
class Item_ident :public Item
{
diff --git a/sql/item_func.cc b/sql/item_func.cc
index d427e3c5a3a..0933977370c 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -30,6 +30,9 @@
#ifdef HAVE_COMPRESS
#include <zlib.h>
#endif
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp.h"
/* return TRUE if item is a constant */
@@ -2852,3 +2855,74 @@ longlong Item_func_srid::val_int()
uint32 res= uint4korr(swkb->ptr());
return (longlong) res;
}
+
+int
+Item_func_sp::execute(Item **itp)
+{
+ DBUG_ENTER("Item_func_sp::execute");
+ THD *thd= current_thd;
+
+ if (! m_sp)
+ m_sp= sp_find_function(thd, &m_name);
+ if (! m_sp)
+ DBUG_RETURN(-1);
+
+ DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp));
+}
+
+enum enum_field_types
+Item_func_sp::field_type() const
+{
+ DBUG_ENTER("Item_func_sp::field_type");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
+ DBUG_RETURN(m_sp->m_returns);
+ }
+ DBUG_RETURN(MYSQL_TYPE_STRING);
+}
+
+Item_result
+Item_func_sp::result_type() const
+{
+ DBUG_ENTER("Item_func_sp::result_type");
+ DBUG_PRINT("info", ("m_sp = %p", m_sp));
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_RETURN(m_sp->result());
+ }
+ DBUG_RETURN(STRING_RESULT);
+}
+
+void
+Item_func_sp::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_sp::fix_length_and_dec");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, &m_name);
+ if (m_sp)
+ {
+ switch (m_sp->result()) {
+ case STRING_RESULT:
+ maybe_null= 1;
+ max_length= 0;
+ break;
+ case REAL_RESULT:
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ break;
+ case INT_RESULT:
+ decimals= 0;
+ max_length= 21;
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index e1d6156c12c..77dd58c85ee 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -1188,3 +1188,69 @@ enum Item_cast
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
};
+
+
+/*
+ *
+ * Stored FUNCTIONs
+ *
+ */
+
+class sp_head;
+
+class Item_func_sp :public Item_func
+{
+private:
+ LEX_STRING m_name;
+ mutable sp_head *m_sp;
+
+ int execute(Item **itp);
+
+public:
+
+ Item_func_sp(LEX_STRING name)
+ :Item_func(), m_name(name), m_sp(NULL)
+ {}
+
+ Item_func_sp(LEX_STRING name, List<Item> &list)
+ :Item_func(list), m_name(name), m_sp(NULL)
+ {}
+
+ virtual ~Item_func_sp()
+ {}
+
+ const char *func_name() const
+ {
+ return m_name.str;
+ }
+
+ enum enum_field_types field_type() const;
+
+ Item_result result_type() const;
+
+ longlong val_int()
+ {
+ return (longlong)Item_func_sp::val();
+ }
+
+ double val()
+ {
+ Item *it;
+
+ if (execute(&it))
+ return 0.0;
+ return it->val();
+ }
+
+ String *val_str(String *str)
+ {
+ Item *it;
+
+ if (execute(&it))
+ return NULL;
+ return it->val_str(str);
+ }
+
+ void fix_length_and_dec();
+
+};
diff --git a/sql/lex.h b/sql/lex.h
index e51b3efff87..46966bd9747 100644
--- a/sql/lex.h
+++ b/sql/lex.h
@@ -60,6 +60,7 @@ static SYMBOL symbols[] = {
{ "AS", SYM(AS),0,0},
{ "ASC", SYM(ASC),0,0},
{ "ASCII", SYM(ASCII_SYM),0,0},
+ { "ASENSITIVE", SYM(ASENSITIVE_SYM),0,0},
{ "AVG", SYM(AVG_SYM),0,0},
{ "AVG_ROW_LENGTH", SYM(AVG_ROW_LENGTH),0,0},
{ "AUTO_INCREMENT", SYM(AUTO_INC),0,0},
@@ -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},
@@ -120,6 +124,7 @@ static SYMBOL symbols[] = {
{ "DAY_SECOND", SYM(DAY_SECOND_SYM),0,0},
{ "DEC", SYM(DECIMAL_SYM),0,0},
{ "DECIMAL", SYM(DECIMAL_SYM),0,0},
+ { "DECLARE", SYM(DECLARE_SYM),0,0},
{ "DES_KEY_FILE", SYM(DES_KEY_FILE),0,0},
{ "DEFAULT", SYM(DEFAULT),0,0},
{ "DELAYED", SYM(DELAYED_SYM),0,0},
@@ -142,6 +147,7 @@ static SYMBOL symbols[] = {
{ "ERRORS", SYM(ERRORS),0,0},
{ "END", SYM(END),0,0},
{ "ELSE", SYM(ELSE),0,0},
+ { "ELSEIF", SYM(ELSEIF_SYM),0,0},
{ "ESCAPE", SYM(ESCAPE_SYM),0,0},
{ "ESCAPED", SYM(ESCAPED),0,0},
{ "ENABLE", SYM(ENABLE_SYM),0,0},
@@ -172,7 +178,7 @@ static SYMBOL symbols[] = {
{ "FOR", SYM(FOR_SYM),0,0},
{ "FULL", SYM(FULL),0,0},
{ "FULLTEXT", SYM(FULLTEXT_SYM),0,0},
- { "FUNCTION", SYM(UDF_SYM),0,0},
+ { "FUNCTION", SYM(FUNCTION_SYM),0,0},
{ "GEOMETRY", SYM(GEOMETRY_SYM),0,0},
{ "GEOMETRYCOLLECTION",SYM(GEOMETRYCOLLECTION),0,0},
{ "GLOBAL", SYM(GLOBAL_SYM),0,0},
@@ -198,6 +204,8 @@ static SYMBOL symbols[] = {
{ "INNER", SYM(INNER_SYM),0,0},
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
+ { "INOUT", SYM(INOUT_SYM),0,0},
+ { "INSENSITIVE", SYM(INSENSITIVE_SYM),0,0},
{ "INSERT", SYM(INSERT),0,0},
{ "INSERT_METHOD", SYM(INSERT_METHOD),0,0},
{ "INT", SYM(INT_SYM),0,0},
@@ -215,12 +223,14 @@ static SYMBOL symbols[] = {
{ "ISOLATION", SYM(ISOLATION),0,0},
{ "ISAM", SYM(ISAM_SYM),0,0},
{ "ISSUER", SYM(ISSUER_SYM),0,0},
+ { "ITERATE", SYM(ITERATE_SYM),0,0},
{ "JOIN", SYM(JOIN_SYM),0,0},
{ "KEY", SYM(KEY_SYM),0,0},
{ "KEYS", SYM(KEYS),0,0},
{ "KILL", SYM(KILL_SYM),0,0},
{ "LAST", SYM(LAST_SYM),0,0},
{ "LEADING", SYM(LEADING),0,0},
+ { "LEAVE", SYM(LEAVE_SYM),0,0},
{ "LEFT", SYM(LEFT),0,0},
{ "LEVEL", SYM(LEVEL_SYM),0,0},
{ "LIKE", SYM(LIKE),0,0},
@@ -236,6 +246,7 @@ static SYMBOL symbols[] = {
{ "LOGS", SYM(LOGS_SYM),0,0},
{ "LONG", SYM(LONG_SYM),0,0},
{ "LONGBLOB", SYM(LONGBLOB),0,0},
+ { "LOOP", SYM(LOOP_SYM),0,0},
{ "LONGTEXT", SYM(LONGTEXT),0,0},
{ "LOW_PRIORITY", SYM(LOW_PRIORITY),0,0},
{ "MASTER", SYM(MASTER_SYM),0,0},
@@ -290,6 +301,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},
@@ -320,13 +332,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 */
@@ -338,6 +352,7 @@ static SYMBOL symbols[] = {
{ "SECOND", SYM(SECOND_SYM),0,0},
{ "SEPARATOR", SYM(SEPARATOR_SYM),0,0},
{ "SELECT", SYM(SELECT_SYM),0,0},
+ { "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
{ "SERIAL", SYM(SERIAL_SYM),0,0},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0},
{ "SESSION", SYM(SESSION_SYM),0,0},
@@ -352,6 +367,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},
@@ -394,6 +410,7 @@ static SYMBOL symbols[] = {
{ "UNIQUE", SYM(UNIQUE_SYM),0,0},
{ "UNLOCK", SYM(UNLOCK_SYM),0,0},
{ "UNSIGNED", SYM(UNSIGNED),0,0},
+ { "UNTIL", SYM(UNTIL_SYM),0,0},
{ "USE", SYM(USE_SYM),0,0},
{ "USE_FRM", SYM(USE_FRM),0,0},
{ "USING", SYM(USING),0,0},
@@ -412,6 +429,7 @@ static SYMBOL symbols[] = {
{ "WRITE", SYM(WRITE_SYM),0,0},
{ "WHEN", SYM(WHEN_SYM),0,0},
{ "WHERE", SYM(WHERE),0,0},
+ { "WHILE", SYM(WHILE_SYM),0,0},
{ "XOR", SYM(XOR),0,0},
{ "X509", SYM(X509_SYM),0,0},
{ "YEAR", SYM(YEAR_SYM),0,0},
@@ -593,7 +611,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)},
diff --git a/sql/lock.cc b/sql/lock.cc
index 82004298453..45ffefed14f 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -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);
diff --git a/sql/log.cc b/sql/log.cc
index 50471041ee1..0fac27989f2 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -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,
@@ -1064,7 +1064,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,
@@ -1436,8 +1436,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,
@@ -1453,12 +1452,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);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 39db264d898..6be3050127e 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -804,7 +804,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;
diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h
index d17faa3cea5..2adfd31d151 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -45,7 +45,7 @@ char *sql_strdup(const char *str);
char *sql_strmake(const char *str,uint len);
gptr sql_memdup(const void * ptr,unsigned size);
void sql_element_free(void *ptr);
-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);
@@ -364,7 +364,7 @@ extern "C" pthread_handler_decl(handle_one_connection,arg);
extern "C" pthread_handler_decl(handle_bootstrap,arg);
void end_thread(THD *thd,bool put_in_cache);
void flush_thread_cache();
-void mysql_execute_command(THD *thd);
+int mysql_execute_command(THD *thd);
bool do_command(THD *thd);
bool dispatch_command(enum enum_server_command command, THD *thd,
char* packet, uint packet_length);
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index 6907da7d8e1..58676ea2fec 100644
--- a/sql/mysqld.cc
+++ b/sql/mysqld.cc
@@ -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
@@ -350,7 +351,7 @@ uint mysql_port;
uint test_flags = 0, select_errors=0, dropping_tables=0,ha_open_options=0;
uint volatile thread_count=0, thread_running=0, kill_cached_threads=0,
wake_thread=0;
-ulong thd_startup_options=(OPTION_UPDATE_LOG | OPTION_AUTO_IS_NULL |
+ulong thd_startup_options=(OPTION_AUTO_IS_NULL |
OPTION_BIN_LOG | OPTION_QUOTE_SHOW_CREATE );
uint protocol_version=PROTOCOL_VERSION;
struct system_variables global_system_variables;
@@ -639,7 +640,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;
@@ -918,7 +919,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
@@ -1400,7 +1400,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
@@ -2191,9 +2191,55 @@ static int init_server_components()
LOG_NORMAL);
if (opt_update_log)
{
- open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
- NullS, LOG_NEW);
- using_update_log=1;
+ /*
+ Update log is removed since 5.0. But we still accept the option.
+ The idea is if the user already uses the binlog and the update log,
+ we completely ignore any option/variable related to the update log, like
+ if the update log did not exist. But if the user uses only the update log,
+ then we translate everything into binlog for him (with warnings).
+ Implementation of the above :
+ - If mysqld is started with --log-update and --log-bin,
+ ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
+ is used, and turn off --sql-bin-update-same.
+ This will completely ignore SQL_LOG_UPDATE
+ - If mysqld is started with --log-update only,
+ change it to --log-bin (with the filename passed to log-update,
+ plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
+ used, and turn on --sql-bin-update-same.
+ This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
+
+ Note that we tell the user that --sql-bin-update-same is deprecated and
+ does nothing, and we don't take into account if he used this option or
+ not; but internally we give this variable a value to have the behaviour we
+ want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
+ As sql-bin-update-same, log-update and log-bin cannot be changed by the user
+ after starting the server (they are not variables), the user will not
+ later interfere with the settings we do here.
+ */
+ if (opt_bin_log)
+ {
+ opt_sql_bin_update= 0;
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log.");
+ }
+ else
+ {
+ opt_sql_bin_update= 1;
+ opt_bin_log= 1;
+ if (opt_update_logname)
+ {
+ // as opt_bin_log==0, no need to free opt_bin_logname
+ if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+ exit(EXIT_OUT_OF_MEMORY);
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin='%s' instead.",opt_bin_logname);
+ }
+ else
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin instead.");
+ }
}
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
@@ -2816,7 +2862,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);
net_printf(thd,ER_CANT_CREATE_THREAD,error);
(void) pthread_mutex_lock(&LOCK_thread_count);
@@ -3681,7 +3727,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,
@@ -3934,9 +3981,9 @@ Disable with --skip-innodb (will save memory)",
(gptr*) &mysql_unix_port, (gptr*) &mysql_unix_port, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
- "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.",
- (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option does nothing anymore.",
+ 0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sql-mode", OPT_SQL_MODE,
"Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, SERIALIZE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index d00ecb5dbc4..50577463c4c 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -360,7 +360,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));
}
diff --git a/sql/records.cc b/sql/records.cc
index e6c6e62a516..9ac04650091 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -181,7 +181,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)
diff --git a/sql/set_var.cc b/sql/set_var.cc
index e4adbb0a318..20bb7bb5b30 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -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);
@@ -275,7 +276,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,
@@ -1470,6 +1471,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 |
OPTION_UPDATE_LOG);
@@ -1477,6 +1502,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=
diff --git a/sql/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt
index 2e6e732fc28..76e69e23426 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -269,3 +269,19 @@ v/*
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 4b2c5438738..363c9baa54c 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -262,4 +262,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 29bf735928d..9d008de76bb 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -270,4 +270,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 2628f806d15..19032f55d83 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index ed5d5c680d5..1bfdf8207ac 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -264,4 +264,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index 6a36c4a2c2e..4775f14f30b 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 1f9a19ffda2..72f66175c0b 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -268,4 +268,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index cee08e0a6fd..4c947568f8c 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index d33bdfe803f..29f28886320 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -261,4 +261,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 1e9a0e5b634..3b64be1e140 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 6a5e7289908..8e81096cc73 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -261,4 +261,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index b4810f2e47a..45622f0d8c1 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 405c093f7c0..2dccc62389e 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -261,4 +261,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index f52fa772e6e..4af581f29bd 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -261,4 +261,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 89f7b359ffc..9bfbfd1a19f 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -263,4 +263,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index fff7dadface..7cfa3dfb258 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index 48a2d15d49e..840c4a78332 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -264,3 +264,19 @@
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index c21d4dd9422..a96437b3e92 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -261,4 +261,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index 8e40e195d1a..034a1aec7a9 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -255,4 +255,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index f49e31e8c57..46cd8fd3052 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -267,4 +267,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index 67967c08404..52e9ce77ec3 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -260,4 +260,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index fbc423a90af..76b9b631632 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -259,4 +259,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d rad(er) kapades av group_concat()" \ No newline at end of file
+"%d rad(er) kapades av group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index 7e023c8cc28..5867dc07c3c 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -264,4 +264,20 @@
"Z_BUF_ERROR: Not enough memory available for zlib"
"Z_MEM_ERROR: Not enough room in the output buffer for zlib (probably, length of uncompressed data was corrupted)"
"Z_DATA_ERROR: Input data was corrupted for zlib"
-"%d line(s) was(were) cut by group_concat()" \ No newline at end of file
+"%d line(s) was(were) cut by group_concat()"
+"Can't create a %s from within another stored routine"
+"%s %s already exists"
+"%s %s does not exist"
+"Failed to DROP %s %s"
+"Failed to CREATE %s %s"
+"%s with no matching label: %s"
+"Redefining label %s"
+"End-label %s without match"
+"Referring to uninitialized variable %s"
+"SELECT in a stored procedure must have INTO"
+"RETURN is only allowed in a FUNCTION"
+"Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION"
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been ignored."
+"The update log is deprecated and replaced by the binary log. SET SQL_LOG_UPDATE has been translated to SET SQL_LOG_BIN."
+"Query execution was interrupted"
+"Wrong number of arguments for %s %s, expected %u, got %u"
diff --git a/sql/slave.cc b/sql/slave.cc
index eae795ae760..1c5ad681153 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -553,7 +553,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());
}
}
}
@@ -1853,7 +1853,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) mi->slave_running,
(int) (error == -2),
diff --git a/sql/sp.cc b/sql/sp.cc
new file mode 100644
index 00000000000..0d6a7b99e32
--- /dev/null
+++ b/sql/sp.cc
@@ -0,0 +1,405 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysql_priv.h"
+#include "sp.h"
+#include "sp_head.h"
+
+static sp_head *
+sp_find_cached_function(THD *thd, char *name, uint namelen);
+
+/*
+ *
+ * DB storage of Stored PROCEDUREs and FUNCTIONs
+ *
+ */
+
+// *openeed=true means we opened ourselves
+static int
+db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
+ enum thr_lock_type ltype, TABLE **tablep, bool *opened)
+{
+ DBUG_ENTER("db_find_routine_aux");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ byte key[65]; // We know name is 64 and the enum is 1 byte
+ uint keylen;
+ int ret;
+
+ // Put the key together
+ keylen= namelen;
+ if (keylen > sizeof(key)-1)
+ keylen= sizeof(key)-1;
+ memcpy(key, name, keylen);
+ memset(key+keylen, (int)' ', sizeof(key)-1 - keylen); // Pad with space
+ key[sizeof(key)-1]= type;
+ keylen= sizeof(key);
+
+ for (table= thd->open_tables ; table ; table= table->next)
+ if (strcmp(table->table_cache_key, "mysql") == 0 &&
+ strcmp(table->real_name, "proc") == 0)
+ break;
+ if (table)
+ *opened= FALSE;
+ else
+ {
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+ if (! (table= open_ltable(thd, &tables, ltype)))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+ *opened= TRUE;
+ }
+
+ if (table->file->index_read_idx(table->record[0], 0,
+ key, keylen,
+ HA_READ_KEY_EXACT))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+ }
+ *tablep= table;
+
+ DBUG_RETURN(SP_OK);
+}
+
+static int
+db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
+{
+ DBUG_ENTER("db_find_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ extern int yyparse(void *thd);
+ LEX *tmplex;
+ TABLE *table;
+ const char *defstr;
+ int ret;
+ bool opened;
+
+ // QQ Set up our own mem_root here???
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
+ if (ret != SP_OK)
+ goto done;
+ if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ if (opened)
+ {
+ close_thread_tables(thd, 0, 1);
+ table= NULL;
+ }
+
+ tmplex= lex_start(thd, (uchar*)defstr, strlen(defstr));
+ if (yyparse(thd) || thd->is_fatal_error || tmplex->sphead == NULL)
+ ret= SP_PARSE_ERROR;
+ else
+ *sphp= tmplex->sphead;
+
+ done:
+ if (table && opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_create_routine(THD *thd, int type,
+ char *name, uint namelen, char *def, uint deflen)
+{
+ DBUG_ENTER("db_create_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def));
+ int ret;
+ TABLE *table;
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_WRITE)))
+ ret= SP_OPEN_TABLE_FAILED;
+ else
+ {
+ restore_record(table, 2); // Get default values for fields
+
+ table->field[0]->store(name, namelen, system_charset_info);
+ table->field[1]->store((longlong)type);
+ table->field[2]->store(def, deflen, system_charset_info);
+
+ if (table->file->write_row(table->record[0]))
+ ret= SP_WRITE_ROW_FAILED;
+ else
+ ret= SP_OK;
+ }
+
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_drop_routine(THD *thd, int type, char *name, uint namelen)
+{
+ DBUG_ENTER("db_drop_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ int ret;
+ bool opened;
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ if (table->file->delete_row(table->record[0]))
+ ret= SP_DELETE_ROW_FAILED;
+ }
+
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ *
+ * PROCEDURE
+ *
+ */
+
+sp_head *
+sp_find_procedure(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_procedure");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
+ name->str, name->length, &sp) != SP_OK)
+ sp= NULL;
+
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen)
+{
+ DBUG_ENTER("sp_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_procedure");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ *
+ * FUNCTION
+ *
+ */
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_function");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ sp= sp_find_cached_function(thd, name->str, name->length);
+ if (! sp)
+ {
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, &sp) != SP_OK)
+ sp= NULL;
+ }
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen)
+{
+ DBUG_ENTER("sp_create_function");
+ DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_function");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ int ret;
+
+ ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name)
+{
+ TABLE *table;
+ bool ret= FALSE;
+ bool opened;
+
+ if (db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, TL_READ,
+ &table, &opened) == SP_OK)
+ {
+ ret= TRUE;
+ }
+ if (opened)
+ close_thread_tables(thd, 0, 1);
+ return ret;
+}
+
+
+/*
+ *
+ * The temporary FUNCTION cache. (QQ This will be rehacked later, but
+ * it's needed now to make functions work at all.)
+ *
+ */
+
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
+{
+ List_iterator_fast<char> li(lex->spfuns);
+ char *fn;
+
+ while ((fn= li++))
+ {
+ uint len= strlen(fn);
+
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)fn, len,
+ (const uchar *)fun.str, fun.length) == 0)
+ break;
+ }
+ if (! fn)
+ {
+ char *s= sql_strmake(fun.str, fun.length);
+ lex->spfuns.push_back(s);
+ }
+}
+
+void
+sp_merge_funs(LEX *dst, LEX *src)
+{
+ List_iterator_fast<char> li(src->spfuns);
+ char *fn;
+
+ while ((fn= li++))
+ {
+ LEX_STRING lx;
+
+ lx.str= fn; lx.length= strlen(fn);
+ sp_add_fun_to_lex(dst, lx);
+ }
+}
+
+/* QQ Not terribly efficient right now, but it'll do for starters.
+ We should actually open the mysql.proc table just once. */
+int
+sp_cache_functions(THD *thd, LEX *lex)
+{
+ List_iterator<char> li(lex->spfuns);
+ char *fn;
+ enum_sql_command cmd= lex->sql_command;
+ int ret= 0;
+
+ while ((fn= li++))
+ {
+ List_iterator_fast<sp_head> lisp(thd->spfuns);
+ sp_head *sp;
+
+ while ((sp= lisp++))
+ {
+ if (my_strcasecmp(system_charset_info, fn, sp->name()) == 0)
+ break;
+ }
+ if (sp)
+ continue;
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION, fn, strlen(fn), &sp) == SP_OK)
+ {
+ ret= sp_cache_functions(thd, &thd->lex);
+ if (ret)
+ break;
+ thd->spfuns.push_back(sp);
+ }
+ else
+ {
+ send_error(thd, ER_SP_DOES_NOT_EXIST);
+ ret= 1;
+ }
+ }
+ lex->sql_command= cmd;
+ return ret;
+}
+
+void
+sp_clear_function_cache(THD *thd)
+{
+ List_iterator_fast<sp_head> li(thd->spfuns);
+ sp_head *sp;
+
+ while ((sp= li++))
+ sp->destroy();
+ thd->spfuns.empty();
+}
+
+static sp_head *
+sp_find_cached_function(THD *thd, char *name, uint namelen)
+{
+ List_iterator_fast<sp_head> li(thd->spfuns);
+ sp_head *sp;
+
+ while ((sp= li++))
+ {
+ uint len;
+ const uchar *n= (const uchar *)sp->name(&len);
+
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name, namelen,
+ n, len) == 0)
+ break;
+ }
+ return sp;
+}
diff --git a/sql/sp.h b/sql/sp.h
new file mode 100644
index 00000000000..21fcb4c5360
--- /dev/null
+++ b/sql/sp.h
@@ -0,0 +1,65 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_H_
+#define _SP_H_
+
+// Return codes from sp_create_* and sp_drop_*:
+#define SP_OK 0
+#define SP_KEY_NOT_FOUND -1
+#define SP_OPEN_TABLE_FAILED -2
+#define SP_WRITE_ROW_FAILED -3
+#define SP_DELETE_ROW_FAILED -4
+#define SP_GET_FIELD_FAILED -5
+#define SP_PARSE_ERROR -6
+
+sp_head *
+sp_find_procedure(THD *thd, LEX_STRING *name);
+
+int
+sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen);
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen);
+
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name);
+
+int
+sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen);
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen);
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name);
+
+
+// QQ More temporary stuff until the real cache is implemented. This is
+// needed since we have to read the functions before we do anything else.
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
+void
+sp_merge_funs(LEX *dst, LEX *src);
+int
+sp_cache_functions(THD *thd, LEX *lex);
+void
+sp_clear_function_cache(THD *thd);
+
+#endif /* _SP_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
new file mode 100644
index 00000000000..3bd958cfba9
--- /dev/null
+++ b/sql/sp_head.cc
@@ -0,0 +1,571 @@
+/* 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(sql_strmake(s->c_ptr_quick(), s->length()),
+ s->length(), it->charset());
+ break;
+ }
+ }
+ }
+
+ DBUG_RETURN(it);
+}
+
+sp_head::sp_head(LEX_STRING *name, LEX *lex)
+ : Sql_alloc(), m_simple_case(FALSE), m_multi_query(FALSE)
+{
+ DBUG_ENTER("sp_head::sp_head");
+ const char *dstr = (const char*)lex->buf;
+
+ DBUG_PRINT("info", ("name: %s", name->str));
+ m_name.length= name->length;
+ m_name.str= name->str;
+ m_defstr.length= lex->end_of_query - lex->buf;
+ m_defstr.str= sql_strmake(dstr, m_defstr.length);
+ m_pcont= lex->spcont;
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
+ m_backpatch.empty();
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::create(THD *thd)
+{
+ DBUG_ENTER("sp_head::create");
+ int ret;
+
+ DBUG_PRINT("info", ("type: %d name: %s def: %s",
+ m_type, m_name.str, m_defstr.str));
+ if (m_type == TYPE_ENUM_FUNCTION)
+ ret= sp_create_function(thd,
+ m_name.str, m_name.length,
+ m_defstr.str, m_defstr.length);
+ else
+ ret= sp_create_procedure(thd,
+ m_name.str, m_name.length,
+ m_defstr.str, m_defstr.length);
+
+ DBUG_RETURN(ret);
+}
+
+void
+sp_head::destroy()
+{
+ DBUG_ENTER("sp_head::destroy");
+ DBUG_PRINT("info", ("name: %s", m_name.str));
+ delete_dynamic(&m_instr);
+ m_pcont->destroy();
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::execute(THD *thd)
+{
+ DBUG_ENTER("sp_head::execute");
+ char olddbname[128];
+ char *olddbptr= thd->db;
+ int ret= 0;
+ uint ip= 0;
+
+ if (olddbptr)
+ {
+ uint i= 0;
+ char *p= olddbptr;
+
+ /* Fast inline strncpy without padding... */
+ while (*p && i < sizeof(olddbname))
+ olddbname[i++]= *p++;
+ if (i == sizeof(olddbname))
+ i-= 1; // QQ Error or warning for truncate?
+ olddbname[i]= '\0';
+ }
+
+ do
+ {
+ sp_instr *i;
+
+ i = get_instr(ip); // Returns NULL when we're done.
+ if (i == NULL)
+ break;
+ DBUG_PRINT("execute", ("Instruction %u", ip));
+ ret= i->execute(thd, &ip);
+ } while (ret == 0 && !thd->killed);
+
+ DBUG_PRINT("info", ("ret=%d killed=%d", ret, thd->killed));
+ if (thd->killed)
+ ret= -1;
+ /* If the DB has changed, the pointer has changed too, but the
+ original thd->db will then have been freed */
+ if (olddbptr && olddbptr != thd->db)
+ {
+ /* QQ Maybe we should issue some special error message or warning here,
+ if this fails?? */
+ if (! thd->killed)
+ ret= mysql_change_db(thd, olddbname);
+ }
+ DBUG_RETURN(ret);
+}
+
+
+int
+sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp)
+{
+ DBUG_ENTER("sp_head::execute_function");
+ DBUG_PRINT("info", ("function %s", m_name.str));
+ uint csize = m_pcont->max_framesize();
+ uint params = m_pcont->params();
+ sp_rcontext *octx = thd->spcont;
+ sp_rcontext *nctx = NULL;
+ uint i;
+ int ret;
+
+ 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 0
+ // QQ This works if the parameter really is a user variable, but
+ // for the moment we can't assure that, so it will crash if it's
+ // something else. So for now, we just do nothing, to avoid a crash.
+ // Note: This also assumes we have a get_name() method in
+ // the Item_func_get_user_var class.
+ Item *item= nctx->get_item(i);
+ Item_func_set_user_var *suv;
+ Item_func_get_user_var *guv= static_cast<Item_func_get_user_var*>(it);
+
+ suv= new Item_func_set_user_var(guv->get_name(), item);
+ suv->fix_fields(thd, NULL, &item);
+ suv->fix_length_and_dec();
+ suv->update();
+#endif
+ }
+ }
+ }
+
+ if (tmp_octx)
+ thd->spcont= NULL;
+ else
+ thd->spcont= octx;
+ }
+
+ DBUG_RETURN(ret);
+}
+
+
+// Reset lex during parsing, before we parse a sub statement.
+void
+sp_head::reset_lex(THD *thd)
+{
+ memcpy(&m_lex, &thd->lex, sizeof(LEX)); // Save old one
+ /* Reset most stuff. The length arguments doesn't matter here. */
+ lex_start(thd, m_lex.buf, m_lex.end_of_query - m_lex.ptr);
+ /* We must reset ptr and end_of_query again */
+ thd->lex.ptr= m_lex.ptr;
+ thd->lex.end_of_query= m_lex.end_of_query;
+ /* And keep the SP stuff too */
+ thd->lex.sphead = m_lex.sphead;
+ thd->lex.spcont = m_lex.spcont;
+ /* Clear all lists. (QQ Why isn't this reset by lex_start()?).
+ We may be overdoing this, but we know for sure that value_list must
+ be cleared at least. */
+ thd->lex.col_list.empty();
+ thd->lex.ref_list.empty();
+ thd->lex.drop_list.empty();
+ thd->lex.alter_list.empty();
+ thd->lex.interval_list.empty();
+ thd->lex.users_list.empty();
+ thd->lex.columns.empty();
+ thd->lex.key_list.empty();
+ thd->lex.create_list.empty();
+ thd->lex.insert_list= NULL;
+ thd->lex.field_list.empty();
+ thd->lex.value_list.empty();
+ thd->lex.many_values.empty();
+ thd->lex.var_list.empty();
+ thd->lex.param_list.empty();
+ thd->lex.proc_list.empty();
+ thd->lex.auxilliary_table_list.empty();
+}
+
+// Restore lex during parsing, after we have parsed a sub statement.
+void
+sp_head::restore_lex(THD *thd)
+{
+ // Update some state in the old one first
+ m_lex.ptr= thd->lex.ptr;
+ m_lex.next_state= thd->lex.next_state;
+
+ // Collect some data from the sub statement lex.
+ sp_merge_funs(&m_lex, &thd->lex);
+#if 0
+ // QQ We're not using this at the moment.
+ if (thd->lex.sql_command == SQLCOM_CALL)
+ {
+ // It would be slightly faster to keep the list sorted, but we need
+ // an "insert before" method to do that.
+ char *proc= thd->lex.udf.name.str;
+
+ List_iterator_fast<char *> li(m_calls);
+ char **it;
+
+ while ((it= li++))
+ if (my_strcasecmp(system_charset_info, proc, *it) == 0)
+ break;
+ if (! it)
+ m_calls.push_back(&proc);
+
+ }
+ // Merge used tables
+ // QQ ...or just open tables in thd->open_tables?
+ // This is not entirerly clear at the moment, but for now, we collect
+ // tables here.
+ for (SELECT_LEX *sl= thd->lex.all_selects_list ;
+ sl ;
+ sl= sl->next_select())
+ {
+ for (TABLE_LIST *tables= sl->get_table_list() ;
+ tables ;
+ tables= tables->next)
+ {
+ List_iterator_fast<char *> li(m_tables);
+ char **tb;
+
+ while ((tb= li++))
+ if (my_strcasecmp(system_charset_info, tables->real_name, *tb) == 0)
+ break;
+ if (! tb)
+ m_tables.push_back(&tables->real_name);
+ }
+ }
+#endif
+
+ memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Restore lex
+}
+
+void
+sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
+{
+ bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
+
+ if (bp)
+ {
+ bp->lab= lab;
+ bp->instr= i;
+ (void)m_backpatch.push_front(bp);
+ }
+}
+
+void
+sp_head::backpatch(sp_label_t *lab)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator_fast<bp_t> li(m_backpatch);
+
+ while ((bp= li++))
+ if (bp->lab == lab)
+ {
+ sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr);
+
+ i->set_destination(dest);
+ }
+}
+
+
+// ------------------------------------------------------------------
+
+//
+// sp_instr_stmt
+//
+int
+sp_instr_stmt::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_stmt::execute");
+ DBUG_PRINT("info", ("command: %d", m_lex.sql_command));
+ LEX olex; // The other lex
+ int res;
+
+ memcpy(&olex, &thd->lex, sizeof(LEX)); // Save the other lex
+
+ memcpy(&thd->lex, &m_lex, sizeof(LEX)); // Use my own lex
+ thd->lex.thd = thd;
+
+ res= mysql_execute_command(thd);
+ if (thd->lock || thd->open_tables || thd->derived_tables)
+ {
+ thd->proc_info="closing tables";
+ close_thread_tables(thd); /* Free tables */
+ }
+
+ memcpy(&thd->lex, &olex, sizeof(LEX)); // Restore the other lex
+
+ *nextp = m_ip+1;
+ DBUG_RETURN(res);
+}
+
+//
+// sp_instr_set
+//
+int
+sp_instr_set::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_set::execute");
+ DBUG_PRINT("info", ("offset: %u", m_offset));
+ thd->spcont->set_item(m_offset, eval_func_item(thd, m_value, m_type));
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump
+//
+int
+sp_instr_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump_if
+//
+int
+sp_instr_jump_if::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+
+ if (it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_jump_if_not
+//
+int
+sp_instr_jump_if_not::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_jump_if_not::execute");
+ DBUG_PRINT("info", ("destination: %u", m_dest));
+ Item *it= eval_func_item(thd, m_expr, MYSQL_TYPE_TINY);
+
+ if (! it->val_int())
+ *nextp = m_dest;
+ else
+ *nextp = m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_return
+//
+int
+sp_instr_return::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_return::execute");
+ thd->spcont->set_result(eval_func_item(thd, m_value, m_type));
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000000..b0685ba7ca3
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,356 @@
+/* -*- 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 0
+ // QQ We're not using this at the moment.
+ List<char *> m_calls; // Called procedures.
+ List<char *> m_tables; // Used tables.
+#endif
+
+ sp_head(LEX_STRING *name, LEX *lex);
+
+ int
+ create(THD *thd);
+
+ // Free memory
+ void
+ destroy();
+
+ int
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ int
+ execute_procedure(THD *thd, List<Item> *args);
+
+ inline void
+ add_instr(sp_instr *i)
+ {
+ insert_dynamic(&m_instr, (gptr)&i);
+ }
+
+ inline uint
+ instructions()
+ {
+ return m_instr.elements;
+ }
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void
+ reset_lex(THD *thd);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void
+ restore_lex(THD *thd);
+
+ // Put the instruction on the backpatch list, associated with the label.
+ void
+ push_backpatch(sp_instr *, struct sp_label *);
+
+ // Update all instruction with this label in the backpatch list to
+ // the current position.
+ void
+ backpatch(struct sp_label *);
+
+ char *name(uint *lenp = 0) const
+ {
+ if (lenp)
+ *lenp= m_name.length;
+ return m_name.str;
+ }
+
+ inline Item_result result()
+ {
+ return sp_map_result_type(m_returns);
+ }
+
+private:
+
+ LEX_STRING m_name;
+ LEX_STRING m_defstr;
+ sp_pcontext *m_pcont; // Parse context
+ LEX m_lex; // Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; // The "instructions"
+ typedef struct
+ {
+ struct sp_label *lab;
+ sp_instr *instr;
+ } bp_t;
+ List<bp_t> m_backpatch; // Instructions needing backpatching
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *ip;
+
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (gptr)&ip, i);
+ else
+ ip= NULL;
+ return ip;
+ }
+
+ int
+ execute(THD *thd);
+
+}; // class sp_head : public Sql_alloc
+
+
+//
+// "Instructions"...
+//
+
+class sp_instr : public Sql_alloc
+{
+ sp_instr(const sp_instr &); /* Prevent use of these */
+ void operator=(sp_instr &);
+
+public:
+
+ // Should give each a name or type code for debugging purposes?
+ sp_instr(uint ip)
+ : Sql_alloc(), m_ip(ip)
+ {}
+
+ virtual ~sp_instr()
+ {}
+
+ // Execute this instrution. '*nextp' will be set to the index of the next
+ // instruction to execute. (For most instruction this will be the
+ // instruction following this one.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int
+ execute(THD *thd, uint *nextp)
+ { // Default is a no-op.
+ *nextp = m_ip+1; // Next instruction
+ return 0;
+ }
+
+protected:
+
+ uint m_ip; // My index
+
+}; // class sp_instr : public Sql_alloc
+
+
+//
+// Call out to some prepared SQL statement.
+//
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+
+ sp_instr_stmt(uint ip)
+ : sp_instr(ip)
+ {}
+
+ virtual ~sp_instr_stmt()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ inline void
+ set_lex(LEX *lex)
+ {
+ memcpy(&m_lex, lex, sizeof(LEX));
+ }
+
+ inline LEX *
+ get_lex()
+ {
+ return &m_lex;
+ }
+
+private:
+
+ LEX m_lex; // My own lex
+
+}; // class sp_instr_stmt : public sp_instr
+
+
+class sp_instr_set : public sp_instr
+{
+ sp_instr_set(const sp_instr_set &); /* Prevent use of these */
+ void operator=(sp_instr_set &);
+
+public:
+
+ sp_instr_set(uint ip, uint offset, Item *val, enum enum_field_types type)
+ : sp_instr(ip), m_offset(offset), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_set()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_offset; // Frame offset
+ Item *m_value;
+ enum enum_field_types m_type; // The declared type
+
+}; // class sp_instr_set : public sp_instr
+
+
+class sp_instr_jump : public sp_instr
+{
+ sp_instr_jump(const sp_instr_jump &); /* Prevent use of these */
+ void operator=(sp_instr_jump &);
+
+public:
+
+ sp_instr_jump(uint ip)
+ : sp_instr(ip)
+ {}
+
+ sp_instr_jump(uint ip, uint dest)
+ : sp_instr(ip), m_dest(dest)
+ {}
+
+ virtual ~sp_instr_jump()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ virtual void
+ set_destination(uint dest)
+ {
+ m_dest= dest;
+ }
+
+protected:
+
+ int m_dest; // Where we will go
+
+}; // class sp_instr_jump : public sp_instr
+
+
+class sp_instr_jump_if : public sp_instr_jump
+{
+ sp_instr_jump_if(const sp_instr_jump_if &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if &);
+
+public:
+
+ sp_instr_jump_if(uint ip, Item *i)
+ : sp_instr_jump(ip), m_expr(i)
+ {}
+
+ sp_instr_jump_if(uint ip, Item *i, uint dest)
+ : sp_instr_jump(ip, dest), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if : public sp_instr_jump
+
+
+class sp_instr_jump_if_not : public sp_instr_jump
+{
+ sp_instr_jump_if_not(const sp_instr_jump_if_not &); /* Prevent use of these */
+ void operator=(sp_instr_jump_if_not &);
+
+public:
+
+ sp_instr_jump_if_not(uint ip, Item *i)
+ : sp_instr_jump(ip), m_expr(i)
+ {}
+
+ sp_instr_jump_if_not(uint ip, Item *i, uint dest)
+ : sp_instr_jump(ip, dest), m_expr(i)
+ {}
+
+ virtual ~sp_instr_jump_if_not()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ Item *m_expr; // The condition
+
+}; // class sp_instr_jump_if_not : public sp_instr_jump
+
+
+class sp_instr_return : public sp_instr
+{
+ sp_instr_return(const sp_instr_return &); /* Prevent use of these */
+ void operator=(sp_instr_return &);
+
+public:
+
+ sp_instr_return(uint ip, Item *val, enum enum_field_types type)
+ : sp_instr(ip), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_return()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+protected:
+
+ Item *m_value;
+ enum enum_field_types m_type;
+
+}; // class sp_instr_return : public sp_instr
+
+#endif /* _SP_HEAD_H_ */
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
new file mode 100644
index 00000000000..3973f05b74b
--- /dev/null
+++ b/sql/sp_pcontext.cc
@@ -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;
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
new file mode 100644
index 00000000000..6900e18aa93
--- /dev/null
+++ b/sql/sp_pcontext.h
@@ -0,0 +1,162 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_PCONTEXT_H_
+#define _SP_PCONTEXT_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+typedef enum
+{
+ sp_param_in,
+ sp_param_out,
+ sp_param_inout
+} sp_param_mode_t;
+
+typedef struct
+{
+ LEX_STRING name;
+ enum enum_field_types type;
+ sp_param_mode_t mode;
+ uint offset; // Offset in current frame
+ my_bool isset;
+} sp_pvar_t;
+
+typedef struct sp_label
+{
+ char *name;
+ uint ip; // Instruction index
+} sp_label_t;
+
+class sp_pcontext : public Sql_alloc
+{
+ sp_pcontext(const sp_pcontext &); /* Prevent use of these */
+ void operator=(sp_pcontext &);
+
+ public:
+
+ sp_pcontext();
+
+ // Free memory
+ void
+ destroy();
+
+ inline uint
+ max_framesize()
+ {
+ return m_framesize;
+ }
+
+ inline uint
+ current_framesize()
+ {
+ return m_pvar.elements;
+ }
+
+ inline uint
+ params()
+ {
+ return m_params;
+ }
+
+ // Set the number of parameters to the current esize
+ inline void
+ set_params()
+ {
+ m_params= m_pvar.elements;
+ }
+
+ inline void
+ set_type(uint i, enum enum_field_types type)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->type= type;
+ }
+
+ inline void
+ set_isset(uint i, my_bool val)
+ {
+ sp_pvar_t *p= find_pvar(i);
+
+ if (p)
+ p->isset= val;
+ }
+
+ void
+ push(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop the last 'num' slots of the frame
+ inline void
+ pop(uint num = 1)
+ {
+ while (num--)
+ pop_dynamic(&m_pvar);
+ }
+
+ // Find by name
+ sp_pvar_t *
+ find_pvar(LEX_STRING *name);
+
+ // Find by index
+ sp_pvar_t *
+ find_pvar(uint i)
+ {
+ sp_pvar_t *p;
+
+ if (i < m_pvar.elements)
+ get_dynamic(&m_pvar, (gptr)&p, i);
+ else
+ p= NULL;
+ return p;
+ }
+
+ sp_label_t *
+ push_label(char *name, uint ip);
+
+ sp_label_t *
+ find_label(char *name);
+
+ inline sp_label_t *
+ last_label()
+ {
+ return m_label.head();
+ }
+
+ inline sp_label_t *
+ pop_label()
+ {
+ return m_label.pop();
+ }
+
+private:
+
+ uint m_params; // The number of parameters
+ uint m_framesize; // The maximum framesize
+
+ DYNAMIC_ARRAY m_pvar;
+
+ List<sp_label_t> m_label; // The label list
+ uint m_genlab; // Gen. label counter
+
+}; // class sp_pcontext : public Sql_alloc
+
+
+#endif /* _SP_PCONTEXT_H_ */
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
new file mode 100644
index 00000000000..78485fdd090
--- /dev/null
+++ b/sql/sp_rcontext.h
@@ -0,0 +1,95 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_RCONTEXT_H_
+#define _SP_RCONTEXT_H_
+
+class sp_rcontext : public Sql_alloc
+{
+ sp_rcontext(const sp_rcontext &); /* Prevent use of these */
+ void operator=(sp_rcontext &);
+
+ public:
+
+ sp_rcontext(uint size)
+ : m_count(0), m_size(size), m_result(NULL)
+ {
+ m_frame = (Item **)sql_alloc(size * sizeof(Item*));
+ m_outs = (int *)sql_alloc(size * sizeof(int));
+ }
+
+ ~sp_rcontext()
+ {
+ // Not needed?
+ //sql_element_free(m_frame);
+ }
+
+ inline void
+ push_item(Item *i)
+ {
+ if (m_count < m_size)
+ m_frame[m_count++] = i;
+ }
+
+ inline void
+ set_item(uint idx, Item *i)
+ {
+ if (idx < m_count)
+ m_frame[idx] = i;
+ }
+
+ inline Item *
+ get_item(uint idx)
+ {
+ return m_frame[idx];
+ }
+
+ inline void
+ set_oindex(uint idx, int oidx)
+ {
+ m_outs[idx] = oidx;
+ }
+
+ inline int
+ get_oindex(uint idx)
+ {
+ return m_outs[idx];
+ }
+
+ inline void
+ set_result(Item *it)
+ {
+ m_result= it;
+ }
+
+ inline Item *
+ get_result()
+ {
+ return m_result;
+ }
+
+private:
+
+ uint m_count;
+ uint m_size;
+ Item **m_frame;
+ int *m_outs;
+ Item *m_result; // For FUNCTIONs
+
+}; // class sp_rcontext : public Sql_alloc
+
+#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index 4bed99489de..db4d60ebf77 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1164,7 +1164,6 @@ bool change_password(THD *thd, const char *host, const char *user,
acl_user->user ? acl_user->user : "",
acl_user->host.hostname ? acl_user->host.hostname : "",
new_password));
- mysql_update_log.write(thd, buff, query_length);
Query_log_event qinfo(thd, buff, query_length, 0);
mysql_bin_log.write(&qinfo);
DBUG_RETURN(0);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index e374cdb3696..c0fc6066ded 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2408,7 +2408,7 @@ bool remove_table_from_cache(THD *thd, const char *db, const char *table_name,
/* Kill delayed insert threads */
if (in_use->system_thread && ! in_use->killed)
{
- in_use->killed=1;
+ in_use->killed= THD::KILL_CONNECTION;
pthread_mutex_lock(&in_use->mysys_var->mutex);
if (in_use->mysys_var->current_cond)
{
diff --git a/sql/sql_cache.cc b/sql/sql_cache.cc
index d8265a1b359..8dc4802c3ee 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -2956,7 +2956,7 @@ void Query_cache::wreck(uint line, const char *message)
DBUG_PRINT("warning", ("%5d QUERY CACHE WRECK => DISABLED",line));
DBUG_PRINT("warning", ("=================================="));
if (thd)
- thd->killed = 1;
+ thd->killed= THD::KILL_CONNECTION;
cache_dump();
/* check_integrity(0); */ /* Can't call it here because of locks */
bins_dump();
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index f579b02ee50..0ead869216f 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -36,6 +36,8 @@
#endif
#include <mysys_err.h>
+#include <sp_rcontext.h>
+
/*
The following is used to initialise Table_ident with a internal
table name
@@ -86,12 +88,13 @@ extern "C" void free_user_var(user_var_entry *entry)
THD::THD():user_time(0), is_fatal_error(0),
last_insert_id_used(0),
insert_id_used(0), rand_used(0), in_lock_tables(0),
- global_read_lock(0), bootstrap(0)
+ global_read_lock(0), bootstrap(0), spcont(NULL)
{
host=user=priv_user=db=query=ip=0;
host_or_ip= "connecting host";
- locked=killed=count_cuted_fields=some_tables_deleted=no_errors=password=
+ locked=count_cuted_fields=some_tables_deleted=no_errors=password=
query_start_used=prepare_command=0;
+ killed= NOT_KILLED;
db_length=query_length=col_access=0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
@@ -181,7 +184,7 @@ THD::THD():user_time(0), is_fatal_error(0),
if (open_cached_file(&transaction.trans_log,
mysql_tmpdir, LOG_PREFIX, binlog_cache_size,
MYF(MY_WME)))
- killed=1;
+ killed= KILL_CONNECTION;
transaction.trans_log.end_of_file= max_binlog_cache_size;
}
#endif
@@ -196,6 +199,9 @@ THD::THD():user_time(0), is_fatal_error(0),
pthread_mutex_unlock(&LOCK_thread_count);
randominit(&rand, tmp + (ulong) &rand, tmp + (ulong) ::query_id);
}
+
+ /* QQ init the temporary function cache */
+ spfuns.empty();
}
@@ -286,6 +292,10 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
+
+ // extern void sp_clear_function_cache(THD *);
+ // sp_clear_function_cache(this);
+
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -337,14 +347,14 @@ THD::~THD()
}
-void THD::awake(bool prepare_to_die)
+void THD::awake(THD::killed_state state_to_set)
{
THD_CHECK_SENTRY(this);
safe_mutex_assert_owner(&LOCK_delete);
- if (prepare_to_die)
- killed = 1;
- thr_alarm_kill(real_id);
+ killed= state_to_set;
+ if (state_to_set != THD::KILL_QUERY)
+ thr_alarm_kill(real_id);
#ifdef SIGNAL_WITH_VIO_CLOSE
close_active_vio();
#endif
@@ -461,7 +471,7 @@ CHANGED_TABLE_LIST* THD::changed_table_dup(const char *key, long key_length)
{
my_error(EE_OUTOFMEMORY, MYF(ME_BELL),
ALIGN_SIZE(sizeof(TABLE_LIST)) + key_length + 1);
- killed= 1;
+ killed= KILL_CONNECTION;
return 0;
}
@@ -1007,9 +1017,12 @@ bool select_exists_subselect::send_data(List<Item> &items)
int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
{
List_iterator_fast<Item> li(list);
- List_iterator_fast<LEX_STRING> gl(var_list);
+ List_iterator_fast<my_var> gl(var_list);
Item *item;
+ my_var *mv;
LEX_STRING *ls;
+
+ row_count= 0;
if (var_list.elements != list.elements)
{
my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
@@ -1018,19 +1031,39 @@ int select_dumpvar::prepare(List<Item> &list, SELECT_LEX_UNIT *u)
unit=u;
while ((item=li++))
{
- ls= gl++;
- Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
- xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item);
- xx->fix_length_and_dec();
- vars.push_back(xx);
+ mv=gl++;
+ ls= &mv->s;
+ if (mv->local)
+ {
+ (void)local_vars.push_back(new Item_splocal(mv->offset));
+ }
+ else
+ {
+ Item_func_set_user_var *xx = new Item_func_set_user_var(*ls,item);
+ xx->fix_fields(thd,(TABLE_LIST*) thd->lex.select_lex.table_list.first,&item);
+ xx->fix_length_and_dec();
+ vars.push_back(xx);
+ }
}
return 0;
}
+
bool select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<Item_func_set_user_var> li(vars);
+ List_iterator_fast<Item_splocal> var_li(local_vars);
+ List_iterator_fast<my_var> my_li(var_list);
+ List_iterator_fast<Item> it(items);
Item_func_set_user_var *xx;
+ Item_splocal *yy;
+ Item *item;
+ my_var *zz;
DBUG_ENTER("send_data");
+ if (unit->offset_limit_cnt)
+ { // using limit offset,count
+ unit->offset_limit_cnt--;
+ DBUG_RETURN(0);
+ }
if (unit->offset_limit_cnt)
{ // Using limit offset,count
@@ -1042,8 +1075,21 @@ bool select_dumpvar::send_data(List<Item> &items)
my_error(ER_TOO_MANY_ROWS, MYF(0));
DBUG_RETURN(1);
}
- while ((xx=li++))
- xx->update();
+ while ((zz=my_li++) && (item=it++))
+ {
+ if (zz->local)
+ {
+ if ((yy=var_li++))
+ {
+ thd->spcont->set_item(yy->get_offset(), item);
+ }
+ }
+ else
+ {
+ if ((xx=li++))
+ xx->update();
+ }
+ }
DBUG_RETURN(0);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 925afde2202..1a3cd66c7e4 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -26,6 +26,7 @@
class Query_log_event;
class Load_log_event;
class Slave_log_event;
+class sp_rcontext;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY };
@@ -534,9 +535,22 @@ public:
bool query_start_used,last_insert_id_used,insert_id_used,rand_used;
bool system_thread,in_lock_tables,global_read_lock;
bool query_error, bootstrap, cleanup_done;
- bool volatile killed;
+
+ enum killed_state { NOT_KILLED=0, KILL_CONNECTION=ER_SERVER_SHUTDOWN, KILL_QUERY=ER_QUERY_INTERRUPTED };
+ killed_state volatile killed;
+ inline int killed_errno() const
+ {
+ return killed;
+ }
+ inline void send_kill_message() const
+ {
+ my_error(killed_errno(), MYF(0));
+ }
+
bool prepare_command;
bool tmp_table_used;
+ sp_rcontext *spcont; // SP runtime context
+ List<sp_head> spfuns; // SP FUNCTIONs
/*
If we do a purge of binary logs, log index info of the threads
@@ -576,7 +590,7 @@ public:
}
void close_active_vio();
#endif
- void awake(bool prepare_to_die);
+ void awake(THD::killed_state state_to_set);
inline const char* enter_cond(pthread_cond_t *cond, pthread_mutex_t* mutex,
const char* msg)
{
@@ -1051,13 +1065,22 @@ public:
bool send_eof();
};
+class my_var : public Sql_alloc {
+public:
+ LEX_STRING s;
+ bool local;
+ uint offset;
+ my_var (LEX_STRING& j, bool i, uint o) : s(j), local(i), offset(o) {}
+ ~my_var() {}
+};
class select_dumpvar :public select_result {
ha_rows row_count;
public:
- List<LEX_STRING> var_list;
+ List<my_var> var_list;
List<Item_func_set_user_var> vars;
- select_dumpvar(void) { var_list.empty(); vars.empty(); row_count=0;}
+ List<Item_splocal> local_vars;
+ select_dumpvar(void) { var_list.empty(); local_vars.empty(); vars.empty(); row_count=0;}
~select_dumpvar() {}
int prepare(List<Item> &list, SELECT_LEX_UNIT *u);
bool send_fields(List<Item> &list, uint flag) {return 0;}
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index fe8a945bff8..0b412a9d0f4 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -231,7 +231,6 @@ int mysql_create_db(THD *thd, char *db, HA_CREATE_INFO *create_info,
query= thd->query;
query_length= thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
@@ -283,7 +282,6 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
create_info->table_charset : default_charset_info;
}
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -369,7 +367,6 @@ int mysql_rm_db(THD *thd,char *db,bool if_exists, bool silent)
query=thd->query;
query_length=thd->query_length;
}
- mysql_update_log.write(thd, query, query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, query, query_length, 0);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 6bb336a87a6..08f1727c515 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -182,7 +182,6 @@ cleanup:
log_delayed= (transactional_table || table->tmp_table);
if (deleted && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -215,7 +214,7 @@ cleanup:
delete select;
free_underlaid_joins(thd, &thd->lex.select_lex);
if (error >= 0 || thd->net.report_error)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN: 0);
+ send_error(thd,thd->killed_errno());
else
{
send_ok(thd,deleted);
@@ -489,7 +488,6 @@ bool multi_delete::send_eof()
*/
if (deleted && (error <= 0 || normal_tables))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -604,7 +602,6 @@ end:
{
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
diff --git a/sql/sql_derived.cc b/sql/sql_derived.cc
index 7f555f37d40..964c50659ff 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -202,7 +202,13 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
else
unit->exclude_level();
org_table_list->db= (char *)"";
-#ifndef DBUG_OFF
+#if 0
+ /* QQ This was #ifndef DBUG_OFF, but that caused crashes with
+ * certain subselect args to SPs. Since ->derived is tested
+ * for non-null value in some places in the code, this seems
+ * to be the wrong way to do it. Simply letting derived be 0
+ * appears to work fine. /pem
+ */
/* Try to catch errors if this is accessed */
org_table_list->derived=(SELECT_LEX_UNIT *) 1;
#endif
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index dcb39f8526f..ab91c3fb29b 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -23,7 +23,7 @@
static int check_null_fields(THD *thd,TABLE *entry);
static TABLE *delayed_get_table(THD *thd,TABLE_LIST *table_list);
static int write_delayed(THD *thd,TABLE *table, enum_duplicates dup,
- char *query, uint query_length, int log_on);
+ char *query, uint query_length, bool log_on);
static void end_delayed_insert(THD *thd);
extern "C" pthread_handler_decl(handle_delayed_insert,arg);
static void unlink_blobs(register TABLE *table);
@@ -38,9 +38,6 @@ static void unlink_blobs(register TABLE *table);
#define my_safe_afree(ptr, size, min_length) if (size > min_length) my_free(ptr,MYF(0))
#endif
-#define DELAYED_LOG_UPDATE 1
-#define DELAYED_LOG_BIN 2
-
/*
Check if insert fields are correct
Updates table->time_stamp to point to timestamp field or 0, depending on
@@ -114,8 +111,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
By default, both logs are enabled (this won't cause problems if the server
runs without --log-update or --log-bin).
*/
- int log_on= DELAYED_LOG_UPDATE | DELAYED_LOG_BIN ;
-
+ bool log_on= (thd->options & OPTION_BIN_LOG) || (!(thd->master_access & SUPER_ACL));
bool transactional_table, log_delayed, bulk_insert;
uint value_count;
ulong counter = 1;
@@ -130,14 +126,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->lex.select_lex.table_list.first;
DBUG_ENTER("mysql_insert");
- if (thd->master_access & SUPER_ACL)
- {
- if (!(thd->options & OPTION_UPDATE_LOG))
- log_on&= ~(int) DELAYED_LOG_UPDATE;
- if (!(thd->options & OPTION_BIN_LOG))
- log_on&= ~(int) DELAYED_LOG_BIN;
- }
-
/*
in safe mode or with skip-new change delayed insert to be regular
if we are told to replace duplicates, the insert cannot be concurrent
@@ -338,7 +326,6 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
log_delayed= (transactional_table || table->tmp_table);
if ((info.copied || info.deleted) && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -562,13 +549,12 @@ public:
char *record,*query;
enum_duplicates dup;
time_t start_time;
- bool query_start_used,last_insert_id_used,insert_id_used;
- int log_query;
+ bool query_start_used,last_insert_id_used,insert_id_used, log_query;
ulonglong last_insert_id;
ulong time_stamp;
uint query_length;
- delayed_row(enum_duplicates dup_arg, int log_query_arg)
+ delayed_row(enum_duplicates dup_arg, bool log_query_arg)
:record(0),query(0),dup(dup_arg),log_query(log_query_arg) {}
~delayed_row()
{
@@ -871,7 +857,7 @@ TABLE *delayed_insert::get_local_table(THD* client_thd)
/* Put a question in queue */
static int write_delayed(THD *thd,TABLE *table,enum_duplicates duplic,
- char *query, uint query_length, int log_on)
+ char *query, uint query_length, bool log_on)
{
delayed_row *row=0;
delayed_insert *di=thd->di;
@@ -951,7 +937,7 @@ void kill_delayed_threads(void)
{
/* Ensure that the thread doesn't kill itself while we are looking at it */
pthread_mutex_lock(&tmp->mutex);
- tmp->thd.killed=1;
+ tmp->thd.killed= THD::KILL_CONNECTION;
if (tmp->thd.mysys_var)
{
pthread_mutex_lock(&tmp->thd.mysys_var->mutex);
@@ -990,7 +976,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
thd->thread_id=thread_id++;
thd->end_time();
threads.append(thd);
- thd->killed=abort_loop;
+ thd->killed=abort_loop ? THD::KILL_CONNECTION : THD::NOT_KILLED;
pthread_mutex_unlock(&LOCK_thread_count);
pthread_mutex_lock(&di->mutex);
@@ -1043,7 +1029,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
for (;;)
{
- if (thd->killed)
+ if (thd->killed == THD::KILL_CONNECTION)
{
uint lock_count;
/*
@@ -1091,7 +1077,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
break;
if (error == ETIME || error == ETIMEDOUT)
{
- thd->killed=1;
+ thd->killed= THD::KILL_CONNECTION;
break;
}
}
@@ -1110,7 +1096,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
/* request for new delayed insert */
if (!(thd->lock=mysql_lock_tables(thd,&di->table,1)))
{
- di->dead=thd->killed=1; // Fatal error
+ di->dead=thd->killed= THD::KILL_CONNECTION; // Fatal error
}
pthread_cond_broadcast(&di->cond_client);
}
@@ -1118,7 +1104,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
{
if (di->handle_inserts())
{
- di->dead=thd->killed=1; // Some fatal error
+ di->dead=thd->killed=THD::KILL_CONNECTION; // Some fatal error
}
}
di->status=0;
@@ -1145,7 +1131,7 @@ end:
close_thread_tables(thd); // Free the table
di->table=0;
- di->dead=thd->killed=1; // If error
+ di->dead=thd->killed= THD::KILL_CONNECTION; // If error
pthread_cond_broadcast(&di->cond_client); // Safety
pthread_mutex_unlock(&di->mutex);
@@ -1214,7 +1200,7 @@ bool delayed_insert::handle_inserts(void)
max_rows=delayed_insert_limit;
if (thd.killed || table->version != refresh_version)
{
- thd.killed=1;
+ thd.killed= THD::KILL_CONNECTION;
max_rows= ~0; // Do as much as possible
}
@@ -1258,15 +1244,10 @@ bool delayed_insert::handle_inserts(void)
using_ignore=0;
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
}
- if (row->query)
+ if (row->query && row->log_query && using_bin_log)
{
- if (row->log_query & DELAYED_LOG_UPDATE)
- mysql_update_log.write(&thd,row->query, row->query_length);
- if (row->log_query & DELAYED_LOG_BIN && using_bin_log)
- {
- Query_log_event qinfo(&thd, row->query, row->query_length,0);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(&thd, row->query, row->query_length,0);
+ mysql_bin_log.write(&qinfo);
}
if (table->blob_fields)
free_delayed_insert_blobs(table);
@@ -1449,7 +1430,6 @@ bool select_insert::send_eof()
if (last_insert_id)
thd->insert_id(last_insert_id); // For update log
::send_ok(thd,info.copied,last_insert_id,buff);
- mysql_update_log.write(thd,thd->query,thd->query_length);
return 0;
}
}
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 9bc4dfc74e7..e1298957a0f 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -21,6 +21,8 @@
#include "item_create.h"
#include <m_ctype.h>
#include <hash.h>
+#include "sp.h"
+#include "sp_head.h"
LEX_STRING tmp_table_alias= {(char*) "tmp-table",8};
@@ -109,6 +111,7 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
LEX *lex= &thd->lex;
lex->thd= thd;
lex->next_state=MY_LEX_START;
+ lex->buf= buf;
lex->end_of_query=(lex->ptr=buf)+length;
lex->yylineno = 1;
lex->select_lex.create_refs=lex->in_comment=0;
@@ -121,6 +124,9 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->yacc_yyss=lex->yacc_yyvs=0;
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
+ lex->spfuns.empty();
return lex;
}
@@ -144,6 +150,17 @@ static int find_keyword(LEX *lex, uint len, bool function)
lex->yylval->symbol.length=len;
return symbol->tok;
}
+
+ LEX_STRING ls;
+ ls.str = (char *)tok; ls.length= len;
+ if (function && sp_function_exists(current_thd, &ls)) // QQ temp fix
+ {
+ lex->safe_to_cache_query= 0;
+ lex->yylval->lex_str.str= lex->thd->strmake((char*)lex->tok_start, len);
+ lex->yylval->lex_str.length= len;
+ return SP_FUNC;
+ }
+
#ifdef HAVE_DLOPEN
udf_func *udf;
if (function && using_udf_functions && (udf=find_udf((char*) tok, len)))
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index e03814bcd2f..df5b84f88e3 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -21,6 +21,9 @@
class Table_ident;
class sql_exchange;
class LEX_COLUMN;
+class sp_head;
+class sp_instr;
+class sp_pcontext;
/*
The following hack is needed because mysql_yacc.cc does not define
@@ -72,6 +75,8 @@ enum enum_sql_command {
SQLCOM_SHOW_WARNS, SQLCOM_EMPTY_QUERY, SQLCOM_SHOW_ERRORS,
SQLCOM_SHOW_COLUMN_TYPES, SQLCOM_SHOW_TABLE_TYPES, SQLCOM_SHOW_PRIVILEGES,
SQLCOM_HELP,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
/* This should be the last !!! */
SQLCOM_END
@@ -429,6 +434,7 @@ typedef struct st_lex
SELECT_LEX_NODE *current_select;
/* list of all SELECT_LEX */
SELECT_LEX *all_selects_list;
+ uchar *buf; /* The beginning of string, used by SPs */
uchar *ptr,*tok_start,*tok_end,*end_of_query;
char *length,*dec,*change,*name;
char *backup_dir; /* For RESTORE/BACKUP */
@@ -488,6 +494,10 @@ typedef struct st_lex
CHARSET_INFO *charset;
char *help_arg;
SQL_LIST *gorder_list;
+ sp_head *sphead;
+ sp_pcontext *spcont;
+ List<char> spfuns; /* Called functions */
+
st_lex() {}
inline void uncacheable()
{
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 3c43c3d9e3a..f030fe5e001 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -315,9 +315,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
sprintf(name,ER(ER_LOAD_INFO),info.records,info.deleted,
info.records-info.copied,thd->cuted_fields);
send_ok(thd,info.copied+info.deleted,0L,name);
- // on the slave thd->query is never initialized
- if (!thd->slave_thread)
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (!log_delayed)
thd->options|=OPTION_STATUS_NO_TRANS_UPDATE;
@@ -379,7 +376,7 @@ read_fixed_length(THD *thd,COPY_INFO &info,TABLE *table,List<Item> &fields,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
it.rewind();
@@ -453,7 +450,7 @@ read_sep_field(THD *thd,COPY_INFO &info,TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
DBUG_RETURN(1);
}
while ((sql_field=(Item_field*) it++))
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index ba8a4af794a..540d7f132ac 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -26,6 +26,9 @@
#include "ha_innodb.h"
#endif
+#include "sp_head.h"
+#include "sp.h"
+
#ifdef HAVE_OPENSSL
/*
Without SSL the handshake consists of one packet. This packet
@@ -44,6 +47,15 @@
#define MIN_HANDSHAKE_SIZE 6
#endif /* HAVE_OPENSSL */
+/* Used in error handling only */
+#define SP_TYPE_STRING(LP) \
+ ((LP)->sphead->m_type == TYPE_ENUM_FUNCTION ? "FUNCTION" : "PROCEDURE")
+#define SP_COM_STRING(LP) \
+ ((LP)->sql_command == SQLCOM_CREATE_SPFUNCTION || \
+ (LP)->sql_command == SQLCOM_ALTER_FUNCTION || \
+ (LP)->sql_command == SQLCOM_DROP_FUNCTION ? \
+ "FUNCTION" : "PROCEDURE")
+
extern int yyparse(void *thd);
extern "C" pthread_mutex_t THR_LOCK_keycache;
#ifdef SOLARIS
@@ -833,7 +845,7 @@ pthread_handler_decl(handle_one_connection,arg)
thd->version=refresh_version;
thd->set_time();
thd->init_for_queries();
- while (!net->error && net->vio != 0 && !thd->killed)
+ while (!net->error && net->vio != 0 && !(thd->killed == THD::KILL_CONNECTION))
{
if (do_command(thd))
break;
@@ -1037,6 +1049,9 @@ bool do_command(THD *thd)
}
else
{
+ if (thd->killed == THD::KILL_QUERY)
+ thd->killed= THD::NOT_KILLED;
+
packet=(char*) net->read_pos;
command = (enum enum_server_command) (uchar) packet[0];
if (command >= COM_END)
@@ -1456,7 +1471,7 @@ restore_user:
{
statistic_increment(com_stat[SQLCOM_KILL],&LOCK_status);
ulong id=(ulong) uint4korr(packet);
- kill_one_thread(thd,id);
+ kill_one_thread(thd,id,false);
break;
}
case COM_DEBUG:
@@ -1511,6 +1526,14 @@ restore_user:
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
+ /*
+ Clear the SP function cache after each statement (QQ this is a temporary
+ solution; caching will be rehacked later).
+ Note: Must do this before we free_root.
+ */
+ sp_clear_function_cache(thd);
+
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1565,7 +1588,7 @@ bool alloc_query(THD *thd, char *packet, ulong packet_length)
** Execute command saved in thd and current_lex->sql_command
****************************************************************************/
-void
+int
mysql_execute_command(THD *thd)
{
int res= 0;
@@ -1575,6 +1598,13 @@ mysql_execute_command(THD *thd)
SELECT_LEX_UNIT *unit= &lex->unit;
DBUG_ENTER("mysql_execute_command");
+ if (lex->sql_command != SQLCOM_CREATE_PROCEDURE &&
+ lex->sql_command != SQLCOM_CREATE_SPFUNCTION)
+ {
+ if (sp_cache_functions(thd, lex))
+ DBUG_RETURN(-1);
+ }
+
/*
Reset warning count for each query that uses tables
A better approach would be to reset this for any commands
@@ -1597,7 +1627,7 @@ mysql_execute_command(THD *thd)
given and the table list says the query should not be replicated
*/
if (table_rules_on && tables && !tables_ok(thd,tables))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
#ifndef TO_BE_DELETED
/*
This is a workaround to deal with the shortcoming in 3.23.44-3.23.46
@@ -1631,8 +1661,8 @@ mysql_execute_command(THD *thd)
cursor)))
{
if (res < 0 || thd->net.report_error)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
- DBUG_VOID_RETURN;
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
}
}
}
@@ -1645,7 +1675,7 @@ mysql_execute_command(THD *thd)
!tables_ok(thd,tables))
#endif
)
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
switch (lex->sql_command) {
@@ -1684,7 +1714,7 @@ mysql_execute_command(THD *thd)
if (!(result= new select_send()))
{
send_error(thd, ER_OUT_OF_RESOURCES);
- DBUG_VOID_RETURN;
+ goto error;
}
else
thd->send_explain_fields(result);
@@ -1947,7 +1977,7 @@ mysql_execute_command(THD *thd)
find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
if (tables->next)
{
@@ -2029,7 +2059,7 @@ mysql_execute_command(THD *thd)
if (thd->locked_tables || thd->active_transaction())
{
send_error(thd,ER_LOCK_OR_ACTIVE_TRANSACTION);
- break;
+ goto error;
}
{
LOCK_ACTIVE_MI;
@@ -2042,7 +2072,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_ALTER_TABLE:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- break;
+ goto error;
#else
{
ulong priv=0;
@@ -2134,7 +2164,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_BINLOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_global_access(thd, SUPER_ACL))
@@ -2147,7 +2177,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_CREATE:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (check_db_used(thd, tables) ||
@@ -2225,7 +2255,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res= mysql_update(thd,tables,
select_lex->item_list,
@@ -2246,7 +2276,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
{
const char *msg= 0;
@@ -2282,7 +2312,7 @@ mysql_execute_command(THD *thd)
if (select_lex->item_list.elements != lex->value_list.elements)
{
send_error(thd,ER_WRONG_VALUE_COUNT);
- DBUG_VOID_RETURN;
+ goto error;
}
res = mysql_insert(thd,tables,lex->field_list,lex->many_values,
select_lex->item_list, lex->value_list,
@@ -2323,8 +2353,7 @@ mysql_execute_command(THD *thd)
if (find_real_table_in_list(tables->next, tables->db, tables->real_name))
{
- net_printf(thd,ER_UPDATE_TABLE_USED,tables->real_name);
- DBUG_VOID_RETURN;
+ lex->select_lex.options |= OPTION_BUFFER_RESULT;
}
/* Skip first table, which is the table we are inserting in */
@@ -2499,7 +2528,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_DATABASES:
#if defined(DONT_ALLOW_SHOW_COMMANDS)
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
if ((specialflag & SPECIAL_SKIP_SHOW_DB) &&
check_global_access(thd, SHOW_DB_ACL))
@@ -2533,7 +2562,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_LOGS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
if (grant_option && check_access(thd, FILE_ACL, any_db))
@@ -2546,7 +2575,7 @@ mysql_execute_command(THD *thd)
/* FALL THROUGH */
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=select_lex->db ? select_lex->db : thd->db;
@@ -2585,7 +2614,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_FIELDS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -2610,7 +2639,7 @@ mysql_execute_command(THD *thd)
case SQLCOM_SHOW_KEYS:
#ifdef DONT_ALLOW_SHOW_COMMANDS
send_error(thd,ER_NOT_ALLOWED_COMMAND); /* purecov: inspected */
- DBUG_VOID_RETURN;
+ goto error;
#else
{
char *db=tables->db;
@@ -2792,26 +2821,25 @@ mysql_execute_command(THD *thd)
res=mysqld_show_create_db(thd,lex->name,&lex->create_info);
break;
}
- case SQLCOM_CREATE_FUNCTION:
- if (check_access(thd,INSERT_ACL,"mysql",0,1))
- break;
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ if (check_access(thd,INSERT_ACL,"mysql",0,1))
+ break;
#ifdef HAVE_DLOPEN
- if (!(res = mysql_create_function(thd,&lex->udf)))
- send_ok(thd);
+ sp_head *sph= sp_find_function(thd, &lex->udf.name);
+ if (sph)
+ {
+ sph->destroy(); // QQ Free memory. Remove this when caching!!!
+ net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
+ goto error;
+ }
+ if (!(res = mysql_create_function(thd,&lex->udf)))
+ send_ok(thd);
#else
- res= -1;
+ res= -1;
#endif
- break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1))
break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
+ }
case SQLCOM_REVOKE:
case SQLCOM_GRANT:
{
@@ -2853,14 +2881,11 @@ mysql_execute_command(THD *thd)
goto error;
if (!(res = mysql_table_grant(thd,tables,lex->users_list, lex->columns,
lex->grant,
- lex->sql_command == SQLCOM_REVOKE)))
+ lex->sql_command == SQLCOM_REVOKE)) &&
+ mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
+ mysql_bin_log.write(&qinfo);
}
}
else
@@ -2875,7 +2900,6 @@ mysql_execute_command(THD *thd)
lex->sql_command == SQLCOM_REVOKE);
if (!res)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2900,7 +2924,7 @@ mysql_execute_command(THD *thd)
reload_acl_and_cache(thd, lex->type, tables);
break;
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
case SQLCOM_SHOW_GRANTS:
res=0;
@@ -2980,16 +3004,200 @@ mysql_execute_command(THD *thd)
res= -1;
thd->options&= ~(ulong) (OPTION_BEGIN | OPTION_STATUS_NO_TRANS_UPDATE);
break;
+ case SQLCOM_CREATE_PROCEDURE:
+ case SQLCOM_CREATE_SPFUNCTION:
+ if (!lex->sphead)
+ {
+ res= -1; // Shouldn't happen
+ break;
+ }
+ else
+ {
+ uint namelen;
+ char *name= lex->sphead->name(&namelen);
+#ifdef HAVE_DLOPEN
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION)
+ {
+ udf_func *udf = find_udf(name, namelen);
+
+ if (udf)
+ {
+ net_printf(thd, ER_UDF_EXISTS, name);
+ goto error;
+ }
+ }
+#endif
+ res= lex->sphead->create(thd);
+
+ lex->sphead->destroy(); // QQ Free memory. Remove this when caching!!!
+
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_WRITE_ROW_FAILED:
+ net_printf(thd, ER_SP_ALREADY_EXISTS, SP_TYPE_STRING(lex), name);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_STORE_FAILED, SP_TYPE_STRING(lex), name);
+ goto error;
+ }
+ break;
+ }
+ case SQLCOM_CALL:
+ {
+ sp_head *sp;
+
+ sp= sp_find_procedure(thd, &lex->udf.name);
+ if (! sp)
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "PROCEDURE", lex->udf.name);
+ goto error;
+ }
+ else
+ {
+ uint smrx;
+ LINT_INIT(smrx);
+
+ // In case the arguments are subselects...
+ if (tables && ((res= check_table_access(thd, SELECT_ACL, tables)) ||
+ (res= open_and_lock_tables(thd, tables))))
+ {
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ break;
+ }
+ fix_tables_pointers(lex->all_selects_list);
+
+#ifndef EMBEDDED_LIBRARY
+ // When executing substatements, they're assumed to send_error when
+ // it happens, but not to send_ok.
+ my_bool nsok= thd->net.no_send_ok;
+ thd->net.no_send_ok= TRUE;
+#endif
+ if (sp->m_multi_query)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_QUERIES))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ goto error;
+ }
+ smrx= thd->server_status & SERVER_MORE_RESULTS_EXISTS;
+ thd->server_status |= SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ res= sp->execute_procedure(thd, &lex->value_list);
+
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ if (sp->m_multi_query)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+
+ if (res == 0)
+ send_ok(thd);
+ else
+ goto error; // Substatement should already have sent error
+ }
+ break;
+ }
+ case SQLCOM_ALTER_PROCEDURE:
+ case SQLCOM_ALTER_FUNCTION:
+ {
+ sp_head *sp;
+
+ if (lex->sql_command == SQLCOM_ALTER_PROCEDURE)
+ sp= sp_find_procedure(thd, &lex->udf.name);
+ else
+ sp= sp_find_function(thd, &lex->udf.name);
+ if (! sp)
+ {
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name);
+ goto error;
+ }
+ else
+ {
+ /* QQ This is an no-op right now, since we haven't
+ put the characteristics in yet. */
+ sp->destroy(); // QQ Free memory. Remove this when caching!!!
+ send_ok(thd);
+ }
+ break;
+ }
+ case SQLCOM_DROP_PROCEDURE:
+ case SQLCOM_DROP_FUNCTION:
+ {
+ if (lex->sql_command == SQLCOM_DROP_PROCEDURE)
+ res= sp_drop_procedure(thd, lex->udf.name.str, lex->udf.name.length);
+ else
+ {
+ res= sp_drop_function(thd, lex->udf.name.str, lex->udf.name.length);
+#ifdef HAVE_DLOPEN
+ if (res == SP_KEY_NOT_FOUND)
+ {
+ udf_func *udf = find_udf(lex->udf.name.str, lex->udf.name.length);
+ if (udf)
+ {
+ if (check_access(thd, DELETE_ACL, "mysql", 0, 1))
+ goto error;
+ if (!(res = mysql_drop_function(thd,&lex->udf.name)))
+ {
+ send_ok(thd);
+ break;
+ }
+ }
+ }
+#endif
+ }
+ switch (res)
+ {
+ case SP_OK:
+ send_ok(thd);
+ break;
+ case SP_KEY_NOT_FOUND:
+ if (lex->drop_if_exists)
+ {
+ push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_WARN,
+ ER_SP_DOES_NOT_EXIST, ER(ER_SP_DOES_NOT_EXIST),
+ SP_COM_STRING(lex), lex->udf.name.str);
+ res= 0;
+ send_ok(thd);
+ break;
+ }
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ default:
+ net_printf(thd, ER_SP_DROP_FAILED, SP_COM_STRING(lex),
+ lex->udf.name.str);
+ goto error;
+ }
+ break;
+ }
default: /* Impossible */
send_ok(thd);
break;
}
thd->proc_info="query end"; // QQ
+
+ // We end up here if res == 0 and send_ok() has been done,
+ // or res != 0 and no send_error() has yet been done.
if (res < 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
+ DBUG_RETURN(res);
error:
- DBUG_VOID_RETURN;
+ // We end up here if send_error() has already been done.
+ DBUG_RETURN(-1);
}
@@ -3973,7 +4181,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
if (options & REFRESH_LOG)
{
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
#ifdef HAVE_REPLICATION
if (expire_logs_days)
@@ -4072,7 +4279,7 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables)
This is written such that we have a short lock on LOCK_thread_count
*/
-void kill_one_thread(THD *thd, ulong id)
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query)
{
THD *tmp;
uint error=ER_NO_SUCH_THREAD;
@@ -4092,7 +4299,7 @@ void kill_one_thread(THD *thd, ulong id)
if ((thd->master_access & SUPER_ACL) ||
!strcmp(thd->user,tmp->user))
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
else
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 95ac57013da..37a1cfe924a 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -738,7 +738,7 @@ static bool send_prepare_results(PREP_STMT *stmt)
DBUG_RETURN(0);
abort:
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
+ send_error(thd,thd->killed_errno());
DBUG_RETURN(1);
}
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index 19b4d299e59..5ef8d5d1c1d 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -79,7 +79,6 @@ bool mysql_rename_tables(THD *thd, TABLE_LIST *table_list)
/* Lets hope this doesn't fail as the result will be messy */
if (!error)
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
diff --git a/sql/sql_repl.cc b/sql/sql_repl.cc
index 0eb444b85c0..9ada41ff837 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -833,7 +833,7 @@ void kill_zombie_dump_threads(uint32 slave_server_id)
it will be slow because it will iterate through the list
again. We just to do kill the thread ourselves.
*/
- tmp->awake(1/*prepare to die*/);
+ tmp->awake(THD::KILL_QUERY);
pthread_mutex_unlock(&tmp->LOCK_delete);
}
}
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index e3d600b9798..cf5d2387ca5 100644
--- a/sql/sql_repl.h
+++ b/sql/sql_repl.h
@@ -20,7 +20,7 @@ extern I_List<i_string> binlog_do_db, binlog_ignore_db;
extern int max_binlog_dump_events;
extern my_bool opt_sporadic_binlog_dump_fail;
-#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(0 /* do not prepare to die*/); pthread_mutex_unlock(&(thd)->LOCK_delete); }
+#define KICK_SLAVE(thd) { pthread_mutex_lock(&(thd)->LOCK_delete); (thd)->awake(THD::NOT_KILLED); pthread_mutex_unlock(&(thd)->LOCK_delete); }
File open_binlog(IO_CACHE *log, const char *log_file_name,
const char **errmsg);
diff --git a/sql/sql_select.cc b/sql/sql_select.cc
index 18768099f0f..559ed59b0a4 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -191,7 +191,8 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
send_error(thd, 0, NullS);
res= 1;
}
- delete result;
+ if (result != lex->result)
+ delete result;
return res;
}
@@ -4983,7 +4984,7 @@ sub_select_cache(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
}
if (join->thd->killed) // If aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
if (join_tab->use_quick != 2 || test_if_quick_select(join_tab) <= 0)
@@ -5022,7 +5023,7 @@ sub_select(JOIN *join,JOIN_TAB *join_tab,bool end_of_records)
{
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; /* purecov: inspected */
}
join->examined_rows++;
@@ -5102,7 +5103,7 @@ flush_cached_records(JOIN *join,JOIN_TAB *join_tab,bool skipp_last)
{
if (join->thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
return -2; // Aborted by user /* purecov: inspected */
}
SQL_SELECT *select=join_tab->select;
@@ -5750,7 +5751,7 @@ end_write(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!end_of_records)
@@ -5818,7 +5819,7 @@ end_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -5888,7 +5889,7 @@ end_unique_update(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
DBUG_RETURN(0);
if (join->thd->killed) // Aborted by user
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
@@ -5935,7 +5936,7 @@ end_write_group(JOIN *join, JOIN_TAB *join_tab __attribute__((unused)),
if (join->thd->killed)
{ // Aborted by user
- my_error(ER_SERVER_SHUTDOWN,MYF(0)); /* purecov: inspected */
+ join->thd->send_kill_message();
DBUG_RETURN(-2); /* purecov: inspected */
}
if (!join->first_record || end_of_records ||
@@ -6657,7 +6658,7 @@ static int remove_dup_with_compare(THD *thd, TABLE *table, Field **first_field,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
@@ -6769,7 +6770,7 @@ static int remove_dup_with_hash_index(THD *thd, TABLE *table,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error=0;
goto err;
}
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 8d6eccae87a..b70a42af350 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1343,7 +1343,7 @@ void mysqld_list_processes(THD *thd,const char *user, bool verbose)
thd_info->command=(int) tmp->command;
if ((mysys_var= tmp->mysys_var))
pthread_mutex_lock(&mysys_var->mutex);
- thd_info->proc_info= (char*) (tmp->killed ? "Killed" : 0);
+ thd_info->proc_info= (char*) (tmp->killed == THD::KILL_CONNECTION? "Killed" : 0);
#ifndef EMBEDDED_LIBRARY
thd_info->state_info= (char*) (tmp->locked ? "Locked" :
tmp->net.reading_or_writing ?
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 8d216f121ab..6d546001781 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -255,15 +255,11 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
if (some_tables_deleted || tmp_table_deleted)
{
query_cache_invalidate3(thd, tables, 0);
- if (!dont_log_query)
+ if (!dont_log_query && mysql_bin_log.is_open())
{
- mysql_update_log.write(thd, thd->query,thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- tmp_table_deleted && !some_tables_deleted);
- mysql_bin_log.write(&qinfo);
- }
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ tmp_table_deleted && !some_tables_deleted);
+ mysql_bin_log.write(&qinfo);
}
}
@@ -914,17 +910,13 @@ int mysql_create_table(THD *thd,const char *db, const char *table_name,
}
thd->tmp_table_used= 1;
}
- if (!tmp_table && !no_log)
- {
+ if (!tmp_table && !no_log && mysql_bin_log.is_open())
// Must be written before unlock
- mysql_update_log.write(thd,thd->query, thd->query_length);
- if (mysql_bin_log.is_open())
- {
- Query_log_event qinfo(thd, thd->query, thd->query_length,
- test(create_info->options &
- HA_LEX_CREATE_TMP_TABLE));
- mysql_bin_log.write(&qinfo);
- }
+ {
+ Query_log_event qinfo(thd, thd->query, thd->query_length,
+ test(create_info->options &
+ HA_LEX_CREATE_TMP_TABLE));
+ mysql_bin_log.write(&qinfo);
}
error=0;
end:
@@ -1748,7 +1740,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
}
if (!error)
{
- mysql_update_log.write(thd, thd->query, thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2129,7 +2120,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
my_free((gptr) new_table,MYF(0));
goto err;
}
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2258,7 +2248,6 @@ int mysql_alter_table(THD *thd,char *new_db, char *new_name,
goto err;
}
thd->proc_info="end";
- mysql_update_log.write(thd, thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length, 0);
@@ -2387,7 +2376,7 @@ copy_data_between_tables(TABLE *from,TABLE *to,
{
if (thd->killed)
{
- my_error(ER_SERVER_SHUTDOWN,MYF(0));
+ thd->send_kill_message();
error= 1;
break;
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 50fdfac7087..1dba10dc299 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -323,7 +323,6 @@ int mysql_update(THD *thd,
log_delayed= (transactional_table || table->tmp_table);
if (updated && (error <= 0 || !transactional_table))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
@@ -357,7 +356,7 @@ int mysql_update(THD *thd,
delete select;
free_underlaid_joins(thd, &thd->lex.select_lex);
if (error >= 0)
- send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0); /* purecov: inspected */
+ send_error(thd,thd->killed_errno()); /* purecov: inspected */
else
{
char buff[80];
@@ -934,7 +933,6 @@ bool multi_update::send_eof()
if (updated && (local_error <= 0 || !trans_safe))
{
- mysql_update_log.write(thd,thd->query,thd->query_length);
if (mysql_bin_log.is_open())
{
Query_log_event qinfo(thd, thd->query, thd->query_length,
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 71035a75084..d1ed240e317 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -35,6 +35,9 @@
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -121,6 +124,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token AVG_SYM
%token BEGIN_SYM
%token BINLOG_SYM
+%token CALL_SYM
%token CHANGE
%token CLIENT_SYM
%token COMMENT_SYM
@@ -198,10 +202,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token COLUMNS
%token COLUMN_SYM
%token CONCURRENT
+%token CONNECTION_SYM
%token CONSTRAINT
%token CONVERT_SYM
%token DATABASES
%token DATA_SYM
+%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
@@ -247,6 +253,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token INFILE
%token INNER_SYM
%token INNOBASE_SYM
+%token INOUT_SYM
%token INTO
%token IN_SYM
%token ISOLATION
@@ -261,6 +268,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token LIKE
%token LINES
%token LOCAL_SYM
+%token LOCATOR_SYM
%token LOG_SYM
%token LOGS_SYM
%token LONG_NUM
@@ -304,6 +312,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token OR
%token OR_OR_CONCAT
%token ORDER_SYM
+%token OUT_SYM
%token OUTER
%token OUTFILE
%token DUMPFILE
@@ -343,6 +352,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
+%token SPECIFIC_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
@@ -363,9 +373,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token FUNC_ARG1
%token FUNC_ARG2
%token FUNC_ARG3
-%token UDF_RETURNS_SYM
+%token RETURN_SYM
+%token RETURNS_SYM
%token UDF_SONAME_SYM
-%token UDF_SYM
+%token FUNCTION_SYM
%token UNCOMMITTED_SYM
%token UNDERSCORE_CHARSET
%token UNICODE_SYM
@@ -504,6 +515,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token ROUND
%token SECOND_SYM
%token SHARE_SYM
+%token SP_FUNC
%token SUBSTRING
%token SUBSTRING_INDEX
%token TRIM
@@ -533,6 +545,18 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SQL_SMALL_RESULT
%token SQL_BUFFER_RESULT
+%token CURSOR_SYM
+%token ELSEIF_SYM
+%token ITERATE_SYM
+%token LEAVE_SYM
+%token LOOP_SYM
+%token REPEAT_SYM
+%token UNTIL_SYM
+%token WHILE_SYM
+%token ASENSITIVE_SYM
+%token INSENSITIVE_SYM
+%token SENSITIVE_SYM
+
%token ISSUER_SYM
%token SUBJECT_SYM
%token CIPHER_SYM
@@ -565,6 +589,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
ULONGLONG_NUM field_ident select_alias ident ident_or_text
UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
NCHAR_STRING
+ SP_FUNC ident_or_spfunc
%type <lex_str_ptr>
opt_table_alias
@@ -600,7 +625,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
table_wild opt_pad no_in_expr expr_expr simple_expr no_and_expr
using_list expr_or_default set_expr_or_default interval_expr
param_marker singlerow_subselect singlerow_subselect_init
- exists_subselect exists_subselect_init
+ exists_subselect exists_subselect_init sp_opt_default
%type <item_list>
expr_list udf_expr_list when_list ident_list ident_list_arg
@@ -686,8 +711,12 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
union_clause union_list union_option
precision subselect_start opt_and charset
subselect_end select_var_list select_var_list_init help opt_len
+ statement
END_OF_INPUT
+%type <NONE> call sp_proc_stmts sp_proc_stmt
+%type <num> sp_decls sp_decl sp_decl_idents sp_opt_inout
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM
@@ -713,10 +742,16 @@ query:
| verb_clause END_OF_INPUT {};
verb_clause:
+ statement
+ | begin
+ ;
+
+/* Verb clauses, except begin */
+statement:
alter
| analyze
| backup
- | begin
+ | call
| change
| check
| commit
@@ -885,20 +920,549 @@ create:
lex->name=$4.str;
lex->create_info.options=$3;
}
- | CREATE udf_func_type UDF_SYM IDENT_sys
+ | CREATE udf_func_type FUNCTION_SYM ident_or_spfunc
{
LEX *lex=Lex;
- lex->sql_command = SQLCOM_CREATE_FUNCTION;
lex->udf.name = $4;
lex->udf.type= $2;
}
- UDF_RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
+ create_function_tail
+ {}
+ | CREATE PROCEDURE ident
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
+ YYABORT;
+ }
+ lex->spcont= new sp_pcontext();
+ lex->sphead= new sp_head(&$3, lex);
+ lex->sphead->m_type= TYPE_ENUM_PROCEDURE;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ lex->sphead->m_old_cmq=
+ YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= (~CLIENT_MULTI_QUERIES);
+ }
+ '(' sp_pdparam_list ')'
+ {
+ Lex->spcont->set_params();
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+
+ lex->sql_command= SQLCOM_CREATE_PROCEDURE;
+ /* Restore flag if it was cleared above */
+ if (lex->sphead->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ }
+ ;
+
+ident_or_spfunc:
+ IDENT_sys { $$= $1; }
+ | SP_FUNC { $$= $1; }
+ ;
+
+create_function_tail:
+ RETURNS_SYM udf_type UDF_SONAME_SYM TEXT_STRING_sys
{
LEX *lex=Lex;
- lex->udf.returns=(Item_result) $7;
- lex->udf.dl=$9.str;
+ lex->sql_command = SQLCOM_CREATE_FUNCTION;
+ lex->udf.returns=(Item_result) $2;
+ lex->udf.dl=$4.str;
}
- ;
+ | '('
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
+ YYABORT;
+ }
+ lex->spcont= new sp_pcontext();
+ lex->sphead= new sp_head(&lex->udf.name, lex);
+ lex->sphead->m_type= TYPE_ENUM_FUNCTION;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ lex->sphead->m_old_cmq=
+ YYTHD->client_capabilities & CLIENT_MULTI_QUERIES;
+ YYTHD->client_capabilities &= ~CLIENT_MULTI_QUERIES;
+ }
+ sp_fdparam_list ')'
+ {
+ Lex->spcont->set_params();
+ }
+ RETURNS_SYM type
+ {
+ Lex->sphead->m_returns= (enum enum_field_types)$7;
+ }
+ sp_proc_stmt
+ {
+ LEX *lex= Lex;
+
+ lex->sql_command = SQLCOM_CREATE_SPFUNCTION;
+ /* Restore flag if it was cleared above */
+ if (lex->sphead->m_old_cmq)
+ YYTHD->client_capabilities |= CLIENT_MULTI_QUERIES;
+ }
+ ;
+
+call:
+ CALL_SYM ident_or_spfunc
+ {
+ LEX *lex = Lex;
+
+ lex->sql_command= SQLCOM_CALL;
+ lex->udf.name= $2;
+ lex->value_list.empty();
+ }
+ '(' sp_cparam_list ')' {}
+ ;
+
+/* CALL parameters */
+sp_cparam_list:
+ /* Empty */
+ | sp_cparams
+ ;
+
+sp_cparams:
+ sp_cparams ',' expr
+ {
+ Lex->value_list.push_back($3);
+ }
+ | expr
+ {
+ Lex->value_list.push_back($1);
+ }
+ ;
+
+/* Stored FUNCTION parameter declaration list */
+sp_fdparam_list:
+ /* Empty */
+ | sp_fdparams
+ ;
+
+sp_fdparams:
+ sp_fdparams ',' sp_fdparam
+ | sp_fdparam
+ ;
+
+sp_fdparam:
+ ident type sp_opt_locator
+ {
+ Lex->spcont->push(&$1, (enum enum_field_types)$2, sp_param_in);
+ }
+ ;
+
+/* Stored PROCEDURE parameter declaration list */
+sp_pdparam_list:
+ /* Empty */
+ | sp_pdparams
+ ;
+
+sp_pdparams:
+ sp_pdparams ',' sp_pdparam
+ | sp_pdparam
+ ;
+
+sp_pdparam:
+ sp_opt_inout ident type sp_opt_locator
+ {
+ Lex->spcont->push(&$2,
+ (enum enum_field_types)$3,
+ (sp_param_mode_t)$1);
+ }
+ ;
+
+sp_opt_inout:
+ /* Empty */ { $$= sp_param_in; }
+ | IN_SYM { $$= sp_param_in; }
+ | OUT_SYM { $$= sp_param_out; }
+ | INOUT_SYM { $$= sp_param_inout; }
+ ;
+
+sp_opt_locator:
+ /* Empty */
+ | AS LOCATOR_SYM
+ ;
+
+sp_proc_stmts:
+ sp_proc_stmt ';'
+ | sp_proc_stmts sp_proc_stmt ';'
+ ;
+
+sp_decls:
+ /* Empty */
+ {
+ $$= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ $$= $1 + $2;
+ }
+ ;
+
+sp_decl:
+ DECLARE_SYM sp_decl_idents type sp_opt_default
+ {
+ LEX *lex= Lex;
+ uint max= lex->spcont->current_framesize();
+ enum enum_field_types type= (enum enum_field_types)$3;
+ Item *it= $4;
+
+ for (uint i = max-$2 ; i < max ; i++)
+ {
+ lex->spcont->set_type(i, type);
+ if (! it)
+ lex->spcont->set_isset(i, FALSE);
+ else
+ {
+ sp_instr_set *in= new sp_instr_set(lex->sphead->instructions(),
+ i, it, type);
+
+ lex->sphead->add_instr(in);
+ lex->spcont->set_isset(i, TRUE);
+ }
+ }
+ $$= $2;
+ }
+ ;
+
+sp_decl_idents:
+ ident
+ {
+ Lex->spcont->push(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
+ Lex->spcont->push(&$3, (enum_field_types)0, sp_param_in);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_opt_default:
+ /* Empty */ { $$ = NULL; }
+ | DEFAULT expr { $$ = $2; }
+ ;
+
+sp_proc_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if (lex->sql_command == SQLCOM_SELECT && !lex->result)
+ {
+ /* We maybe have one or more SELECT without INTO */
+ lex->sphead->m_multi_query= TRUE;
+ }
+ /* Don't add an instruction for empty SET statements.
+ ** (This happens if the SET only contained local variables,
+ ** which get their set instructions generated separately.)
+ */
+ if (lex->sql_command != SQLCOM_SET_OPTION ||
+ !lex->var_list.is_empty())
+ {
+ /* Currently we can't handle queries inside a FUNCTION,
+ ** because of the way table locking works.
+ ** This is unfortunate, and limits the usefulness of functions
+ ** a great deal, but it's nothing we can do about this at the
+ ** moment.
+ */
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ lex->sql_command != SQLCOM_SET_OPTION)
+ {
+ send_error(YYTHD, ER_SP_BADQUERY);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_stmt *i=new sp_instr_stmt(lex->sphead->instructions());
+
+ i->set_lex(lex);
+ lex->sphead->add_instr(i);
+ }
+ }
+ lex->sphead->restore_lex(YYTHD);
+ }
+ | RETURN_SYM expr
+ {
+ LEX *lex= Lex;
+
+ if (lex->sphead->m_type == TYPE_ENUM_PROCEDURE)
+ {
+ send_error(YYTHD, ER_SP_BADRETURN);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_return *i=
+ new sp_instr_return(lex->sphead->instructions(),
+ $2, lex->sphead->m_returns);
+
+ lex->sphead->add_instr(i);
+ }
+ }
+ | IF sp_if END IF {}
+ | CASE_SYM WHEN_SYM
+ {
+ Lex->sphead->m_simple_case= FALSE;
+ }
+ sp_case END CASE_SYM {}
+ | CASE_SYM expr WHEN_SYM
+ {
+ /* We "fake" this by using an anonymous variable which we
+ set to the expression. Note that all WHENs are evaluate
+ at the same frame level, so we then know that it's the
+ top-most variable in the frame. */
+ LEX *lex= Lex;
+ uint offset= lex->spcont->current_framesize();
+ sp_instr_set *i = new sp_instr_set(lex->sphead->instructions(),
+ offset, $2, MYSQL_TYPE_STRING);
+ LEX_STRING dummy;
+
+ dummy.str= (char *)"";
+ dummy.length= 0;
+ lex->spcont->push(&dummy, MYSQL_TYPE_STRING, sp_param_in);
+ lex->sphead->add_instr(i);
+ lex->sphead->m_simple_case= TRUE;
+ }
+ sp_case END CASE_SYM
+ {
+ Lex->spcont->pop();
+ }
+ | sp_labeled_control
+ {}
+ | { /* Unlabeled controls get a secret label. */
+ LEX *lex= Lex;
+
+ lex->spcont->push_label((char *)"", lex->sphead->instructions());
+ }
+ sp_unlabeled_control
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ | LEAVE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_head *sp = lex->sphead;
+ sp_label_t *lab= lex->spcont->find_label($2.str);
+
+ if (! lab)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "LEAVE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ sp_instr_jump *i= new sp_instr_jump(sp->instructions());
+
+ sp->push_backpatch(i, lab); /* Jumping forward */
+ sp->add_instr(i);
+ }
+ }
+ | ITERATE_SYM IDENT
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($2.str);
+
+ if (! lab)
+ {
+ net_printf(YYTHD, ER_SP_LILABEL_MISMATCH, "ITERATE", $2.str);
+ YYABORT;
+ }
+ else
+ {
+ uint ip= lex->sphead->instructions();
+ sp_instr_jump *i= new sp_instr_jump(ip, lab->ip); /* Jump back */
+
+ lex->sphead->add_instr(i);
+ }
+ }
+ ;
+
+sp_if:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $1);
+
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_elseifs:
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts
+ ;
+
+sp_case:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+
+ if (! sp->m_simple_case)
+ i= new sp_instr_jump_if_not(ip, $1);
+ else
+ { /* Simple case: <caseval> = <whenval> */
+ Item *var= (Item*) new Item_splocal(ctx->current_framesize()-1);
+ Item *expr= Item_bool_func2::eq_creator(var, $1);
+
+ i= new sp_instr_jump_if_not(ip, expr);
+ }
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_whens
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_whens:
+ /* Empty */ {}
+ | WHEN_SYM sp_case {}
+ | ELSE sp_proc_stmts {}
+ ;
+
+sp_labeled_control:
+ IDENT ':'
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($1.str);
+
+ if (lab)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_REDEFINE, $1.str);
+ YYABORT;
+ }
+ else
+ {
+ lex->spcont->push_label($1.str,
+ lex->sphead->instructions());
+ }
+ }
+ sp_unlabeled_control IDENT
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($5.str);
+
+ if (!lab ||
+ my_strcasecmp(system_charset_info, $5.str, lab->name) != 0)
+ {
+ net_printf(YYTHD, ER_SP_LABEL_MISMATCH, $5.str);
+ YYABORT;
+ }
+ else
+ {
+ lex->spcont->pop_label();
+ lex->sphead->backpatch(lab);
+ }
+ }
+ ;
+
+sp_unlabeled_control:
+ BEGIN_SYM
+ sp_decls
+ sp_proc_stmts
+ END
+ { /* QQ This is just a dummy for grouping declarations and statements
+ together. No [[NOT] ATOMIC] yet, and we need to figure out how
+ make it coexist with the existing BEGIN COMMIT/ROLLBACK. */
+ Lex->spcont->pop($2);
+ }
+ | LOOP_SYM
+ sp_proc_stmts END LOOP_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | WHILE_SYM expr DO_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $2);
+
+ /* Jumping forward */
+ sp->push_backpatch(i, lex->spcont->last_label());
+ sp->add_instr(i);
+ }
+ sp_proc_stmts END WHILE_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump *i = new sp_instr_jump(ip, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ | REPEAT_SYM sp_proc_stmts UNTIL_SYM expr END REPEAT_SYM
+ {
+ LEX *lex= Lex;
+ uint ip= lex->sphead->instructions();
+ sp_label_t *lab= lex->spcont->last_label(); /* Jumping back */
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $4, lab->ip);
+
+ lex->sphead->add_instr(i);
+ }
+ ;
create2:
'(' field_list ')' opt_create_table_options create3 {}
@@ -1546,8 +2110,30 @@ alter:
LEX *lex=Lex;
lex->sql_command=SQLCOM_ALTER_DB;
lex->name=$3.str;
- };
+ }
+ | ALTER PROCEDURE ident
+ /* QQ Characteristics missing for now */
+ opt_restrict
+ {
+ LEX *lex=Lex;
+ /* This is essensially an no-op right now, since we haven't
+ put the characteristics in yet. */
+ lex->sql_command= SQLCOM_ALTER_PROCEDURE;
+ lex->udf.name= $3;
+ }
+ | ALTER FUNCTION_SYM ident
+ /* QQ Characteristics missing for now */
+ opt_restrict
+ {
+ LEX *lex=Lex;
+
+ /* This is essensially an no-op right now, since we haven't
+ put the characteristics in yet. */
+ lex->sql_command= SQLCOM_ALTER_FUNCTION;
+ lex->udf.name= $3;
+ }
+ ;
alter_list:
| alter_list_item
@@ -2235,6 +2821,8 @@ simple_expr:
{ $$= ((Item*(*)(Item*,Item*))($1.symbol->create_func))($3,$5);}
| FUNC_ARG3 '(' expr ',' expr ',' expr ')'
{ $$= ((Item*(*)(Item*,Item*,Item*))($1.symbol->create_func))($3,$5,$7);}
+ | REPEAT_SYM '(' expr ',' expr ')'
+ { $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
@@ -2472,6 +3060,14 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
+ | SP_FUNC '(' udf_expr_list ')'
+ {
+ sp_add_fun_to_lex(Lex, $1);
+ if ($3)
+ $$= new Item_func_sp($1, *$3);
+ else
+ $$= new Item_func_sp($1);
+ }
| UDA_CHAR_SUM '(' udf_expr_list ')'
{
if ($3 != NULL)
@@ -3127,11 +3723,33 @@ select_var_list:
| select_var_ident {}
;
-select_var_ident: '@' ident_or_text
+select_var_ident:
+ '@' ident_or_text
+ {
+ LEX *lex=Lex;
+ if (lex->result)
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0));
+ else
+ YYABORT;
+ }
+ | ident_or_text
{
LEX *lex=Lex;
- if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING))))
+ if (!lex->spcont)
+ YYABORT;
+ sp_pvar_t *t;
+ if (!(t=lex->spcont->find_pvar(&$1)))
+ {
+ send_error(lex->thd, ER_SYNTAX_ERROR);
YYABORT;
+ }
+ if (! lex->result)
+ YYABORT;
+ else
+ {
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset));
+ t->isset= TRUE;
+ }
}
;
@@ -3212,13 +3830,21 @@ drop:
lex->drop_if_exists=$3;
lex->name=$4.str;
}
- | DROP UDF_SYM IDENT_sys
+ | DROP FUNCTION_SYM if_exists IDENT_sys opt_restrict
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_FUNCTION;
- lex->udf.name = $3;
- };
-
+ lex->drop_if_exists= $3;
+ lex->udf.name= $4;
+ }
+ | DROP PROCEDURE if_exists IDENT_sys opt_restrict
+ {
+ LEX *lex=Lex;
+ lex->sql_command = SQLCOM_DROP_PROCEDURE;
+ lex->drop_if_exists= $3;
+ lex->udf.name= $4;
+ }
+ ;
table_list:
table_name
@@ -3274,7 +3900,6 @@ replace:
}
insert_field_spec
{}
- {}
;
insert_lock_option:
@@ -3814,18 +4439,23 @@ purge_option:
/* kill threads */
kill:
- KILL_SYM expr
+ KILL_SYM kill_option expr
{
LEX *lex=Lex;
- if ($2->fix_fields(lex->thd, 0, &$2) || $2->check_cols(1))
+ if ($3->fix_fields(lex->thd, 0, &$3) || $3->check_cols(1))
{
send_error(lex->thd, ER_SET_CONSTANTS_ONLY);
YYABORT;
}
lex->sql_command=SQLCOM_KILL;
- lex->thread_id= (ulong) $2->val_int();
+ lex->thread_id= (ulong) $3->val_int();
};
+kill_option:
+ /* empty */ { Lex->type= 0; }
+ | CONNECTION_SYM { Lex->type= 0; }
+ | QUERY_SYM { Lex->type= ONLY_KILL_QUERY; };
+
/* change database */
use: USE_SYM ident
@@ -4011,8 +4641,25 @@ order_ident:
simple_ident:
ident
{
- SELECT_LEX_NODE *sel=Select;
- $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str);
+ sp_pvar_t *spv;
+ LEX *lex = Lex;
+ sp_pcontext *spc = lex->spcont;
+
+ if (spc && (spv = spc->find_pvar(&$1)))
+ { /* We're compiling a stored procedure and found a variable */
+ if (lex->sql_command != SQLCOM_CALL && ! spv->isset)
+ {
+ net_printf(YYTHD, ER_SP_UNINIT_VAR, $1.str);
+ YYABORT;
+ }
+ else
+ $$ = (Item*) new Item_splocal(spv->offset);
+ }
+ else
+ {
+ SELECT_LEX_NODE *sel=Select;
+ $$ = !sel->create_refs || sel->get_in_sum_expr() > 0 ? (Item*) new Item_field(NullS,NullS,$1.str) : (Item*) new Item_ref(NullS,NullS,$1.str);
+ }
}
| ident '.' ident
{
@@ -4325,7 +4972,7 @@ keyword:
| TIMESTAMP {}
| TIME_SYM {}
| TYPE_SYM {}
- | UDF_SYM {}
+ | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| USE_FRM {}
@@ -4380,15 +5027,12 @@ opt_var_ident_type:
;
option_value:
- '@' ident_or_text equal expr
- {
- Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
- }
- | internal_variable_name equal set_expr_or_default
+ '@' ident_or_text equal expr
{
- LEX *lex=Lex;
- lex->var_list.push_back(new set_var(lex->option_type, $1, $3));
+ Lex->var_list.push_back(new set_var_user(new Item_func_set_user_var($2,$4)));
}
+ | internal_or_splocal
+ {}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
LEX *lex=Lex;
@@ -4453,6 +5097,32 @@ internal_variable_name:
}
;
+internal_or_splocal:
+ ident equal set_expr_or_default
+ {
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ { /* Not an SP local variable */
+ sys_var *tmp= find_sys_var($1.str, $1.length);
+
+ if (!tmp)
+ YYABORT;
+ lex->var_list.push_back(new set_var(lex->option_type, tmp, $3));
+ }
+ else
+ { /* An SP local variable */
+ sp_instr_set *i= new sp_instr_set(lex->sphead->instructions(),
+ spv->offset, $3, spv->type);
+
+ lex->sphead->add_instr(i);
+ spv->isset= TRUE;
+ }
+ }
+ ;
+
isolation_types:
READ_SYM UNCOMMITTED_SYM { $$= ISO_READ_UNCOMMITTED; }
| READ_SYM COMMITTED_SYM { $$= ISO_READ_COMMITTED; }