summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.bzrignore4
-rw-r--r--BitKeeper/etc/logging_ok2
-rwxr-xr-xBitKeeper/triggers/post-commit19
-rw-r--r--Docs/sp-imp-spec.txt1055
-rw-r--r--Docs/sp-implemented.txt114
-rw-r--r--client/mysql.cc3
-rw-r--r--client/mysqltest.c27
-rw-r--r--configure.in18
-rw-r--r--include/config-netware.h20
-rw-r--r--include/my_global.h7
-rw-r--r--include/mysql_com.h4
-rw-r--r--include/mysqld_error.h29
-rw-r--r--libmysql/libmysql.c1
-rw-r--r--libmysqld/Makefile.am3
-rw-r--r--libmysqld/lib_sql.cc4
-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/r/connect.result3
-rw-r--r--mysql-test/r/distinct.result4
-rw-r--r--mysql-test/r/insert.result13
-rw-r--r--mysql-test/r/query_cache.result24
-rw-r--r--mysql-test/r/show_check.result1
-rw-r--r--mysql-test/r/sp-error.result222
-rw-r--r--mysql-test/r/sp.result765
-rw-r--r--mysql-test/r/status.result2
-rw-r--r--mysql-test/r/subselect.result8
-rw-r--r--mysql-test/r/variables.result32
-rw-r--r--mysql-test/t/distinct.test4
-rw-r--r--mysql-test/t/insert.test31
-rw-r--r--mysql-test/t/query_cache.test14
-rw-r--r--mysql-test/t/sp-error.test303
-rw-r--r--mysql-test/t/sp.test886
-rw-r--r--mysql-test/t/subselect.test5
-rw-r--r--mysql-test/t/variables.test16
-rwxr-xr-xnetware/BUILD/compile-netware-END2
-rwxr-xr-xnetware/BUILD/compile-netware-all1
-rwxr-xr-xnetware/BUILD/compile-netware-standard2
-rwxr-xr-xnetware/BUILD/mwenv8
-rwxr-xr-xnetware/BUILD/nwbootstrap13
-rw-r--r--netware/Makefile.am57
-rw-r--r--netware/my_manage.c768
-rw-r--r--netware/my_manage.h34
-rw-r--r--netware/mysql_test_run.c618
-rw-r--r--scripts/make_binary_distribution.sh92
-rw-r--r--scripts/mysql_create_system_tables.sh20
-rw-r--r--scripts/mysql_fix_privilege_tables.sql17
-rw-r--r--sql/Makefile.am5
-rw-r--r--sql/filesort.cc10
-rw-r--r--sql/ha_berkeley.cc2
-rw-r--r--sql/ha_innodb.cc4
-rw-r--r--sql/ha_myisam.cc8
-rw-r--r--sql/item.cc53
-rw-r--r--sql/item.h71
-rw-r--r--sql/item_cmpfunc.cc2
-rw-r--r--sql/item_create.cc14
-rw-r--r--sql/item_func.cc98
-rw-r--r--sql/item_func.h71
-rw-r--r--sql/item_subselect.cc38
-rw-r--r--sql/item_sum.cc8
-rw-r--r--sql/lex.h36
-rw-r--r--sql/lock.cc2
-rw-r--r--sql/log.cc16
-rw-r--r--sql/log_event.cc2
-rw-r--r--sql/mysql_priv.h14
-rw-r--r--sql/mysqld.cc86
-rw-r--r--sql/protocol.cc24
-rw-r--r--sql/protocol.h8
-rw-r--r--sql/protocol_cursor.cc12
-rw-r--r--sql/records.cc2
-rw-r--r--sql/repl_failsafe.cc2
-rw-r--r--sql/set_var.cc30
-rw-r--r--sql/share/czech/errmsg.txt27
-rw-r--r--sql/share/danish/errmsg.txt27
-rw-r--r--sql/share/dutch/errmsg.txt27
-rw-r--r--sql/share/english/errmsg.txt27
-rw-r--r--sql/share/estonian/errmsg.txt27
-rw-r--r--sql/share/french/errmsg.txt27
-rw-r--r--sql/share/german/errmsg.txt27
-rw-r--r--sql/share/greek/errmsg.txt27
-rw-r--r--sql/share/hungarian/errmsg.txt27
-rw-r--r--sql/share/italian/errmsg.txt27
-rw-r--r--sql/share/japanese/errmsg.txt27
-rw-r--r--sql/share/korean/errmsg.txt27
-rw-r--r--sql/share/norwegian-ny/errmsg.txt27
-rw-r--r--sql/share/norwegian/errmsg.txt27
-rw-r--r--sql/share/polish/errmsg.txt27
-rw-r--r--sql/share/portuguese/errmsg.txt29
-rw-r--r--sql/share/romanian/errmsg.txt27
-rw-r--r--sql/share/russian/errmsg.txt27
-rw-r--r--sql/share/serbian/errmsg.txt27
-rw-r--r--sql/share/slovak/errmsg.txt27
-rw-r--r--sql/share/spanish/errmsg.txt27
-rw-r--r--sql/share/swedish/errmsg.txt27
-rw-r--r--sql/share/ukrainian/errmsg.txt27
-rw-r--r--sql/slave.cc6
-rw-r--r--sql/sp.cc444
-rw-r--r--sql/sp.h65
-rw-r--r--sql/sp_cache.cc148
-rw-r--r--sql/sp_cache.h101
-rw-r--r--sql/sp_head.cc917
-rw-r--r--sql/sp_head.h631
-rw-r--r--sql/sp_pcontext.cc193
-rw-r--r--sql/sp_pcontext.h242
-rw-r--r--sql/sp_rcontext.cc245
-rw-r--r--sql/sp_rcontext.h252
-rw-r--r--sql/sql_acl.cc43
-rw-r--r--sql/sql_base.cc8
-rw-r--r--sql/sql_cache.cc27
-rw-r--r--sql/sql_class.cc93
-rw-r--r--sql/sql_class.h40
-rw-r--r--sql/sql_db.cc3
-rw-r--r--sql/sql_delete.cc29
-rw-r--r--sql/sql_derived.cc8
-rw-r--r--sql/sql_error.cc2
-rw-r--r--sql/sql_insert.cc64
-rw-r--r--sql/sql_lex.cc26
-rw-r--r--sql/sql_lex.h30
-rw-r--r--sql/sql_load.cc7
-rw-r--r--sql/sql_parse.cc385
-rw-r--r--sql/sql_prepare.cc49
-rw-r--r--sql/sql_rename.cc1
-rw-r--r--sql/sql_repl.cc32
-rw-r--r--sql/sql_repl.h2
-rw-r--r--sql/sql_select.cc43
-rw-r--r--sql/sql_show.cc2
-rw-r--r--sql/sql_table.cc39
-rw-r--r--sql/sql_union.cc22
-rw-r--r--sql/sql_update.cc28
-rw-r--r--sql/sql_yacc.yy1109
-rw-r--r--sql/table.h1
-rw-r--r--sql/udf_example.cc45
-rw-r--r--tests/udf_test3
-rw-r--r--tests/udf_test.res24
135 files changed, 10788 insertions, 1148 deletions
diff --git a/.bzrignore b/.bzrignore
index 27f32040bc8..b9a3c3fd9b4 100644
--- a/.bzrignore
+++ b/.bzrignore
@@ -385,6 +385,10 @@ libmysqld/repl_failsafe.cc
libmysqld/set_var.cc
libmysqld/simple-test
libmysqld/slave.cc
+libmysqld/sp.cc
+libmysqld/sp_cache.cc
+libmysqld/sp_head.cc
+libmysqld/sp_pcontext.cc
libmysqld/spatial.cc
libmysqld/sql_acl.cc
libmysqld/sql_analyse.cc
diff --git a/BitKeeper/etc/logging_ok b/BitKeeper/etc/logging_ok
index 9df789c0340..9a7da4b8537 100644
--- a/BitKeeper/etc/logging_ok
+++ b/BitKeeper/etc/logging_ok
@@ -95,10 +95,12 @@ paul@ice.local
paul@ice.snake.net
paul@teton.kitebird.com
pem@mysql.com
+pem@per-erik-martins-dator.local
peter@linux.local
peter@mysql.com
peterg@mysql.com
pgulutzan@linux.local
+pmartin@build.mysql2.com
ram@gw.mysql.r18.ru
ram@gw.udmsearch.izhnet.ru
ram@mysql.r18.ru
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..6fee125fbea
--- /dev/null
+++ b/Docs/sp-imp-spec.txt
@@ -0,0 +1,1055 @@
+
+ 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 three 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 a minimum of 5 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.
+ - sp_instr_freturn
+ Return a value from a FUNCTION and exit.
+ For condition HANDLERs some special instructions are also needed, see
+ that section below.
+
+ - 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 the internal cache).
+
+
+ - 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.
+
+
+ - A simple 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 in 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 invocation
+
+ 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 invocation; 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 with an in-memory cache, 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.
+
+
+ - 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.
+
+
+ - Condition and Handlers
+
+ Condition names are lexical entities and are kept in the parser context
+ just like variables. But, condition are just "aliases" for SQLSTATE
+ strings, or mysqld error codes (which is a non-standard extension in
+ MySQL), and are only used during parsing.
+
+ Handlers comes in three types, CONTINUE, EXIT and UNDO. The latter is
+ like an EXIT handler with an implicit rollback, and is currently not
+ implemented.
+ The EXIT handler jumps to the end of its BEGIN-END block when finished.
+ The CONTINUE handler returns to the statement following that which
+ invoked the handler.
+
+ The handlers in effect at any point is part of each thread's runtime
+ state, so we need to push and pop handlers in the sp_rcontext during
+ execution. We use special instructions for this:
+ - sp_instr_hpush_jump
+ Push a handler. The instruction contains the necessary information,
+ like which conditions we handle and the location of the handler.
+ The jump takes us to the location after the handler code.
+ - sp_instr_hpop
+ Pop the handlers of the current frame (which we are just leaving).
+
+ It might seems strange to jump past the handlers like that, but there's
+ no extra cost in doing this, and for technical reasons it's easiest for
+ the parser to generate the handler instructions when they occur in the
+ source.
+
+ When an error occurs, one of the error routines is called and an error
+ message is normally sent back to the client immediately.
+ Catching a condition must be done in these error routines (there are
+ quite a few) to prevent them from doing this. We do this by calling
+ a method in the THD's sp_rcontext (if there is one). If a handler is
+ found, this is recorded in the context and the routine returns without
+ sending the error message.
+ The exectution loop (sp_head::execute()) checks for this after each
+ statement and invokes the handler that has been found. If several
+ errors or warnings occurs during one statement, only the first is
+ caught, the rest are ignored.
+
+ Invoking and returning from a handler is trivial in the EXIT case.
+ We simply jump to it, and it will have an sp_instr_jump as its last
+ instruction.
+
+ Calling and returning from a CONTINUE handler poses some special
+ problems. Since we need to return to the point after its invokation,
+ we push the return location on a stack in the sp_rcontext (this is
+ done by the exectution loop). The handler then ends with a special
+ instruction, sp_instr_hreturn, which returns to this location.
+
+ CONTINUE handlers have one additional problem: They are parsed at
+ the lexical level where they occur, so variable offsets will assume
+ that it's actually called at that level. However, a handler might be
+ invoked from a sub-block where additional local variables have been
+ declared, which will then share the location of any local variables
+ in the handler itself. So, when calling a CONTINUE handler, we need
+ to save any local variables above the handler's frame offset, and
+ restore them upon return. (This is not a problem for EXIT handlers,
+ since they will leave the block anyway.)
+ This is taken care of by the execution loop and the sp_instr_hreturn
+ instruction.
+
+ - Examples:
+
+ - EXIT handler
+ begin
+ declare x int default 0;
+
+ begin
+ declare exit handler for 'XXXXX' set x = 1;
+
+ (statement1);
+ (statement2);
+ end;
+ (statement3);
+ end
+
+ Pos. Instruction
+ 0 sp_instr_set(0, '0')
+ 1 sp_instr_hpush_jump(4, 1) # location and frame size
+ 2 sp_instr_set(0, '1')
+ 3 sp_instr_jump(6)
+ 4 sp_instr_stmt('statement1')
+ 5 sp_instr_stmt('statement2')
+ 6 sp_instr_hpop(1)
+ 7 sp_instr_stmt('statement3')
+
+ - CONTINUE handler
+ create procedure hndlr1(val int)
+ begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare continue handler for foo set x = 1;
+
+ insert into t3 values ("hndlr1", val); # Non-existing table?
+ if x>0 then
+ insert into t1 values ("hndlr1", val); # This instead then
+ end if;
+ end|
+
+ Pos. Instruction
+ 0 sp_instr_set(1, '0')
+ 1 sp_instr_hpush_jump(4, 2)
+ 2 sp_instr_set(1, '1')
+ 3 sp_instr_hreturn(2) # frame size
+ 4 sp_instr_stmt('insert ... t3 ...')
+ 5 sp_instr_jump_if_not(7, 'x>0')
+ 6 sp_instr_stmt('insert ... t1 ...')
+ 7 sp_instr_hpop(2)
+
+
+ - Cursors
+
+ For stored procedures to be really useful, you want to have cursors.
+ MySQL doesn't yet have "real" cursor support (with API and ODBC support,
+ allowing updating, arbitrary scrolling, etc), but a simple asensitive,
+ non-scrolling, read-only cursor can be implemented in SPs using the
+ class Protocol_cursor.
+ This class intecepts the creation and sending of results sets and instead
+ stores it in-memory, as MYSQL_FIELDS and MYSQL_ROWS (as in the client API).
+
+ To support this, we need the usual name binding support in sp_pcontext
+ (similar to variables and conditions) to keep track on declared cursor
+ names, and a corresponding run-time mechanism in sp_rcontext.
+ Cursors are lexically scoped like everything with a body or BEGIN/END
+ block, so they are pushed and poped as usual (see conditions and variables
+ above).
+ The basic operations on a cursor are OPEN, FETCH and CLOSE, which will
+ each have a corresponding instruction. In addition, we need instructions
+ to push a new cursor (this will encapsulate the LEX of the SELECT statement
+ of the cursor), and a pop instruction:
+ - sp_instr_cpush
+ Push a cursor to the sp_rcontext. This instruction contains the LEX
+ for the select statement
+ - sp_instr_cpop
+ Pop a number of cursors from the sp_rcontext.
+ - sp_instr_copen
+ Open a cursor: This will execute the select and get the result set
+ in a sepeate memroot.
+ - sp_instr_cfetch
+ Fetch the next row from the in-memory result set. The instruction
+ contains a list of the variables (frame offsets) to set.
+ - sp_instr_cclose
+ Free the result set.
+
+ A cursor is a separate class, sp_cursor (defined in sp_rcontex.h) which
+ encapsulates the basic operations used by the above instructions.
+ This class contains the LEX, Protocol_cursor object, and its memroot,
+ as well as the cursor's current state.
+ Compiling and executing is fairly straight-forward. sp_instr_copen is
+ a subclass of sp_instr_stmt and uses its mechanism to execute a
+ substatement.
+
+ - Example:
+
+ begin
+ declare x int;
+ declare c cursor for select a from t1;
+
+ open c;
+ fetch c into x;
+ close c;
+ end
+
+ Pos. Instruction
+ 0 sp_instr_cpush('select a from ...')
+ 1 sp_instr_copen(0) # The 0'th cursor
+ 2 sp_instr_cfetch(0) # Contains the variable list
+ 3 sp_instr_cclose(0)
+ 4 sp_instr_cpop(1)
+
+
+
+ - The SP cache
+
+ There are two ways to cache SPs:
+
+ 1) one global cache, share by all threads/connections,
+ 2) one cache per thread.
+
+ There are pros and cons with both methods:
+
+ 1) Pros: Save memory, each SP only read from table once,
+ Cons: Needs locking (= serialization at access), requires thread-safe
+ data structures,
+ 2) Pros: Fast, no locking required (almost), limited thread-safe
+ requirement,
+ Cons: Uses more memory, each SP read from table once per thread.
+
+ Unfortunately, we cannot use alternative 1 for the time being, as most
+ of the datastructures to be cached (lex and items) are not reentrant
+ and thread-safe. (Things are modifed at execution, we have THD pointers
+ stored everywhere, etc.)
+ This leaves us with alternative 2, one cache per thread; or actually
+ two, since we keep FUNCTIONs and PROCEDUREs in separate caches.
+ This is not that terrible; the only case when it will perform
+ significantly worse than a global cache is when we have an application
+ where new threads are connecting, calling a procedure, and disconnecting,
+ over and over again.
+
+ The cache implementation itself is simple and straightforward, a hashtable
+ wrapped in a class and a C API (see APIs below).
+
+ There is however one issue with multiple caches: dropping and altering
+ procedures. Normally, this should be a very rare event in a running
+ system; it's typically something you do during development and testing,
+ so it's not unthinkable that we would simply ignore the issue and let
+ any threads running with a cached version of an SP keep doing so until
+ its disconnected.
+ But assuming we want to keep the caches consistent with respect to drop
+ and alter, it can be done:
+
+ 1) A global counter is needed, initialized to 0 at start.
+ 2) At each DROP or ALTER, increase the counter by one.
+ 3) Each cache has its own copy of the counter, copied at the last read.
+ 4) When looking up a name in the cache, first check if the global counter
+ is larger than the local copy.
+ If so, clear the cache and return "not found", and update the local
+ counter; otherwise, lookup as usual.
+
+ This minimizes the cost to a single brief lock for the access of an
+ integer when operating normally. Only in the event of an actual drop or
+ alter, is the cache cleared. This may seem to be drastic, but since we
+ assume that this is a rare event, it's not a problem.
+ It would of course be possible to have a much more fine-grained solution,
+ keeping track of each SP, but the overhead of doing so is not worth the
+ effort.
+
+
+ - Class and function APIs
+ This is an outline of the key types. Some types and other details
+ in the actual files have been omitted for readability.
+
+ - The parser context: sp_pcontext.h
+
+ 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_cond_type
+ {
+ enum { number, state, warning, notfound, exception } type;
+ char sqlstate[6];
+ uint mysqlerr;
+ } sp_cond_type_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_var(LEX_STRING *name,
+ enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop 'num' variables from the frame.
+ void pop_var(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();
+
+ // Push a condition to the context
+ void push_cond(LEX_STRING *name, sp_cond_type_t *val);
+
+ // Pop a 'num' condition from the context
+ void pop_cond(uint num);
+
+ // Find a condition in the context
+ sp_cond_type_t *find_cond(LEX_STRING *name);
+
+ // Increase the handler count
+ void add_handler();
+
+ // Returns the handler count
+ uint handlers();
+
+ // Push a cursor
+ void push_cursor(LEX_STRING *name);
+
+ // Find a cursor
+ my_bool find_cursor(LEX_STRING *name, uint *poff);
+
+ // Pop 'num' cursors
+ void pop_cursor(uint num);
+
+ // Return the number of cursors
+ uint cursors();
+ }
+
+
+ - The run-time context (call frame): sp_rcontext.h
+
+ #define SP_HANDLER_NONE 0
+ #define SP_HANDLER_EXIT 1
+ #define SP_HANDLER_CONTINUE 2
+ #define SP_HANDLER_UNDO 3
+
+ typedef struct
+ {
+ struct sp_cond_type *cond;
+ uint handler; // Location of handler
+ int type;
+ uint foffset; // Frame offset for the handlers declare level
+ } sp_handler_t;
+
+ class sp_rcontext
+ {
+ // 'fsize' is the max size of the context, 'hmax' the number of handlers,
+ // 'cmax' the number of cursors
+ sp_rcontext(uint fsize, uint hmax, , uint cmax);
+
+ // 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();
+
+ // Push handler at location 'h' for condition 'cond'. 'f' is the
+ // current variable frame size.
+ void push_handler(sp_cond_type_t *cond, uint h, int type, uint f);
+
+ // Pop 'count' handlers
+ void pop_handlers(uint count);
+
+ // Find a handler for this error. This sets the state for a found
+ // handler in the context. If called repeatedly without clearing,
+ // only the first call's state is kept.
+ int find_handler(uint sql_errno);
+
+ // Returns 1 if a handler has been found, with '*ip' and '*fp' set
+ // to the handler location and frame size respectively.
+ int found_handler(uint *ip, uint *fp);
+
+ // Clear the found handler state.
+ void clear_handler();
+
+ // Push a return address for a CONTINUE handler
+ void push_hstack(uint ip);
+
+ // Pop the CONTINUE handler return stack
+ uint pop_hstack();
+
+ // Save variables from frame index 'fp' and up.
+ void save_variables(uint fp);
+
+ // Restore saved variables from to frame index 'fp' and up.
+ void restore_variables(uint fp);
+
+ // Push a cursor for the statement (lex)
+ void push_cursor(LEX *lex);
+
+ // Pop 'count' cursors
+ void pop_cursors(uint count);
+
+ // Pop all cursors
+ void pop_all_cursors();
+
+ // Get the 'i'th cursor
+ sp_cursor *get_cursor(uint i);
+
+ }
+
+
+ - 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();
+
+ void init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
+
+ // 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 *);
+
+ // Returns the number of instructions.
+ uint instructions();
+
+ // Returns the last instruction
+ sp_instr *last_instruction();
+
+ // 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 *);
+
+ // Returns the SP name (with optional length in '*lenp').
+ char *name(uint *lenp = 0);
+
+ // Returns the result type for a function
+ Item_result result();
+
+ // Sets various attributes
+ void sp_set_info(char *creator, uint creatorlen,
+ longlong created, longlong modified,
+ bool suid, char *comment, uint commentlen);
+ }
+
+
+ - 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_freturn : public sp_instr
+ {
+ // Return the value 'val'
+ sp_instr_freturn(uint ip, Item *val, enum enum_field_types type);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Push a handler and jump
+ class sp_instr_hpush_jump : public sp_instr_jump
+ {
+ // Push handler of type 'htype', with current frame size 'fp'
+ sp_instr_hpush_jump(uint ip, int htype, uint fp);
+
+ int execute(THD *thd, uint *nextp);
+
+ // Add condition for this handler
+ void add_condition(struct sp_cond_type *cond);
+ }
+
+ - Pops handlers
+ class sp_instr_hpop : public sp_instr
+ {
+ // Pop 'count' handlers
+ sp_instr_hpop(uint ip, uint count);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Return from a CONTINUE handler
+ class sp_instr_hreturn : public sp_instr
+ {
+ // Return from handler, and restore variables to 'fp'.
+ sp_instr_hreturn(uint ip, uint fp);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Push a CURSOR
+ class sp_instr_cpush : public sp_instr_stmt
+ {
+ // Push a cursor for statement 'lex'
+ sp_instr_cpush(uint ip, LEX *lex)
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Pop CURSORs
+ class sp_instr_cpop : public sp_instr_stmt
+ {
+ // Pop 'count' cursors
+ sp_instr_cpop(uint ip, uint count)
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Open a CURSOR
+ class sp_instr_copen : public sp_instr_stmt
+ {
+ // Open the 'c'th cursor
+ sp_instr_copen(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Close a CURSOR
+ class sp_instr_cclose : public sp_instr
+ {
+ // Close the 'c'th cursor
+ sp_instr_cclose(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+ }
+
+ - Fetch a row with CURSOR
+ class sp_instr_cfetch : public sp_instr
+ {
+ // Fetch next with the 'c'th cursor
+ sp_instr_cfetch(uint ip, uint c);
+
+ int execute(THD *thd, uint *nextp);
+
+ // Add a target variable for the fetch
+ void add_to_varlist(struct sp_pvar *var);
+ }
+
+
+ - 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,
+ char *comment, uint commentlen, bool suid);
+
+ // 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,
+ char *comment, uint commentlen, bool suid);
+
+ // Drop the function 'name' from the database.
+ int sp_drop_function(THD *, char *name, uint namelen);
+
+
+ - The cache: sp_cache.h
+
+ /* Initialize the SP caching once at startup */
+ void sp_cache_init();
+
+ /* Clear the cache *cp and set *cp to NULL */
+ void sp_cache_clear(sp_cache **cp);
+
+ /* Insert an SP to cache. If **cp points to NULL, it's set to a
+ new cache */
+ void sp_cache_insert(sp_cache **cp, sp_head *sp);
+
+ /* Lookup an SP in cache */
+ sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen);
+
+ /* Remove an SP from cache */
+ void sp_cache_remove(sp_cache **cp, sp_head *sp);
+
+--
diff --git a/Docs/sp-implemented.txt b/Docs/sp-implemented.txt
new file mode 100644
index 00000000000..c9112d75e43
--- /dev/null
+++ b/Docs/sp-implemented.txt
@@ -0,0 +1,114 @@
+Stored Procedures implemented 2003-09-16:
+
+
+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)
+ - SQL-99 COMMIT (related to BEGIN/END)
+ - FOR-loops
+ - CASCADE/RESTRICT for ALTER and DROP
+ - ALTER/DROP METHOD (as it implies User Defined Types)
+ - SIGNAL and RESIGNAL, and UNDO handlers
+
+
+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
+ - Prepared SP caching
+ - CONDITIONs and HANDLERs
+ - Simple read-only CURSORs.
+
+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.
+ - SPs are cached, but with a separate cache for each thread (THD).
+ There are still quite a few non-reentrant constructs in the lexical
+ context which makes sharing prepared SPs impossible. And, even when
+ this is resolved, it's not necessarily the case that it will be faster
+ than a cache per thread. A global cache requires locks, which might
+ become a buttleneck. (It would save memory though.)
+ - CONDITIONs and HANDLERs are implemented, but not the SIGNAL and
+ RESIGNAL statements. (It's unclear if these can be implemented.)
+ The semantics of CONDITIONs is expanded to allow catching MySQL error
+ codes as well. UNDO handlers are not implemented (since we don't have
+ SQL-99 style transaction control yet).
+ - Simple read-only CURSORs are implemented, but not yet any of the
+ optional arguments to DECLARE (SCROLL, SENSITIVE, etc) or FETCH
+ (NEXT, PRIOR, etc). Cursors are ASENSITIVE, READ-ONLY, non-SCROLLing.
+ (The additional syntax will be added for completeness, but for the
+ most part unsupported with the current underlying cursor mechanism.)
+
+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 c93875a55fc..b13f1e6d4bc 100644
--- a/client/mysql.cc
+++ b/client/mysql.cc
@@ -2815,6 +2815,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...)
{
va_list args;
+ NETWARE_YIELD
va_start(args, fmt);
(void) vfprintf(file, fmt, args);
#ifdef OS2
@@ -2828,6 +2829,7 @@ void tee_fprintf(FILE *file, const char *fmt, ...)
void tee_fputs(const char *s, FILE *file)
{
+ NETWARE_YIELD
fputs(s, file);
#ifdef OS2
fflush( file);
@@ -2839,6 +2841,7 @@ void tee_fputs(const char *s, FILE *file)
void tee_puts(const char *s, FILE *file)
{
+ NETWARE_YIELD
fputs(s, file);
fputs("\n", file);
#ifdef OS2
diff --git a/client/mysqltest.c b/client/mysqltest.c
index 6a7f4ab7cac..11ffbb95a3a 100644
--- a/client/mysqltest.c
+++ b/client/mysqltest.c
@@ -91,6 +91,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,
@@ -137,6 +138,8 @@ static const char *embedded_server_groups[] = {
NullS
};
+static char delimiter= DEFAULT_DELIMITER;
+
DYNAMIC_ARRAY q_lines;
#include "sslopt-vars.h"
@@ -215,6 +218,7 @@ Q_REQUIRE_VERSION,
Q_ENABLE_WARNINGS, Q_DISABLE_WARNINGS,
Q_ENABLE_INFO, Q_DISABLE_INFO,
Q_EXEC,
+Q_DELIMITER,
Q_UNKNOWN, /* Unknown command. */
Q_COMMENT, /* Comments, ignored. */
Q_COMMENT_WITH_COMMAND
@@ -286,6 +290,7 @@ const char *command_names[]=
"enable_info",
"disable_info",
"exec",
+ "delimiter",
0
};
@@ -1627,6 +1632,17 @@ 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 read_line(char* buf, int size)
{
@@ -1656,7 +1672,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;
@@ -1696,7 +1712,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;
@@ -1716,7 +1732,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;
@@ -1737,7 +1753,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;
@@ -2609,6 +2625,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 207404a89c5..907b2d75d30 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
@@ -36,7 +36,7 @@ for i in $AVAILABLE_LANGUAGES
do
AVAILABLE_LANGUAGES_ERRORS="$AVAILABLE_LANGUAGES_ERRORS $i/errmsg.sys"
case $host_os in
- netware* | modesto*)
+ netware*)
echo "$i/errmsg.sys: $i/errmsg.txt
\$(top_builddir)/extra/comp_err.linux -C\$(srcdir)/charsets/ \$^ $i/errmsg.sys" \
>> $AVAILABLE_LANGUAGES_ERRORS_RULES
@@ -458,7 +458,7 @@ else
*cygwin*)
FIND_PROC="$PS -e | grep mysqld | grep \" \$\$PID \" > /dev/null"
;;
- *netware* | *modesto*)
+ *netware*)
FIND_PROC=
;;
*)
@@ -1551,7 +1551,7 @@ AC_SUBST(LIBDL)
# System characteristics
case $SYSTEM_TYPE in
- *netware* | *modesto*) ;;
+ *netware*) ;;
*)
AC_SYS_RESTARTABLE_SYSCALLS
;;
@@ -1581,10 +1581,12 @@ else
fi
if expr "$SYSTEM_TYPE" : ".*netware.*" > /dev/null; then
- DEBUG_CFLAGS="$DEBUG_CFLAGS -DDEBUG -sym internal,codeview4"
- DEBUG_CXXFLAGS="$DEBUG_CXXFLAGS -DDEBUG -sym internal,codeview4"
- OPTIMIZE_CFLAGS="$OPTIMIZE_CFLAGS -DNDEBUG"
- OPTIMIZE_CXXFLAGS="$OPTIMIZE_CXXFLAGS -DNDEBUG"
+ DEBUG_CFLAGS="-g -DDEBUG -sym internal,codeview4"
+ DEBUG_CXXFLAGS="-g -DDEBUG -sym internal,codeview4"
+ DEBUG_OPTIMIZE_CC="-DDEBUG"
+ DEBUG_OPTIMIZE_CXX="-DDEBUG"
+ OPTIMIZE_CFLAGS="-O3 -DNDEBUG"
+ OPTIMIZE_CXXFLAGS="-O3 -DNDEBUG"
fi
AC_ARG_WITH(debug,
diff --git a/include/config-netware.h b/include/config-netware.h
index dab365a7127..6a7f6291e26 100644
--- a/include/config-netware.h
+++ b/include/config-netware.h
@@ -14,7 +14,10 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
-/* Defines for netware compatible with MySQL */
+/* Header for NetWare compatible with MySQL */
+
+#ifndef _config_netware_h
+#define _config_netware_h
/* required headers */
#include <unistd.h>
@@ -32,6 +35,10 @@
#include <pthread.h>
#include <termios.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
/* required adjustments */
#undef HAVE_READDIR_R
#undef HAVE_RWLOCK_INIT
@@ -80,6 +87,15 @@
/* do not use the extended time in LibC sys\stat.h */
#define _POSIX_SOURCE
-/* Some macros for portability */
+/* kernal call on NetWare that will only yield if our time slice is up */
+void kYieldIfTimeSliceUp(void);
+/* some macros for portability */
#define set_timespec(ABSTIME,SEC) { (ABSTIME).tv_sec=(SEC); (ABSTIME).tv_nsec=0; }
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _config_netware_h */
+
diff --git a/include/my_global.h b/include/my_global.h
index 637b8d4b371..d98e6ad1dc6 100644
--- a/include/my_global.h
+++ b/include/my_global.h
@@ -73,6 +73,13 @@
#endif
#endif /* _WIN32... */
+/* extra protection against CPU Hogs on NetWare */
+#ifdef __NETWARE__
+ #define NETWARE_YIELD { kYieldIfTimeSliceUp(); }
+#else
+ #define NETWARE_YIELD { }
+#endif
+
/*
The macros below are borrowed from include/linux/compiler.h in the
Linux kernel. Use them to indicate the likelyhood of the truthfulness
diff --git a/include/mysql_com.h b/include/mysql_com.h
index be8db185737..3b7369217f5 100644
--- a/include/mysql_com.h
+++ b/include/mysql_com.h
@@ -130,6 +130,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;
@@ -283,6 +285,8 @@ typedef struct st_udf_args
char **args; /* Pointer to argument */
unsigned long *lengths; /* Length of string arguments */
char *maybe_null; /* Set to 1 for all maybe_null args */
+ char **attributes; /* Pointer to attribute name */
+ unsigned long *attribute_lengths; /* Length of attribute arguments */
} UDF_ARGS;
/* This holds information about the result */
diff --git a/include/mysqld_error.h b/include/mysqld_error.h
index 53714505fc2..f12cdda257a 100644
--- a/include/mysqld_error.h
+++ b/include/mysqld_error.h
@@ -296,4 +296,31 @@
#define ER_MISSING_SKIP_SLAVE 1277
#define ER_UNTIL_COND_IGNORED 1278
#define ER_WRONG_INDEX_NAME 1279
-#define ER_ERROR_MESSAGES 280
+#define ER_SP_NO_RECURSIVE_CREATE 1280
+#define ER_SP_ALREADY_EXISTS 1281
+#define ER_SP_DOES_NOT_EXIST 1282
+#define ER_SP_DROP_FAILED 1283
+#define ER_SP_STORE_FAILED 1284
+#define ER_SP_LILABEL_MISMATCH 1285
+#define ER_SP_LABEL_REDEFINE 1286
+#define ER_SP_LABEL_MISMATCH 1287
+#define ER_SP_UNINIT_VAR 1288
+#define ER_SP_BADSELECT 1289
+#define ER_SP_BADRETURN 1290
+#define ER_SP_BADQUERY 1291
+#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1292
+#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1293
+#define ER_QUERY_INTERRUPTED 1294
+#define ER_SP_WRONG_NO_OF_ARGS 1295
+#define ER_SP_COND_MISMATCH 1296
+#define ER_SP_NORETURN 1297
+#define ER_SP_NORETURNEND 1298
+#define ER_SP_BAD_CURSOR_QUERY 1299
+#define ER_SP_BAD_CURSOR_SELECT 1300
+#define ER_SP_CURSOR_MISMATCH 1301
+#define ER_SP_CURSOR_ALREADY_OPEN 1302
+#define ER_SP_CURSOR_NOT_OPEN 1303
+#define ER_SP_UNDECLARED_VAR 1304
+#define ER_SP_WRONG_NO_OF_FETCH_ARGS 1305
+#define ER_SP_FETCH_NO_DATA 1306
+#define ER_ERROR_MESSAGES 307
diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c
index 1cad02948e1..14f2e6d2090 100644
--- a/libmysql/libmysql.c
+++ b/libmysql/libmysql.c
@@ -3157,6 +3157,7 @@ MYSQL_DATA * STDCALL cli_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 09b03bce79a..dbbdd3f191e 100644
--- a/libmysqld/Makefile.am
+++ b/libmysqld/Makefile.am
@@ -57,7 +57,8 @@ sqlsources = derror.cc field.cc field_conv.cc filesort.cc \
sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \
sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \
unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \
- spatial.cc gstream.cc sql_help.cc
+ spatial.cc gstream.cc sql_help.cc protocol_cursor.cc \
+ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc
libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources)
libmysqld_a_SOURCES=
diff --git a/libmysqld/lib_sql.cc b/libmysqld/lib_sql.cc
index d4cb916a89c..87f24f807cc 100644
--- a/libmysqld/lib_sql.cc
+++ b/libmysqld/lib_sql.cc
@@ -754,7 +754,7 @@ bool Protocol::convert_str(const char *from, uint length)
bool setup_params_data(st_prep_stmt *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
ulong param_no= 0;
@@ -787,7 +787,7 @@ bool setup_params_data(st_prep_stmt *stmt)
bool setup_params_data_withlog(st_prep_stmt *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
MYSQL_BIND *client_param= thd->client_params;
diff --git a/myisam/mi_check.c b/myisam/mi_check.c
index ceac8f6356c..b23f152b112 100644
--- a/myisam/mi_check.c
+++ b/myisam/mi_check.c
@@ -2628,7 +2628,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 76d3205dc01..f280c34352e 100644
--- a/myisam/myisamchk.c
+++ b/myisam/myisamchk.c
@@ -1657,9 +1657,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 9844bb7b36d..faec560c763 100644
--- a/myisam/myisamdef.h
+++ b/myisam/myisamdef.h
@@ -695,7 +695,7 @@ int mi_open_keyfile(MYISAM_SHARE *share);
void mi_setup_functions(register MYISAM_SHARE *share);
/* Functions needed by mi_check */
-volatile bool *killed_ptr(MI_CHECK *param);
+int *killed_ptr(void *thd);
void mi_check_print_error _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_warning _VARARGS((MI_CHECK *param, const char *fmt,...));
void mi_check_print_info _VARARGS((MI_CHECK *param, const char *fmt,...));
diff --git a/myisam/sort.c b/myisam/sort.c
index a41713f750f..4617dec46f4 100644
--- a/myisam/sort.c
+++ b/myisam/sort.c
@@ -848,7 +848,8 @@ merge_buffers(MI_SORT_PARAM *info, uint keys, IO_CACHE *from_file,
uchar *strpos;
BUFFPEK *buffpek,**refpek;
QUEUE queue;
- volatile bool *killed= killed_ptr(info->sort_info->param);
+ int *killed= killed_ptr(info->sort_info->param->thd);
+
DBUG_ENTER("merge_buffers");
count=error=0;
diff --git a/mysql-test/r/connect.result b/mysql-test/r/connect.result
index c0608af0de2..635c9fd5242 100644
--- a/mysql-test/r/connect.result
+++ b/mysql-test/r/connect.result
@@ -8,6 +8,7 @@ help_keyword
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
@@ -24,6 +25,7 @@ help_keyword
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
@@ -41,6 +43,7 @@ help_keyword
help_relation
help_topic
host
+proc
tables_priv
user
show tables;
diff --git a/mysql-test/r/distinct.result b/mysql-test/r/distinct.result
index d9beed25edf..1f1850183b7 100644
--- a/mysql-test/r/distinct.result
+++ b/mysql-test/r/distinct.result
@@ -457,10 +457,10 @@ drop table t1,t2;
CREATE TABLE t1 (
html varchar(5) default NULL,
rin int(11) default '0',
-out int(11) default '0'
+rout int(11) default '0'
) TYPE=MyISAM;
INSERT INTO t1 VALUES ('1',1,0);
-SELECT DISTINCT html,SUM(out)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
+SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
html prod
1 0.00
drop table t1;
diff --git a/mysql-test/r/insert.result b/mysql-test/r/insert.result
index 23eef0b3c59..d6d99d7ce98 100644
--- a/mysql-test/r/insert.result
+++ b/mysql-test/r/insert.result
@@ -86,3 +86,16 @@ use mysqltest;
create table t1 (c int);
insert into mysqltest.t1 set mysqltest.t1.c = '1';
drop database mysqltest;
+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/query_cache.result b/mysql-test/r/query_cache.result
index a6aca2f983c..3cc0b3e9852 100644
--- a/mysql-test/r/query_cache.result
+++ b/mysql-test/r/query_cache.result
@@ -756,3 +756,27 @@ show status like "Qcache_queries_in_cache";
Variable_name Value
Qcache_queries_in_cache 4
DROP TABLE t1;
+create table t1 (a int);
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 0
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 44
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 12
+/**/ select * from t1;
+a
+/**/ select * from t1;
+a
+show status like "Qcache_queries_in_cache";
+Variable_name Value
+Qcache_queries_in_cache 1
+show status like "Qcache_inserts";
+Variable_name Value
+Qcache_inserts 45
+show status like "Qcache_hits";
+Variable_name Value
+Qcache_hits 13
+drop table t1;
diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result
index 2a5955e6d87..d112bfb1f36 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..ef8b2ac1552
--- /dev/null
+++ b/mysql-test/r/sp-error.result
@@ -0,0 +1,222 @@
+delete from mysql.proc;
+create procedure syntaxerror(t int);
+ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+create procedure syntaxerror(t int);
+ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+create procedure syntaxerror(t int);
+ERROR 42000: You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near '' at line 1
+create procedure proc1()
+set @x = 42;
+create function func1() returns int
+return 42;
+create procedure foo()
+create procedure bar() set @x=3;
+ERROR HY000: Can't create a PROCEDURE from within another stored routine
+create procedure foo()
+create function bar() returns double return 2.3;
+ERROR HY000: Can't create a FUNCTION from within another stored routine
+create procedure proc1()
+set @x = 42;
+ERROR HY000: PROCEDURE proc1 already exists
+create function func1() returns int
+return 42;
+ERROR HY000: FUNCTION func1 already exists
+drop procedure proc1;
+drop function func1;
+alter procedure foo;
+ERROR HY000: PROCEDURE foo does not exist
+alter function foo;
+ERROR HY000: FUNCTION foo does not exist
+drop procedure foo;
+ERROR HY000: PROCEDURE foo does not exist
+drop function foo;
+ERROR HY000: FUNCTION foo does not exist
+call foo();
+ERROR HY000: PROCEDURE foo does not exist
+drop procedure if exists foo;
+Warnings:
+Warning 1281 PROCEDURE foo does not exist
+create procedure foo()
+foo: loop
+leave bar;
+end loop;
+ERROR HY000: LEAVE with no matching label: bar
+create procedure foo()
+foo: loop
+iterate bar;
+end loop;
+ERROR HY000: ITERATE with no matching label: bar
+create procedure foo()
+foo: loop
+foo: loop
+set @x=2;
+end loop foo;
+end loop foo;
+ERROR HY000: Redefining label foo
+create procedure foo()
+foo: loop
+set @x=2;
+end loop bar;
+ERROR HY000: End-label bar without match
+create procedure foo(out x int)
+begin
+declare y int;
+set x = y;
+end;
+ERROR HY000: Referring to uninitialized variable y
+create procedure foo()
+begin
+select name from mysql.proc;
+select type from mysql.proc;
+end;
+call foo();
+ERROR HY000: SELECT in a stored procedure must have INTO
+drop procedure foo;
+create procedure foo()
+return 42;
+ERROR HY000: RETURN is only allowed in a FUNCTION
+create function foo() returns int
+begin
+declare x int;
+select max(c) into x from test.t;
+return x;
+end;
+ERROR HY000: Queries, like SELECT, INSERT, UPDATE (and others), are not allowed in a FUNCTION
+create procedure p(x int)
+insert into test.t1 values (x);
+create function f(x int) returns int
+return x+42;
+call p();
+ERROR HY000: Wrong number of arguments for PROCEDURE p, expected 1, got 0
+call p(1, 2);
+ERROR HY000: Wrong number of arguments for PROCEDURE p, expected 1, got 2
+select f();
+ERROR HY000: Wrong number of arguments for FUNCTION f, expected 1, got 0
+select f(1, 2);
+ERROR HY000: Wrong number of arguments for FUNCTION f, expected 1, got 2
+drop procedure p;
+drop function f;
+create procedure p(val int, out res int)
+begin
+declare x int default 0;
+declare continue handler for foo set x = 1;
+insert into test.t1 values (val);
+if (x) then
+set res = 0;
+else
+set res = 1;
+end if;
+end;
+ERROR HY000: Undefined CONDITION: foo
+create procedure p(val int, out res int)
+begin
+declare x int default 0;
+declare foo condition for 1146;
+declare continue handler for bar set x = 1;
+insert into test.t1 values (val);
+if (x) then
+set res = 0;
+else
+set res = 1;
+end if;
+end;
+ERROR HY000: Undefined CONDITION: bar
+create function f(val int) returns int
+begin
+declare x int;
+set x = val+3;
+end;
+ERROR HY000: No RETURN found in FUNCTION f
+create function f(val int) returns int
+begin
+declare x int;
+set x = val+3;
+if x < 4 then
+return x;
+end if;
+end;
+select f(10);
+ERROR HY000: FUNCTION f ended without RETURN
+drop function f;
+create procedure p()
+begin
+declare c cursor for insert into test.t1 values ("foo", 42);
+open c;
+close c;
+end;
+ERROR HY000: Cursor statement must be a SELECT
+create procedure p()
+begin
+declare x int;
+declare c cursor for select * into x from test.t limit 1;
+open c;
+close c;
+end;
+ERROR HY000: Cursor SELECT must not have INTO
+create procedure p()
+begin
+declare c cursor for select * from test.t;
+open cc;
+close c;
+end;
+ERROR HY000: Undefined CURSOR: cc
+drop table if exists t1;
+create table t1 (val int);
+create procedure p()
+begin
+declare c cursor for select * from test.t1;
+open c;
+open c;
+close c;
+end;
+call p();
+ERROR HY000: Cursor is already open
+drop procedure p;
+create procedure p()
+begin
+declare c cursor for select * from test.t1;
+open c;
+close c;
+close c;
+end;
+call p();
+ERROR HY000: Cursor is not open
+drop procedure p;
+drop table t1;
+drop table if exists t1;
+create table t1 (val int, x float);
+insert into t1 values (42, 3.1), (19, 1.2);
+create procedure p()
+begin
+declare c cursor for select * from t1;
+declare x int;
+open c;
+fetch c into x, y;
+close c;
+end;
+ERROR HY000: Undeclared variable: y
+create procedure p()
+begin
+declare c cursor for select * from t1;
+declare x int;
+open c;
+fetch c into x;
+close c;
+end;
+call p();
+ERROR HY000: Wrong number of FETCH variables
+drop procedure p;
+create procedure p()
+begin
+declare c cursor for select * from t1;
+declare x int;
+declare y float;
+declare z int;
+open c;
+fetch c into x, y, z;
+close c;
+end;
+call p();
+ERROR HY000: Wrong number of FETCH variables
+drop procedure p;
+drop table t1;
diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result
new file mode 100644
index 00000000000..4f9c1ed5f7e
--- /dev/null
+++ b/mysql-test/r/sp.result
@@ -0,0 +1,765 @@
+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 bar;
+call zap(7, @zap);
+select @zap;
+@zap
+8
+drop procedure zap;
+create procedure c1(x int)
+call c2("c", x);
+create procedure c2(s char(16), x int)
+call c3(x, s);
+create procedure c3(x int, s char(16))
+call c4("level", x, s);
+create procedure c4(l char(8), x int, s char(16))
+insert into t1 values (concat(l,s), x);
+call c1(42);
+select * from t1;
+id data
+levelc 42
+delete from t1;
+drop procedure c1;
+drop procedure c2;
+drop procedure c3;
+drop procedure c4;
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+call inc2(x2, y);
+insert into test.t1 values (x1, y);
+end;
+create procedure inc2(x char(16), y int)
+begin
+call inc(y);
+insert into test.t1 values (x, y);
+end;
+create procedure inc(inout io int)
+set io = io + 1;
+call iotest("io1", "io2", 1);
+select * from t1;
+id data
+io2 2
+io1 1
+delete from t1;
+drop procedure iotest;
+drop procedure inc2;
+create procedure incr(inout x int)
+call inc(x);
+select @zap;
+@zap
+8
+call incr(@zap);
+select @zap;
+@zap
+9
+drop procedure inc;
+drop procedure incr;
+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;
+create procedure hndlr1(val int)
+begin
+declare x int default 0;
+declare foo condition for 1146;
+declare continue handler for foo set x = 1;
+insert into test.t666 values ("hndlr1", val); # Non-existing table
+if (x) then
+insert into test.t1 values ("hndlr1", val); # This instead then
+end if;
+end;
+call hndlr1(42);
+select * from t1;
+id data
+hndlr1 42
+delete from t1;
+drop procedure hndlr1;
+create procedure hndlr2(val int)
+begin
+declare x int default 0;
+begin
+declare exit handler for '42S02' set x = 1;
+insert into test.t666 values ("hndlr2", val); # Non-existing table
+end;
+insert into test.t1 values ("hndlr2", x);
+end;
+call hndlr2(42);
+select * from t1;
+id data
+hndlr2 1
+delete from t1;
+drop procedure hndlr2;
+create procedure hndlr3(val int)
+begin
+declare x int default 0;
+declare continue handler for sqlexception # Any error
+begin
+declare z int;
+set z = 2 * val;
+set x = 1;
+end;
+if val < 10 then
+begin
+declare y int;
+set y = val + 10;
+insert into test.t666 values ("hndlr3", y); # Non-existing table
+if x then
+insert into test.t1 values ("hndlr3", y);
+end if;
+end;
+end if;
+end;
+call hndlr3(3);
+select * from t1;
+id data
+hndlr3 13
+delete from t1;
+drop procedure hndlr3;
+drop table if exists t3;
+create table t3 ( id char(16), data int );
+create procedure hndlr4()
+begin
+declare x int default 0;
+declare val int; # No default
+declare continue handler for sqlexception set x=1;
+select data into val from test.t3 where id='z' limit 1; # No hits
+insert into test.t3 values ('z', val);
+end;
+call hndlr4();
+select * from t3;
+id data
+z NULL
+drop table t3;
+drop procedure hndlr4;
+create procedure cur1()
+begin
+declare done int default 0;
+declare continue handler for 1305 set done = 1;
+declare c cursor for select * from test.t2;
+declare a char(16);
+declare b int;
+declare c double;
+open c;
+repeat
+fetch c into a, b, c;
+if not done then
+insert into test.t1 values (a, b+c);
+end if;
+until done end repeat;
+close c;
+end;
+insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14);
+call cur1();
+select * from t1;
+id data
+foo 40
+bar 15
+zap 663
+drop procedure cur1;
+drop table if exists t3;
+create table t3 ( s char(16), i int );
+create procedure cur2()
+begin
+declare done int default 0;
+declare continue handler for 1305 set done = 1;
+declare c1 cursor for select id,data from test.t1;
+declare c2 cursor for select i from test.t2;
+open c1;
+open c2;
+repeat
+begin
+declare a char(16);
+declare b,c int;
+fetch c1 into a, b;
+fetch c2 into c;
+if not done then
+if b < c then
+insert into test.t3 values (a, b);
+else
+insert into test.t3 values (a, c);
+end if;
+end if;
+end;
+until done end repeat;
+close c1;
+close c2;
+end;
+call cur2();
+select * from t3;
+s i
+foo 40
+bar 3
+zap 663
+delete from t1;
+delete from t2;
+drop table t3;
+drop procedure cur2;
+create procedure bug822(a_id char(16), a_data int)
+begin
+declare n int;
+select count(*) into n from t1 where id = a_id and data = a_data;
+if n = 0 then
+insert into t1 (id, data) values (a_id, a_data);
+end if;
+end;
+call bug822('foo', 42);
+call bug822('foo', 42);
+call bug822('bar', 666);
+select * from t1;
+id data
+foo 42
+bar 666
+delete from t1;
+drop procedure bug822;
+create procedure bug1495()
+begin
+declare x int;
+select data into x from t1 order by id limit 1;
+if x > 10 then
+insert into t1 values ("less", x-10);
+else
+insert into t1 values ("more", x+10);
+end if;
+end;
+insert into t1 values ('foo', 12);
+call bug1495();
+delete from t1 where id='foo';
+insert into t1 values ('bar', 7);
+call bug1495();
+delete from t1 where id='bar';
+select * from t1;
+id data
+less 2
+more 17
+delete from t1;
+drop procedure bug1495;
+create procedure bug1547(s char(16))
+begin
+declare x int;
+select data into x from t1 where s = id limit 1;
+if x > 10 then
+insert into t1 values ("less", x-10);
+else
+insert into t1 values ("more", x+10);
+end if;
+end;
+insert into t1 values ("foo", 12), ("bar", 7);
+call bug1547("foo");
+call bug1547("bar");
+select * from t1;
+id data
+foo 12
+bar 7
+less 2
+more 17
+delete from t1;
+drop procedure bug1547;
+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 14a132531ca..3f0dd147c0d 100644
--- a/mysql-test/r/subselect.result
+++ b/mysql-test/r/subselect.result
@@ -622,6 +622,14 @@ x
3
3
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
+select * from t1;
+x
+1
+2
+3
+3
+11
+11
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
ERROR 42S22: Unknown column 'x' in 'field list'
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
diff --git a/mysql-test/r/variables.result b/mysql-test/r/variables.result
index d65b44e7846..709bf2d65eb 100644
--- a/mysql-test/r/variables.result
+++ b/mysql-test/r/variables.result
@@ -1,8 +1,30 @@
drop table if exists t1,t2;
-set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
-select @test,@`select`,@TEST,@not_used;
-@test @`select` @TEST @not_used
-1 2 3 NULL
+set @`test`=1;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+1 1 1 1 1
+set @TEST=2;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+2 2 2 2 2
+set @"tEST"=3;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+3 3 3 3 3
+set @`TeST`=4;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+4 4 4 4 4
+select @`teST`:=5;
+@`teST`:=5
+5
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+@test @`test` @TEST @`TEST` @"teSt"
+5 5 5 5 5
+set @select=2,@t5=1.23456;
+select @`select`,@not_used;
+@`select` @not_used
+2 NULL
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
@test_int @test_double @test_string @test_string2 @select
@@ -322,6 +344,8 @@ set sql_buffer_result=1;
set sql_log_bin=1;
set sql_log_off=1;
set sql_log_update=1;
+Warnings:
+Note 1291 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/distinct.test b/mysql-test/t/distinct.test
index 56c00e7501c..54e50fc7147 100644
--- a/mysql-test/t/distinct.test
+++ b/mysql-test/t/distinct.test
@@ -326,9 +326,9 @@ drop table t1,t2;
CREATE TABLE t1 (
html varchar(5) default NULL,
rin int(11) default '0',
- out int(11) default '0'
+ rout int(11) default '0'
) TYPE=MyISAM;
INSERT INTO t1 VALUES ('1',1,0);
-SELECT DISTINCT html,SUM(out)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
+SELECT DISTINCT html,SUM(rout)/(SUM(rin)+1) as 'prod' FROM t1 GROUP BY rin;
drop table t1;
diff --git a/mysql-test/t/insert.test b/mysql-test/t/insert.test
index 62d277bfad5..2b74a0c800f 100644
--- a/mysql-test/t/insert.test
+++ b/mysql-test/t/insert.test
@@ -87,3 +87,34 @@ use mysqltest;
create table t1 (c int);
insert into mysqltest.t1 set mysqltest.t1.c = '1';
drop database mysqltest;
+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/query_cache.test b/mysql-test/t/query_cache.test
index ed4c5c91c4d..112e3ee4507 100644
--- a/mysql-test/t/query_cache.test
+++ b/mysql-test/t/query_cache.test
@@ -534,3 +534,17 @@ show status like "Qcache_queries_in_cache";
# Keep things tidy
#
DROP TABLE t1;
+
+#
+# comments before command
+#
+create table t1 (a int);
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+/**/ select * from t1;
+/**/ select * from t1;
+show status like "Qcache_queries_in_cache";
+show status like "Qcache_inserts";
+show status like "Qcache_hits";
+drop table t1;
diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test
new file mode 100644
index 00000000000..c075e96cc78
--- /dev/null
+++ b/mysql-test/t/sp-error.test
@@ -0,0 +1,303 @@
+#
+# Stored PROCEDURE error tests
+#
+
+# Make sure we don't have any procedures left.
+delete from mysql.proc;
+
+delimiter |;
+
+# This should give three syntax errors (sometimes crashed; bug #643)
+# (Unfortunately, this is not a 100% test, on some platforms this
+# passed despite the bug.)
+--error 1064
+create procedure syntaxerror(t int)|
+--error 1064
+create procedure syntaxerror(t int)|
+--error 1064
+create procedure syntaxerror(t int)|
+
+# Check that we get the right error, i.e. UDF declaration parses correctly,
+# but foo.so doesn't exist.
+# QQ This generates an error message containing a misleading errno which
+# might vary between systems (it usually doesn't have anything to do with
+# the actual failing dlopen()).
+#--error 1126
+#create function foo returns real soname "foo.so"|
+
+create procedure proc1()
+ set @x = 42|
+
+create function func1() returns int
+ return 42|
+
+# Can't create recursively
+--error 1279
+create procedure foo()
+ create procedure bar() set @x=3|
+--error 1279
+create procedure foo()
+ create function bar() returns double return 2.3|
+
+# Already exists
+--error 1280
+create procedure proc1()
+ set @x = 42|
+--error 1280
+create function func1() returns int
+ return 42|
+
+drop procedure proc1|
+drop function func1|
+
+# Does not exist
+--error 1281
+alter procedure foo|
+--error 1281
+alter function foo|
+--error 1281
+drop procedure foo|
+--error 1281
+drop function foo|
+--error 1281
+call foo()|
+drop procedure if exists foo|
+
+# LEAVE/ITERATE with no match
+--error 1284
+create procedure foo()
+foo: loop
+ leave bar;
+end loop|
+--error 1284
+create procedure foo()
+foo: loop
+ iterate bar;
+end loop|
+
+# Redefining label
+--error 1285
+create procedure foo()
+foo: loop
+ foo: loop
+ set @x=2;
+ end loop foo;
+end loop foo|
+
+# End label mismatch
+--error 1286
+create procedure foo()
+foo: loop
+ set @x=2;
+end loop bar|
+
+# Referring to undef variable
+--error 1287
+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 1288
+call foo()|
+drop procedure foo|
+
+# RETURN in FUNCTION only
+--error 1289
+create procedure foo()
+ return 42|
+
+# Doesn't allow queries in FUNCTIONs (for now :-( )
+--error 1290
+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 1294
+call p()|
+--error 1294
+call p(1, 2)|
+--error 1294
+select f()|
+--error 1294
+select f(1, 2)|
+
+drop procedure p|
+drop function f|
+
+--error 1295
+create procedure p(val int, out res int)
+begin
+ declare x int default 0;
+ declare continue handler for foo set x = 1;
+
+ insert into test.t1 values (val);
+ if (x) then
+ set res = 0;
+ else
+ set res = 1;
+ end if;
+end|
+
+--error 1295
+create procedure p(val int, out res int)
+begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare continue handler for bar set x = 1;
+
+ insert into test.t1 values (val);
+ if (x) then
+ set res = 0;
+ else
+ set res = 1;
+ end if;
+end|
+
+--error 1296
+create function f(val int) returns int
+begin
+ declare x int;
+
+ set x = val+3;
+end|
+
+create function f(val int) returns int
+begin
+ declare x int;
+
+ set x = val+3;
+ if x < 4 then
+ return x;
+ end if;
+end|
+
+--error 1297
+select f(10)|
+
+drop function f|
+
+--error 1298
+create procedure p()
+begin
+ declare c cursor for insert into test.t1 values ("foo", 42);
+
+ open c;
+ close c;
+end|
+
+--error 1299
+create procedure p()
+begin
+ declare x int;
+ declare c cursor for select * into x from test.t limit 1;
+
+ open c;
+ close c;
+end|
+
+--error 1300
+create procedure p()
+begin
+ declare c cursor for select * from test.t;
+
+ open cc;
+ close c;
+end|
+
+--disable_warnings
+drop table if exists t1|
+--enable_warnings
+create table t1 (val int)|
+
+create procedure p()
+begin
+ declare c cursor for select * from test.t1;
+
+ open c;
+ open c;
+ close c;
+end|
+--error 1301
+call p()|
+drop procedure p|
+
+create procedure p()
+begin
+ declare c cursor for select * from test.t1;
+
+ open c;
+ close c;
+ close c;
+end|
+--error 1302
+call p()|
+drop procedure p|
+
+drop table t1|
+
+--disable_warnings
+drop table if exists t1|
+--enable_warnings
+create table t1 (val int, x float)|
+insert into t1 values (42, 3.1), (19, 1.2)|
+
+--error 1303
+create procedure p()
+begin
+ declare c cursor for select * from t1;
+ declare x int;
+
+ open c;
+ fetch c into x, y;
+ close c;
+end|
+
+create procedure p()
+begin
+ declare c cursor for select * from t1;
+ declare x int;
+
+ open c;
+ fetch c into x;
+ close c;
+end|
+--error 1304
+call p()|
+drop procedure p|
+
+create procedure p()
+begin
+ declare c cursor for select * from t1;
+ declare x int;
+ declare y float;
+ declare z int;
+
+ open c;
+ fetch c into x, y, z;
+ close c;
+end|
+--error 1304
+call p()|
+drop procedure p|
+
+drop table t1|
+
+delimiter ;|
diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test
new file mode 100644
index 00000000000..ad5dbe9f25e
--- /dev/null
+++ b/mysql-test/t/sp.test
@@ -0,0 +1,886 @@
+#
+# 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 bar|
+
+# Top-level OUT parameter
+call zap(7, @zap)|
+select @zap|
+
+drop procedure zap|
+
+
+# "Deep" calls...
+create procedure c1(x int)
+ call c2("c", x)|
+create procedure c2(s char(16), x int)
+ call c3(x, s)|
+create procedure c3(x int, s char(16))
+ call c4("level", x, s)|
+create procedure c4(l char(8), x int, s char(16))
+ insert into t1 values (concat(l,s), x)|
+
+call c1(42)|
+select * from t1|
+delete from t1|
+drop procedure c1|
+drop procedure c2|
+drop procedure c3|
+drop procedure c4|
+
+# INOUT test
+create procedure iotest(x1 char(16), x2 char(16), y int)
+begin
+ call inc2(x2, y);
+ insert into test.t1 values (x1, y);
+end|
+
+create procedure inc2(x char(16), y int)
+begin
+ call inc(y);
+ insert into test.t1 values (x, y);
+end|
+
+create procedure inc(inout io int)
+ set io = io + 1|
+
+call iotest("io1", "io2", 1)|
+select * from t1|
+delete from t1|
+drop procedure iotest|
+drop procedure inc2|
+
+# Propagating top-level @-vars
+create procedure incr(inout x int)
+ call inc(x)|
+
+# Before
+select @zap|
+call incr(@zap)|
+# After
+select @zap|
+
+drop procedure inc|
+drop procedure incr|
+
+# 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|
+
+
+#
+# CONDITIONs and HANDLERs
+#
+
+create procedure hndlr1(val int)
+begin
+ declare x int default 0;
+ declare foo condition for 1146;
+ declare continue handler for foo set x = 1;
+
+ insert into test.t666 values ("hndlr1", val); # Non-existing table
+ if (x) then
+ insert into test.t1 values ("hndlr1", val); # This instead then
+ end if;
+end|
+
+call hndlr1(42)|
+select * from t1|
+delete from t1|
+drop procedure hndlr1|
+
+create procedure hndlr2(val int)
+begin
+ declare x int default 0;
+
+ begin
+ declare exit handler for '42S02' set x = 1;
+
+ insert into test.t666 values ("hndlr2", val); # Non-existing table
+ end;
+
+ insert into test.t1 values ("hndlr2", x);
+end|
+
+call hndlr2(42)|
+select * from t1|
+delete from t1|
+drop procedure hndlr2|
+
+
+create procedure hndlr3(val int)
+begin
+ declare x int default 0;
+ declare continue handler for sqlexception # Any error
+ begin
+ declare z int;
+
+ set z = 2 * val;
+ set x = 1;
+ end;
+
+ if val < 10 then
+ begin
+ declare y int;
+
+ set y = val + 10;
+ insert into test.t666 values ("hndlr3", y); # Non-existing table
+ if x then
+ insert into test.t1 values ("hndlr3", y);
+ end if;
+ end;
+ end if;
+end|
+
+call hndlr3(3)|
+select * from t1|
+delete from t1|
+drop procedure hndlr3|
+
+
+# Variables might be uninitialized when using handlers
+# (Otherwise the compiler can detect if a variable is not set, but
+# not in this case.)
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( id char(16), data int )|
+
+create procedure hndlr4()
+begin
+ declare x int default 0;
+ declare val int; # No default
+ declare continue handler for sqlexception set x=1;
+
+ select data into val from test.t3 where id='z' limit 1; # No hits
+
+ insert into test.t3 values ('z', val);
+end|
+
+call hndlr4()|
+select * from t3|
+drop table t3|
+drop procedure hndlr4|
+
+
+#
+# Cursors
+#
+create procedure cur1()
+begin
+ declare done int default 0;
+ declare continue handler for 1305 set done = 1;
+ declare c cursor for select * from test.t2;
+ declare a char(16);
+ declare b int;
+ declare c double;
+
+ open c;
+ repeat
+ fetch c into a, b, c;
+ if not done then
+ insert into test.t1 values (a, b+c);
+ end if;
+ until done end repeat;
+ close c;
+end|
+
+insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14)|
+call cur1()|
+select * from t1|
+drop procedure cur1|
+
+--disable_warnings
+drop table if exists t3|
+--enable_warnings
+create table t3 ( s char(16), i int )|
+
+create procedure cur2()
+begin
+ declare done int default 0;
+ declare continue handler for 1305 set done = 1;
+ declare c1 cursor for select id,data from test.t1;
+ declare c2 cursor for select i from test.t2;
+
+ open c1;
+ open c2;
+ repeat
+ begin
+ declare a char(16);
+ declare b,c int;
+
+ fetch c1 into a, b;
+ fetch c2 into c;
+ if not done then
+ if b < c then
+ insert into test.t3 values (a, b);
+ else
+ insert into test.t3 values (a, c);
+ end if;
+ end if;
+ end;
+ until done end repeat;
+ close c1;
+ close c2;
+end|
+
+call cur2()|
+select * from t3|
+delete from t1|
+delete from t2|
+drop table t3|
+drop procedure cur2|
+
+#
+# BUG#822
+#
+create procedure bug822(a_id char(16), a_data int)
+begin
+ declare n int;
+ select count(*) into n from t1 where id = a_id and data = a_data;
+ if n = 0 then
+ insert into t1 (id, data) values (a_id, a_data);
+ end if;
+end|
+
+call bug822('foo', 42)|
+call bug822('foo', 42)|
+call bug822('bar', 666)|
+select * from t1|
+delete from t1|
+drop procedure bug822|
+
+#
+# BUG#1495
+#
+create procedure bug1495()
+begin
+ declare x int;
+
+ select data into x from t1 order by id limit 1;
+ if x > 10 then
+ insert into t1 values ("less", x-10);
+ else
+ insert into t1 values ("more", x+10);
+ end if;
+end|
+
+insert into t1 values ('foo', 12)|
+call bug1495()|
+delete from t1 where id='foo'|
+insert into t1 values ('bar', 7)|
+call bug1495()|
+delete from t1 where id='bar'|
+select * from t1|
+delete from t1|
+drop procedure bug1495|
+
+#
+# BUG#1547
+#
+create procedure bug1547(s char(16))
+begin
+ declare x int;
+
+ select data into x from t1 where s = id limit 1;
+ if x > 10 then
+ insert into t1 values ("less", x-10);
+ else
+ insert into t1 values ("more", x+10);
+ end if;
+end|
+
+insert into t1 values ("foo", 12), ("bar", 7)|
+call bug1547("foo")|
+call bug1547("bar")|
+select * from t1|
+delete from t1|
+drop procedure bug1547|
+
+
+#
+# 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 8c0b3fb3919..140d264c8d9 100644
--- a/mysql-test/t/subselect.test
+++ b/mysql-test/t/subselect.test
@@ -352,7 +352,9 @@ INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
select * from t1;
INSERT INTO t1 (x) select (SELECT SUM(a)+1 FROM t2) FROM t2;
select * from t1;
+# After this, only data based on old t1 records should have been added.
INSERT INTO t1 (x) select (SELECT SUM(x)+2 FROM t1) FROM t2;
+select * from t1;
-- error 1054
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(x) FROM t2));
INSERT DELAYED INTO t1 (x) VALUES ((SELECT SUM(a) FROM t2));
@@ -507,6 +509,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);
diff --git a/mysql-test/t/variables.test b/mysql-test/t/variables.test
index 396fb3ac722..efd4fd6aa7f 100644
--- a/mysql-test/t/variables.test
+++ b/mysql-test/t/variables.test
@@ -5,8 +5,20 @@
drop table if exists t1,t2;
--enable_warnings
-set @`test`=1,@TEST=3,@select=2,@t5=1.23456;
-select @test,@`select`,@TEST,@not_used;
+# case insensitivity tests (new in 5.0)
+set @`test`=1;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @TEST=2;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @"tEST"=3;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+set @`TeST`=4;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+select @`teST`:=5;
+select @test, @`test`, @TEST, @`TEST`, @"teSt";
+
+set @select=2,@t5=1.23456;
+select @`select`,@not_used;
set @test_int=10,@test_double=1e-10,@test_string="abcdeghi",@test_string2="abcdefghij",@select=NULL;
select @test_int,@test_double,@test_string,@test_string2,@select;
set @test_int="hello",@test_double="hello",@test_string="hello",@test_string2="hello";
diff --git a/netware/BUILD/compile-netware-END b/netware/BUILD/compile-netware-END
index e0097484500..19d24ff8c6f 100755
--- a/netware/BUILD/compile-netware-END
+++ b/netware/BUILD/compile-netware-END
@@ -29,7 +29,7 @@ rm -rf Makefile.in.bk
make clean bin-dist
# mark the build
-for file in *.tar.gz
+for file in *.tar.gz *.zip
do
if (expr "$file" : "mysql-[1-9].*" > /dev/null)
then
diff --git a/netware/BUILD/compile-netware-all b/netware/BUILD/compile-netware-all
index 35d275f3b42..6baff699e94 100755
--- a/netware/BUILD/compile-netware-all
+++ b/netware/BUILD/compile-netware-all
@@ -8,6 +8,7 @@ set -e
path=`dirname $0`
+$path/compile-netware-src
$path/compile-netware-standard
$path/compile-netware-debug
#$path/compile-netware-max
diff --git a/netware/BUILD/compile-netware-standard b/netware/BUILD/compile-netware-standard
index 12cae1f024e..45f5021862c 100755
--- a/netware/BUILD/compile-netware-standard
+++ b/netware/BUILD/compile-netware-standard
@@ -13,7 +13,7 @@ path=`dirname $0`
suffix="standard"
extra_configs=" \
- --with-innodb
+ --with-innodb \
"
. $path/compile-netware-END
diff --git a/netware/BUILD/mwenv b/netware/BUILD/mwenv
index 27f7056d251..4445bc5b2e5 100755
--- a/netware/BUILD/mwenv
+++ b/netware/BUILD/mwenv
@@ -7,8 +7,8 @@
export MYDEV="WINE_BUILD_DIR"
export MWCNWx86Includes="$MYDEV/libc/include;$MYDEV/zlib-1.1.4"
-export MWNWx86Libraries="$MYDEV/libc/imports;$MYDEV/mw/lib;$MYDEV/zlib-1.1.4"
-export MWNWx86LibraryFiles="libcpre.o;libc.imp;netware.imp;mwcrtl.lib;mwcpp.lib;libz.a"
+export MWNWx86Libraries="$MYDEV/libc/imports;$MYDEV/mw/lib;$MYDEV/mysql-VERSION/netware/BUILD;$MYDEV/zlib-1.1.4"
+export MWNWx86LibraryFiles="libcpre.o;libc.imp;netware.imp;mwcrtl.lib;mwcpp.lib;knetware.imp;libz.a"
export WINEPATH="$MYDEV/mw/bin"
@@ -19,9 +19,9 @@ export AR='mwldnlm'
export AR_FLAGS='-type library -o'
export AS='mwasmnlm'
export CC='mwccnlm -gccincludes'
-export CFLAGS='-dialect c -proc 686 -relax_pointers'
+export CFLAGS='-align 8 -proc 686 -relax_pointers -dialect c'
export CXX='mwccnlm -gccincludes'
-export CXXFLAGS='-dialect c++ -proc 686 -bool on -wchar_t on -relax_pointers -D_WCHAR_T'
+export CXXFLAGS='-align 8 -proc 686 -relax_pointers -dialect c++ -bool on -wchar_t on -D_WCHAR_T'
export LD='mwldnlm'
export LDFLAGS='-entry _LibCPrelude -exit _LibCPostlude -flags pseudopreemption'
export RANLIB=:
diff --git a/netware/BUILD/nwbootstrap b/netware/BUILD/nwbootstrap
index 5d068e4e4de..43b61d5254d 100755
--- a/netware/BUILD/nwbootstrap
+++ b/netware/BUILD/nwbootstrap
@@ -147,10 +147,12 @@ then
fi
# make files writeable
+echo "making files writable..."
cd $target_dir
chmod -R u+rw,g+rw .
# edit the mvenv file
+echo "updating the mwenv environment file..."
mwenv="./netware/BUILD/mwenv"
mv -f $mwenv $mwenv.org
sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \
@@ -158,6 +160,17 @@ sed -e "s;WINE_BUILD_DIR;$wine_build_dir;g" \
-e "s;VERSION;$version;g" $mwenv.org > $mwenv
chmod +rwx $mwenv
+#edit the def file versions
+echo "updating *.def file versions..."
+nlm_version=`echo "$version" | sed -e "s;\([0-9]*\)\.\([0-9]*\)\.\([0-9]*\).*;\1, \2, \3;"`
+
+for file in ./netware/*.def
+do
+mv -f $file $file.org
+sed -e "s;VERSION.*;VERSION $nlm_version;g" $file.org > $file
+rm $file.org
+done
+
# build linux tools
echo "compiling linux tools..."
./netware/BUILD/compile-linux-tools
diff --git a/netware/Makefile.am b/netware/Makefile.am
index 6495f538ffb..5cc0e21f989 100644
--- a/netware/Makefile.am
+++ b/netware/Makefile.am
@@ -1,4 +1,4 @@
-# Copyright (c) 2002 Novell, Inc. All Rights Reserved.
+# Copyright (c) 2002 Novell, Inc. All Rights Reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -11,38 +11,37 @@
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
+# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-INCLUDES = -I$(srcdir)/../include -I../include -I..
-bin_PROGRAMS = mysqld_safe mysql_install_db mysql_test_run libmysql
-mysqld_safe_SOURCES= mysqld_safe.c my_manage.c
-mysql_install_db_SOURCES= mysql_install_db.c my_manage.c
-mysql_test_run_SOURCES= mysql_test_run.c my_manage.c
-libmysql_SOURCES= libmysqlmain.c
-libmysql_LDADD = ../libmysql/.libs/libmysqlclient.a
+INCLUDES= -I$(srcdir)/../include -I../include -I..
+bin_PROGRAMS= mysqld_safe mysql_install_db mysql_test_run libmysql
+mysqld_safe_SOURCES= mysqld_safe.c my_manage.c
+mysql_install_db_SOURCES= mysql_install_db.c my_manage.c
+mysql_test_run_SOURCES= mysql_test_run.c my_manage.c
+libmysql_SOURCES= libmysqlmain.c
+libmysql_LDADD= ../libmysql/.libs/libmysqlclient.a
-netware_build_files = client/mysql.def client/mysqladmin.def \
- client/mysqlbinlog.def client/mysqlcheck.def \
- client/mysqldump.def client/mysqlimport.def \
- client/mysqlshow.def client/mysqltest.def \
- extra/mysql_install.def extra/my_print_defaults.def \
- extra/perror.def extra/replace.def \
- extra/resolveip.def extra/comp_err.def \
- isam/isamchk.def \
- isam/isamlog.def isam/pack_isam.def \
- libmysqld/libmysqld.def myisam/myisamchk.def \
- myisam/myisamlog.def myisam/myisampack.def \
- sql/mysqld.def
-
+netware_build_files = client/mysql.def client/mysqladmin.def \
+ client/mysqlbinlog.def client/mysqlcheck.def \
+ client/mysqldump.def client/mysqlimport.def \
+ client/mysqlshow.def client/mysqltest.def \
+ extra/mysql_install.def extra/my_print_defaults.def \
+ extra/perror.def extra/replace.def \
+ extra/resolveip.def extra/comp_err.def \
+ isam/isamchk.def \
+ isam/isamlog.def isam/pack_isam.def \
+ libmysqld/libmysqld.def myisam/myisamchk.def \
+ myisam/myisamlog.def myisam/myisampack.def \
+ sql/mysqld.def
+
link_sources:
- set -x; \
- for f in $(netware_build_files); do \
- rm -f $(srcdir)/../$$f; \
- org=`echo $$f | sed -e 's/.*\/\(.*\)/\1/g'`; \
- @LN_CP_F@ $(srcdir)/$$org $(srcdir)/../$$f; \
- done;
-
+ set -x; \
+ for f in $(netware_build_files); do \
+ rm -f $(srcdir)/../$$f; \
+ org=`echo $$f | sed -e 's/.*\/\(.*\)/\1/g'`; \
+ @LN_CP_F@ $(srcdir)/$$org $(srcdir)/../$$f; \
+ done;
# Don't update the files from bitkeeper
%::SCCS/s.%
diff --git a/netware/my_manage.c b/netware/my_manage.c
index 490438b0485..f5889766726 100644
--- a/netware/my_manage.c
+++ b/netware/my_manage.c
@@ -1,90 +1,104 @@
/*
- Copyright (c) 2003 Novell, Inc. All Rights Reserved.
+ Copyright (c) 2003 MySQL AB
+ Copyright (c) 2003 Novell, Inc.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
-
-#include <stdio.h>
-#include <errno.h>
-#include <dirent.h>
-#include <string.h>
-#include <screen.h>
-#include <proc.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <assert.h>
+*/
+
+/*****************************************************************************
+** Utility functions for support programs
+*****************************************************************************/
+
+/* MySQL library headers */
+#include <my_global.h>
+#include <my_sys.h>
+#include <my_dir.h>
+#include <m_string.h>
+
+/* These 'should' be POSIX or ANSI */
+#include <assert.h> /* ASSERT */
+#include <stdarg.h> /* vsprintf, va_* */
+#include <sys/types.h> /* pid_t */
+#ifndef __WIN__
+#include <unistd.h> /* fork, rmdir, execve */
+#endif
+#include <stdio.h> /* freopen */
+#include <stdlib.h> /* FILE */
+#ifndef __WIN__
+#include <dirent.h> /* opendir, readdir */
+#endif
+
+#if !defined(__NETWARE__) && !defined(__WIN__)
+#include <sys/wait.h>
+#endif
+
+#if !defined(__NETWARE__)
+#include <signal.h>
+#endif
+
+/* For ASSERT -- Not totally sure about this one: */
+#if !defined(ASSERT)
+#define ASSERT(A) assert(A)
+#endif
#include "my_manage.h"
-
-/******************************************************************************
-
- macros
-
-******************************************************************************/
-
-/******************************************************************************
-
- global variables
-
-******************************************************************************/
-
+#define __STDC__ 1
+#include "process.h"
/******************************************************************************
- functions
-
-******************************************************************************/
-
-/******************************************************************************
+ init_args()
- init_args()
-
- Init an argument list.
+ Init an argument list.
******************************************************************************/
void init_args(arg_list_t *al)
{
+#ifndef __WIN__
ASSERT(al != NULL);
-
+
al->argc = 0;
al->size = ARG_BUF;
- al->argv = malloc(al->size * sizeof(char *));
+ al->argv = (char **)my_malloc(al->size * sizeof(char *), MYF(MY_WME));
ASSERT(al->argv != NULL);
-
+#else
+ win_args[0]= '\0';
+ skip_first_param= TRUE;
+#endif
return;
}
/******************************************************************************
- add_arg()
-
- Add an argument to a list.
+ add_arg()
+
+ Add an argument to a list.
******************************************************************************/
-void add_arg(arg_list_t *al, char *format, ...)
+void add_arg(arg_list_t *al, const char *format, ...)
{
+#ifndef __WIN__
va_list ap;
char temp[PATH_MAX];
ASSERT(al != NULL);
- // increase size
- if (al->argc >= al->size)
+ /* increase size */
+ if (al->argc >= (int)al->size)
{
al->size += ARG_BUF;
- al->argv = realloc(al->argv, al->size * sizeof(char *));
+ al->argv = (char **)my_realloc((char *)al->argv, al->size * sizeof(char *), MYF(MY_WME));
ASSERT(al->argv != NULL);
}
@@ -94,7 +108,7 @@ void add_arg(arg_list_t *al, char *format, ...)
vsprintf(temp, format, ap);
va_end(ap);
- al->argv[al->argc] = malloc(strlen(temp)+1);
+ al->argv[al->argc] = my_malloc(strlen(temp)+1, MYF(MY_WME));
ASSERT(al->argv[al->argc] != NULL);
strcpy(al->argv[al->argc], temp);
@@ -104,19 +118,36 @@ void add_arg(arg_list_t *al, char *format, ...)
{
al->argv[al->argc] = NULL;
}
+#else
+ va_list ap;
+ char param[PATH_MAX];
+ if (!skip_first_param)
+ {
+ va_start(ap, format);
+ vsprintf(&param, format, ap);
+ va_end(ap);
+ strcat(win_args," ");
+ strcat(win_args,param);
+ }
+ else
+ {
+ skip_first_param= FALSE;
+ }
+#endif
return;
}
/******************************************************************************
- free_args()
-
- Free an argument list.
+ free_args()
+
+ Free an argument list.
******************************************************************************/
void free_args(arg_list_t *al)
{
+#ifndef __WIN__
int i;
ASSERT(al != NULL);
@@ -124,74 +155,110 @@ void free_args(arg_list_t *al)
for(i = 0; i < al->argc; i++)
{
ASSERT(al->argv[i] != NULL);
- free(al->argv[i]);
+ my_free(al->argv[i], MYF(MY_WME));
al->argv[i] = NULL;
}
- free(al->argv);
+ my_free((char *)al->argv, MYF(MY_WME));
al->argc = 0;
al->argv = NULL;
-
+#endif
return;
}
/******************************************************************************
- sleep_until_file_deleted()
-
- Sleep until the given file is no longer found.
+ sleep_until_file_deleted()
+
+ Sleep until the given file is no longer found.
******************************************************************************/
int sleep_until_file_deleted(char *pid_file)
{
- struct stat buf;
- int i, err;
-
- for(i = 0; (i < TRY_MAX) && (err = !stat(pid_file, &buf)); i++) sleep(1);
-
- if (err != 0) err = errno;
-
- return err;
+ MY_STAT stat_info;
+ int i, err = 0;
+#ifndef __WIN__
+ for(i = 0; i < TRY_MAX; i++)
+ {
+ if (my_stat(pid_file, &stat_info, MYF(0)) == (MY_STAT *) NULL)
+ {
+ err = errno;
+ break;
+ }
+ my_sleep(1);
+ }
+#else
+ switch (pid_mode)
+ {
+ case MASTER_PID:
+ err= (WaitForSingleObject(master_server, TRY_MAX*1000) == WAIT_TIMEOUT);
+ pid_mode= 0;
+ break;
+ case SLAVE_PID:
+ err= (WaitForSingleObject(slave_server, TRY_MAX*1000) == WAIT_TIMEOUT);
+ pid_mode= 0;
+ break;
+ };
+#endif
+ return err;
}
/******************************************************************************
- sleep_until_file_exists()
-
- Sleep until the given file exists.
+ sleep_until_file_exists()
+
+ Sleep until the given file exists.
******************************************************************************/
int sleep_until_file_exists(char *pid_file)
{
- struct stat buf;
- int i, err;
-
- for(i = 0; (i < TRY_MAX) && (err = stat(pid_file, &buf)); i++) sleep(1);
-
- if (err != 0) err = errno;
-
- return err;
+ MY_STAT stat_info;
+ int i, err = 0;
+
+#ifndef __WIN__
+ for(i = 0; i < TRY_MAX; i++)
+ {
+ if (my_stat(pid_file, &stat_info, MYF(0)) == (MY_STAT *) NULL)
+ {
+ err = errno;
+ break;
+ }
+ my_sleep(1);
+ }
+#else
+ switch (pid_mode)
+ {
+ case MASTER_PID:
+ WaitForSingleObject(master_server, TRY_MAX*1000);
+ pid_mode= 0;
+ break;
+ case SLAVE_PID:
+ WaitForSingleObject(slave_server, TRY_MAX*1000);
+ pid_mode= 0;
+ break;
+ };
+#endif
+
+ return err;
}
/******************************************************************************
- wait_for_server_start()
-
- Wait for the server on the given port to start.
+ wait_for_server_start()
+
+ Wait for the server on the given port to start.
******************************************************************************/
int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
{
arg_list_t al;
- int err, i;
- char mysqladmin_file[PATH_MAX];
+ int err = 0, i;
char trash[PATH_MAX];
- // mysqladmin file
- snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
- snprintf(trash, PATH_MAX, "/tmp/trash.out");
-
- // args
+ /* mysqladmin file */
+ my_snprintf(trash, PATH_MAX, "/tmp/trash.out");
+
+ /* args */
init_args(&al);
add_arg(&al, "%s", mysqladmin_file);
add_arg(&al, "--no-defaults");
@@ -205,13 +272,13 @@ int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
add_arg(&al, "--host=localhost");
add_arg(&al, "ping");
- // NetWare does not support the connect timeout in the TCP/IP stack
- // -- we will try the ping multiple times
- for(i = 0; (i < TRY_MAX)
- && (err = spawn(mysqladmin_file, &al, TRUE, NULL,
- trash, NULL)); i++) sleep(1);
-
- // free args
+ /* NetWare does not support the connect timeout in the TCP/IP stack
+ -- we will try the ping multiple times */
+ for(i = 0; (i < TRY_MAX)
+ && (err = spawn(mysqladmin_file, &al, TRUE, NULL,
+ trash, NULL, NOT_NEED_PID)); i++) sleep(1);
+
+ /* free args */
free_args(&al);
return err;
@@ -219,20 +286,23 @@ int wait_for_server_start(char *bin_dir, char *user, char *password, int port)
/******************************************************************************
- spawn()
-
- Spawn the given path with the given arguments.
+ spawn()
+
+ Spawn the executable at the given path with the given arguments.
******************************************************************************/
+
+#ifdef __NETWARE__
+
int spawn(char *path, arg_list_t *al, int join, char *input,
char *output, char *error)
{
- pid_t pid;
+ pid_t pid;
int result = 0;
wiring_t wiring = { FD_UNUSED, FD_UNUSED, FD_UNUSED };
unsigned long flags = PROC_CURRENT_SPACE | PROC_INHERIT_CWD;
- // open wiring
+ /* open wiring */
if (input)
wiring.infd = open(input, O_RDONLY);
@@ -242,14 +312,14 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
if (error)
wiring.errfd = open(error, O_WRONLY | O_CREAT | O_TRUNC);
- // procve requires a NULL
+ /* procve requires a NULL */
add_arg(al, NULL);
- // go
+ /* go */
pid = procve(path, flags, NULL, &wiring, NULL, NULL, 0,
NULL, (const char **)al->argv);
- if (pid == -1)
+ if (pid == -1)
{
result = -1;
}
@@ -257,8 +327,8 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
{
waitpid(pid, &result, 0);
}
-
- // close wiring
+
+ /* close wiring */
if (wiring.infd != -1)
close(wiring.infd);
@@ -268,207 +338,411 @@ int spawn(char *path, arg_list_t *al, int join, char *input,
if (wiring.errfd != -1)
close(wiring.errfd);
- return result;
+ return result;
+}
+
+#else /* NOT __NETWARE__ */
+
+#ifdef __WIN__
+
+int my_vsnprintf_(char *to, size_t n, const char* value, ...)
+{
+ char *start=to, *end=to+n-1;
+ uint length, num_state, pre_zero;
+ reg2 char *par;// = value;
+ va_list args;
+ va_start(args,value);
+
+ par = va_arg(args, char *);
+ while (par != NULL)
+ {
+ uint plen,left_len = (uint)(end-to)+1;
+ if (!par) par = (char*)"(null)";
+ plen = (uint) strlen(par);
+ if (left_len <= plen)
+ plen = left_len - 1;
+ to=strnmov(to+strlen(to),par,plen);
+ par = va_arg(args, char *);
+ }
+ va_end(args);
+ DBUG_ASSERT(to <= end);
+ *to='\0';
+ return (uint) (to - start);
+}
+
+int spawn(char *path, arg_list_t *al, int join, char *input,
+ char *output, char *error)
+{
+ char *cl;
+ char *arg;
+ intptr_t result;
+ int j;
+ int err;
+ STARTUPINFO startup_info;
+ PROCESS_INFORMATION process_information;
+ ULONG dosretval;
+ int retval;
+ DWORD exit_code;
+ SECURITY_ATTRIBUTES process_attributes, thread_attributes;
+ char command_line[1024]= "";
+
+
+ memset(&startup_info,0,sizeof(STARTUPINFO));
+ startup_info.cb = sizeof(STARTUPINFO);
+
+ if (input)
+ freopen(input, "rb", stdin);
+
+ if (output)
+ freopen(output, "wb", stdout);
+
+ if (error)
+ freopen(error, "wb", stderr);
+
+ result= CreateProcess(
+ path,
+ &win_args,
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &startup_info,
+ &process_information
+ );
+
+ if (process_information.hProcess)
+ {
+ if (join)
+ {
+ if (WaitForSingleObject(process_information.hProcess, mysqld_timeout) == WAIT_TIMEOUT)
+ {
+ exit_code= -1;
+ }
+ else
+ {
+ GetExitCodeProcess(process_information.hProcess, &exit_code);
+ }
+ CloseHandle(process_information.hProcess);
+ }
+ else
+ {
+ exit_code= 0;
+ }
+ if (run_server)
+ {
+ switch (pid_mode)
+ {
+ case MASTER_PID:
+ master_server= process_information.hProcess;
+ break;
+ case SLAVE_PID:
+ slave_server= process_information.hProcess;
+ break;
+ };
+ pid_mode= 0;
+ run_server= FALSE;
+ };
+ }
+ else
+ {
+ exit_code= -1;
+ }
+ if (input)
+ freopen("CONIN$","rb",stdin);
+ if (output)
+ freopen("CONOUT$","wb",stdout);
+ if (error)
+ freopen("CONOUT$","wb",stderr);
+
+ return exit_code;
}
+#else /* NOT __NETWARE__, NOT __WIN__ */
+
+/* This assumes full POSIX.1 compliance */
+int spawn(char *path, arg_list_t *al, int join, char *input,
+ char *output, char *error)
+{
+ int result = 0;
+ pid_t pid;
+
+ if ((pid = fork()))
+ {
+ /* Remains in parent process */
+ if (join && (pid != -1))
+ waitpid(pid, &result, 0);
+ }
+ else
+ {
+ /* Child process */
+
+ /* Reassign streams */
+ if (input)
+ freopen(input, "r", stdin);
+
+ if (output)
+ freopen(output, "w", stdout);
+
+ if (error)
+ freopen(error, "w", stderr);
+
+ /* Spawn the process */
+ execve(path, al->argv, environ);
+ }
+
+ return result;
+}
+
+#endif /* __WIN__ */
+
+#endif /* __NETWARE__ */
+
/******************************************************************************
- stop_server()
-
- Stop the server with the given port and pid file.
+ stop_server()
+
+ Stop the server with the given port and pid file.
******************************************************************************/
int stop_server(char *bin_dir, char *user, char *password, int port,
char *pid_file)
{
- arg_list_t al;
- int err, i, argc = 0;
- char mysqladmin_file[PATH_MAX];
+ arg_list_t al;
+ int err;
char trash[PATH_MAX];
- // mysqladmin file
- snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
- snprintf(trash, PATH_MAX, "/tmp/trash.out");
-
- // args
+ my_snprintf(trash, PATH_MAX, "/tmp/trash.out");
+
+ /* args */
init_args(&al);
- add_arg(&al, "%s", mysqladmin_file);
- add_arg(&al, "--no-defaults");
- add_arg(&al, "--port=%u", port);
- add_arg(&al, "--user=%s", user);
- add_arg(&al, "--password=%s", password);
- add_arg(&al, "-O");
- add_arg(&al, "shutdown_timeout=20");
- add_arg(&al, "shutdown");
-
- // spawn
- if ((err = spawn(mysqladmin_file, &al, TRUE, NULL,
- trash, NULL)) == 0)
- {
- sleep_until_file_deleted(pid_file);
- }
- else
- {
+ add_arg(&al, "%s", mysqladmin_file);
+ add_arg(&al, "--no-defaults");
+ add_arg(&al, "--port=%u", port);
+ add_arg(&al, "--user=%s", user);
+ add_arg(&al, "--password=%s", password);
+ add_arg(&al, "-O");
+ add_arg(&al, "shutdown_timeout=20");
+ add_arg(&al, "shutdown");
+
+ /* spawn */
+ if ((err = spawn(mysqladmin_file, &al, TRUE, NULL,
+ trash, NULL)) == 0)
+ {
+ sleep_until_file_deleted(pid_file);
+ }
+ else
+ {
pid_t pid = get_server_pid(pid_file);
-
- // shutdown failed - kill server
- kill_server(pid);
-
- sleep(TRY_MAX);
- // remove pid file if possible
- err = remove(pid_file);
+ /* shutdown failed - kill server */
+ kill_server(pid);
+
+ sleep(TRY_MAX);
+
+ /* remove pid file if possible */
+ err = my_delete(pid_file, MYF(MY_WME));
}
- // free args
+ /* free args */
free_args(&al);
- return err;
+ return err;
}
/******************************************************************************
- get_server_pid()
-
- Get the VM id with the given pid file.
+ get_server_pid()
+
+ Get the VM id with the given pid file.
******************************************************************************/
pid_t get_server_pid(char *pid_file)
{
- char buf[PATH_MAX];
- int fd, err;
- char *p;
- pid_t id;
-
- // discover id
- fd = open(pid_file, O_RDONLY);
-
- err = read(fd, buf, PATH_MAX);
-
- close(fd);
-
- if (err > 0)
- {
- // terminate string
- if ((p = strchr(buf, '\n')) != NULL)
- {
- *p = NULL;
-
- // check for a '\r'
- if ((p = strchr(buf, '\r')) != NULL)
- {
- *p = NULL;
- }
- }
- else
- {
- buf[err] = NULL;
- }
-
- id = strtol(buf, NULL, 0);
- }
+ char buf[PATH_MAX];
+ int err;
+ File fd;
+ char *p;
+ pid_t id = 0;
+
+ /* discover id */
+ fd = my_open(pid_file, O_RDONLY, MYF(MY_WME));
+
+ err = my_read(fd, buf, PATH_MAX, MYF(MY_WME));
+
+ my_close(fd, MYF(MY_WME));
+
+ if (err > 0)
+ {
+ /* terminate string */
+ if ((p = strchr(buf, '\n')) != NULL)
+ {
+ *p = '\0';
+
+ /* check for a '\r' */
+ if ((p = strchr(buf, '\r')) != NULL)
+ {
+ *p = '\0';
+ }
+ }
+ else
+ {
+ buf[err] = '\0';
+ }
+ id = strtol(buf, NULL, 0);
+ }
+
return id;
}
-
/******************************************************************************
- kill_server()
-
- Force a kill of the server with the given pid.
+ kill_server()
+
+ Force a kill of the server with the given pid.
******************************************************************************/
void kill_server(pid_t pid)
{
if (pid > 0)
{
- // destroy vm
+
+#if !defined(__NETWARE__)
+ /* Send SIGTERM to pid */
+ kill(pid, SIGTERM);
+#else /* __NETWARE__ */
+ /* destroy vm */
NXVmDestroy(pid);
+
+#endif
+
}
}
/******************************************************************************
- del_tree()
-
- Delete the directory and subdirectories.
+ del_tree()
+
+ Delete the directory and subdirectories.
******************************************************************************/
void del_tree(char *dir)
{
- DIR *parent = opendir(dir);
- DIR *entry;
- char temp[PATH_MAX];
-
- if (parent == NULL)
- {
- return;
- }
-
- while((entry = readdir(parent)) != NULL)
- {
- // create long name
- snprintf(temp, PATH_MAX, "%s/%s", dir, entry->d_name);
-
- if (entry->d_name[0] == '.')
- {
- // Skip
- }
- else if (S_ISDIR(entry->d_type))
- {
- // delete subdirectory
- del_tree(temp);
- }
- else
- {
- // remove file
- remove(temp);
- }
- }
-
- // remove directory
- rmdir(dir);
+ MY_DIR *current;
+ uint i;
+ char temp[PATH_MAX];
+
+ current = my_dir(dir, MYF(MY_WME | MY_WANT_STAT));
+
+ /* current is NULL if dir does not exist */
+ if (current == NULL)
+ return;
+
+ for (i = 0; i < current->number_off_files; i++)
+ {
+ /* create long name */
+ my_snprintf(temp, PATH_MAX, "%s/%s", dir, current->dir_entry[i].name);
+
+ if (current->dir_entry[i].name[0] == '.')
+ {
+ /* Skip */
+ }
+ else if (MY_S_ISDIR(current->dir_entry[i].mystat.st_mode))
+ {
+ /* delete subdirectory */
+ del_tree(temp);
+ }
+ else
+ {
+ /* remove file */
+ my_delete(temp, MYF(MY_WME));
+ }
+ }
+
+ my_dirend(current);
+
+ /* remove directory */
+ rmdir(dir);
}
/******************************************************************************
- removef()
-
+ removef()
+
******************************************************************************/
-int removef(char *format, ...)
+int removef(const char *format, ...)
{
- va_list ap;
+ va_list ap;
char path[PATH_MAX];
-
- va_start(ap, format);
- vsnprintf(path, PATH_MAX, format, ap);
-
- va_end(ap);
+ va_start(ap, format);
+
+ my_vsnprintf(path, PATH_MAX, format, ap);
- return remove(path);
+ va_end(ap);
+#ifdef __WIN__
+ {
+ MY_DIR *current;
+ uint i;
+ struct _finddata_t find;
+ char temp[PATH_MAX];
+#ifdef _WIN64
+ __int64 handle;
+#else
+ long handle;
+#endif
+ char *p;
+
+ p= strrchr(path,'\\');
+ if (p == NULL)
+ {
+ p= strrchr(path,'/');
+ if (p == NULL)
+ p= &path;
+ else
+ p++;
+ }
+ else
+ p++;
+
+ if ((handle=_findfirst(path,&find)) == -1L)
+ return 0;
+ do
+ {
+ strcpy(p,find.name);
+ my_delete(path, MYF(MY_WME));
+ } while (!_findnext(handle,&find));
+ _findclose(handle);
+ }
+#else
+ return my_delete(path, MYF(MY_WME));
+#endif
}
/******************************************************************************
- get_basedir()
-
+ get_basedir()
+
******************************************************************************/
void get_basedir(char *argv0, char *basedir)
{
- char temp[PATH_MAX];
- char *p;
-
- ASSERT(argv0 != NULL);
+ char temp[PATH_MAX];
+ char *p;
+
+ ASSERT(argv0 != NULL);
ASSERT(basedir != NULL);
- strcpy(temp, strlwr(argv0));
- while((p = strchr(temp, '\\')) != NULL) *p = '/';
-
- if ((p = strindex(temp, "/bin/")) != NULL)
- {
- *p = NULL;
- strcpy(basedir, temp);
- }
+ strcpy(temp, argv0);
+#ifndef __WIN__
+ casedn_str(temp);
+#endif
+ while((p = strchr(temp, '\\')) != NULL) *p = '/';
+
+ if ((p = strstr(temp, "/bin/")) != NullS)
+ {
+ *p = '\0';
+ strcpy(basedir, temp);
+ }
}
diff --git a/netware/my_manage.h b/netware/my_manage.h
index b19662c4ee9..4215ae59a25 100644
--- a/netware/my_manage.h
+++ b/netware/my_manage.h
@@ -26,17 +26,41 @@
******************************************************************************/
#include <stdlib.h>
+#ifndef __WIN__
#include <unistd.h>
+#endif
/******************************************************************************
macros
******************************************************************************/
+#ifdef __WIN__
+#define PATH_MAX _MAX_PATH
+#define NAME_MAX _MAX_FNAME
+#define kill(A,B) TerminateProcess((HANDLE)A,0)
+#define NOT_NEED_PID 0
+#define MASTER_PID 1
+#define SLAVE_PID 2
+#define mysqld_timeout 60000
+
+intptr_t master_server;
+intptr_t slave_server;
+int pid_mode;
+bool run_server;
+char win_args[1024];
+bool skip_first_param;
+#endif
+
#define ARG_BUF 10
#define TRY_MAX 5
+#ifdef __NETWARE__
+#define strstr(A,B) strindex(A,B)
+#endif
+
+
/******************************************************************************
structures
@@ -53,6 +77,8 @@ typedef struct
} arg_list_t;
+
+typedef int pid_t;
/******************************************************************************
global variables
@@ -66,7 +92,7 @@ typedef struct
******************************************************************************/
void init_args(arg_list_t *);
-void add_arg(arg_list_t *, char *, ...);
+void add_arg(arg_list_t *, const char *, ...);
void free_args(arg_list_t *);
int sleep_until_file_exists(char *);
@@ -80,8 +106,12 @@ pid_t get_server_pid(char *);
void kill_server(pid_t pid);
void del_tree(char *);
-int removef(char *, ...);
+int removef(const char *, ...);
void get_basedir(char *, char *);
+char mysqladmin_file[PATH_MAX];
+
#endif /* _MY_MANAGE */
+
+
diff --git a/netware/mysql_test_run.c b/netware/mysql_test_run.c
index ff629546793..fb914c355e4 100644
--- a/netware/mysql_test_run.c
+++ b/netware/mysql_test_run.c
@@ -16,21 +16,29 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
-#include <stdlib.h>
+
+/* MySQL library headers */
+#include <my_global.h>
+#include <my_sys.h>
+#include <my_dir.h>
+#include <m_string.h>
+
+/* These 'should' be POSIX or ANSI */
#include <stdio.h>
-#include <errno.h>
-#include <dirent.h>
-#include <string.h>
+#include <stdarg.h>
+
+#ifdef __NETWARE__
#include <screen.h>
-#include <nks/vm.h>
-#include <ctype.h>
-#include <sys/stat.h>
-#include <unistd.h>
-#include <fcntl.h>
+#endif
#include "my_config.h"
#include "my_manage.h"
+#ifdef __WIN__
+#include <Shlwapi.h>
+#endif
+
+
/******************************************************************************
macros
@@ -42,16 +50,16 @@
#define NW_TEST_SUFFIX ".nw-test"
#define NW_RESULT_SUFFIX ".nw-result"
-#define TEST_SUFFIX ".test"
-#define RESULT_SUFFIX ".result"
-#define REJECT_SUFFIX ".reject"
-#define OUT_SUFFIX ".out"
-#define ERR_SUFFIX ".err"
+#define TEST_SUFFIX ".test"
+#define RESULT_SUFFIX ".result"
+#define REJECT_SUFFIX ".reject"
+#define OUT_SUFFIX ".out"
+#define ERR_SUFFIX ".err"
-#define TEST_PASS "[ pass ]"
-#define TEST_SKIP "[ skip ]"
-#define TEST_FAIL "[ fail ]"
-#define TEST_BAD "[ bad ]"
+#define TEST_PASS "[ pass ]"
+#define TEST_SKIP "[ skip ]"
+#define TEST_FAIL "[ fail ]"
+#define TEST_BAD "[ bad ]"
/******************************************************************************
@@ -59,7 +67,7 @@
******************************************************************************/
-char base_dir[PATH_MAX] = "sys:/mysql";
+char base_dir[PATH_MAX] = "/mysql";
char db[PATH_MAX] = "test";
char user[PATH_MAX] = "root";
char password[PATH_MAX] = "";
@@ -67,7 +75,7 @@ char password[PATH_MAX] = "";
int master_port = 9306;
int slave_port = 9307;
-// comma delimited list of tests to skip or empty string
+/* comma delimited list of tests to skip or empty string */
char skip_test[PATH_MAX] = "";
char bin_dir[PATH_MAX];
@@ -80,7 +88,6 @@ char slave_dir[PATH_MAX];
char lang_dir[PATH_MAX];
char char_dir[PATH_MAX];
-char mysqladmin_file[PATH_MAX];
char mysqld_file[PATH_MAX];
char mysqltest_file[PATH_MAX];
char master_pid[PATH_MAX];
@@ -109,17 +116,30 @@ int single_test = TRUE;
int restarts = 0;
FILE *log_fd = NULL;
+// WAX
+#include <my_getopt.h>
+
+const char* mysqld = "mysqld", *opt_exedir="client_debug";
+
+static struct my_option my_long_options[] =
+{
+ {"mysqld", 'M', "Type of mysqld (without extention of file)- mysqld, mysql-nt, mysql-nt-max, mysqld-max.",
+ (gptr*) &mysqld, (gptr*) &mysqld, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+ {"exefiledir", 'e', "Directory of .exe files (client_debug or client_release).", (gptr*) &opt_exedir,
+ (gptr*) &opt_exedir, 0, GET_STR, REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
+};
+
/******************************************************************************
functions
-
+
******************************************************************************/
/******************************************************************************
prototypes
-
+
******************************************************************************/
void report_stats();
@@ -135,17 +155,17 @@ void mysql_restart();
int read_option(char *, char *);
void run_test(char *);
void setup(char *);
-void vlog(char *, va_list);
-void log(char *, ...);
-void log_info(char *, ...);
-void log_error(char *, ...);
-void log_errno(char *, ...);
-void die(char *);
+void vlog(const char *, va_list);
+void mtr_log(const char *, ...);
+void mtr_log_info(const char *, ...);
+void mtr_log_error(const char *, ...);
+void mtr_log_errno(const char *, ...);
+void die(const char *);
/******************************************************************************
report_stats()
-
+
Report the gathered statistics.
******************************************************************************/
@@ -153,42 +173,42 @@ void report_stats()
{
if (total_fail == 0)
{
- log("\nAll %d test(s) were successful.\n", total_test);
+ mtr_log("\nAll %d test(s) were successful.\n", total_test);
}
else
{
double percent = ((double)total_pass / total_test) * 100;
-
- log("\nFailed %u/%u test(s), %.02f%% successful.\n",
+
+ mtr_log("\nFailed %u/%u test(s), %.02f%% successful.\n",
total_fail, total_test, percent);
- log("\nThe .out and .err files in %s may give you some\n", result_dir);
- log("hint of what when wrong.\n");
- log("\nIf you want to report this error, please first read the documentation\n");
- log("at: http://www.mysql.com/doc/M/y/MySQL_test_suite.html\n");
+ mtr_log("\nThe .out and .err files in %s may give you some\n", result_dir);
+ mtr_log("hint of what when wrong.\n");
+ mtr_log("\nIf you want to report this error, please first read the documentation\n");
+ mtr_log("at: http://www.mysql.com/doc/M/y/MySQL_test_suite.html\n");
}
- log("\n%.02f total minutes elapsed in the test cases\n\n", total_time / 60);
+ mtr_log("\n%.02f total minutes elapsed in the test cases\n\n", total_time / 60);
}
/******************************************************************************
install_db()
-
+
Install the a database.
******************************************************************************/
void install_db(char *datadir)
{
arg_list_t al;
- int err, i;
+ int err;
char input[PATH_MAX];
char output[PATH_MAX];
char error[PATH_MAX];
// input file
- snprintf(input, PATH_MAX, "%s/bin/init_db.sql", base_dir);
- snprintf(output, PATH_MAX, "%s/install.out", datadir);
- snprintf(error, PATH_MAX, "%s/install.err", datadir);
+ my_snprintf(input, PATH_MAX, "%s/bin/init_db.sql", base_dir);
+ my_snprintf(output, PATH_MAX, "%s/install.out", datadir);
+ my_snprintf(error, PATH_MAX, "%s/install.err", datadir);
// args
init_args(&al);
@@ -201,6 +221,7 @@ void install_db(char *datadir)
add_arg(&al, "--skip-bdb");
// spawn
+
if ((err = spawn(mysqld_file, &al, TRUE, input, output, error)) != 0)
{
die("Unable to create database.");
@@ -221,34 +242,34 @@ void mysql_install_db()
{
char temp[PATH_MAX];
- // var directory
- snprintf(temp, PATH_MAX, "%s/var", mysql_test_dir);
+ /* var directory */
+ my_snprintf(temp, PATH_MAX, "%s/var", mysql_test_dir);
- // clean up old direcotry
+ /* clean up old directory */
del_tree(temp);
- // create var directory
- mkdir(temp, S_IRWXU);
-
- // create subdirectories
- snprintf(temp, PATH_MAX, "%s/var/run", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/tmp", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/master-data", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/master-data/mysql", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/master-data/test", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/slave-data", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/slave-data/mysql", mysql_test_dir);
- mkdir(temp, S_IRWXU);
- snprintf(temp, PATH_MAX, "%s/var/slave-data/test", mysql_test_dir);
- mkdir(temp, S_IRWXU);
-
- // install databases
+ /* create var directory */
+ my_mkdir(temp, 0700, MYF(MY_WME));
+
+ /* create subdirectories */
+ my_snprintf(temp, PATH_MAX, "%s/var/run", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/tmp", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/master-data", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/master-data/mysql", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/master-data/test", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/slave-data", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/slave-data/mysql", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+ my_snprintf(temp, PATH_MAX, "%s/var/slave-data/test", mysql_test_dir);
+ my_mkdir(temp, 0700, MYF(MY_WME));
+
+ /* install databases */
install_db(master_dir);
install_db(slave_dir);
}
@@ -263,41 +284,35 @@ void mysql_install_db()
void start_master()
{
arg_list_t al;
- int err, i;
+ int err;
char master_out[PATH_MAX];
char master_err[PATH_MAX];
- // remove old berkeley db log files that can confuse the server
+ /* remove old berkeley db log files that can confuse the server */
removef("%s/log.*", master_dir);
-
- // remove stale binary logs
+
+ /* remove stale binary logs */
removef("%s/*-bin.*", master_dir);
- // remove stale binary logs
+ /* remove stale binary logs */
removef("%s/*.index", master_dir);
- // remove master.info file
+ /* remove master.info file */
removef("%s/master.info", master_dir);
- // remove relay files
+ /* remove relay files */
removef("%s/var/log/*relay*", mysql_test_dir);
- // remove relay-log.info file
+ /* remove relay-log.info file */
removef("%s/relay-log.info", master_dir);
- // init script
- if (master_init_script[0] != NULL)
- {
- // run_init_script(master_init_script);
- }
-
- // redirection files
- snprintf(master_out, PATH_MAX, "%s/var/run/master%u.out",
+ /* redirection files */
+ my_snprintf(master_out, PATH_MAX, "%s/var/run/master%u.out",
mysql_test_dir, restarts);
- snprintf(master_err, PATH_MAX, "%s/var/run/master%u.err",
+ my_snprintf(master_err, PATH_MAX, "%s/var/run/master%u.err",
mysql_test_dir, restarts);
- // args
+ /* args */
init_args(&al);
add_arg(&al, "%s", mysqld_file);
add_arg(&al, "--no-defaults");
@@ -313,11 +328,11 @@ void start_master()
add_arg(&al, "--tmpdir=%s", mysql_tmp_dir);
add_arg(&al, "--language=%s", lang_dir);
- // $MASTER_40_ARGS
+ /* $MASTER_40_ARGS */
add_arg(&al, "--rpl-recovery-rank=1");
add_arg(&al, "--init-rpl-role=master");
- // $SMALL_SERVER
+ /* $SMALL_SERVER */
add_arg(&al, "-O");
add_arg(&al, "key_buffer_size=1M");
add_arg(&al, "-O");
@@ -325,8 +340,8 @@ void start_master()
add_arg(&al, "-O");
add_arg(&al, "max_heap_table_size=1M");
- // $EXTRA_MASTER_OPT
- if (master_opt[0] != NULL)
+ /* $EXTRA_MASTER_OPT */
+ if (master_opt[0] != '\0')
{
char *p;
@@ -335,34 +350,39 @@ void start_master()
while(p)
{
add_arg(&al, "%s", p);
-
+
p = (char *)strtok(NULL, " \t");
}
}
- // remove the pid file if it exists
- remove(master_pid);
+ /* remove the pid file if it exists */
+#ifndef __WIN__
+ my_delete(master_pid, MYF(MY_WME));
+#else
+ pid_mode= MASTER_PID;
+ run_server= TRUE;
+#endif
- // spawn
+ /* spawn */
if ((err = spawn(mysqld_file, &al, FALSE, NULL, master_out, master_err)) == 0)
{
sleep_until_file_exists(master_pid);
- if ((err = wait_for_server_start(bin_dir, user, password, master_port)) == 0)
+ if ((err = wait_for_server_start(bin_dir, user, password, master_port)) == 0)
{
master_running = TRUE;
}
else
{
- log_error("The master server went down early.");
+ mtr_log_error("The master server went down early.");
}
}
else
{
- log_error("Unable to start master server.");
+ mtr_log_error("Unable to start master server.");
}
- // free_args
+ /* free_args */
free_args(&al);
}
@@ -376,48 +396,48 @@ void start_master()
void start_slave()
{
arg_list_t al;
- int err, i;
+ int err;
char slave_out[PATH_MAX];
char slave_err[PATH_MAX];
char temp[PATH_MAX];
- // skip?
+ /* skip? */
if (skip_slave) return;
- // remove stale binary logs
+ /* remove stale binary logs */
removef("%s/*-bin.*", slave_dir);
- // remove stale binary logs
+ /* remove stale binary logs */
removef("%s/*.index", slave_dir);
- // remove master.info file
+ /* remove master.info file */
removef("%s/master.info", slave_dir);
- // remove relay files
+ /* remove relay files */
removef("%s/var/log/*relay*", mysql_test_dir);
- // remove relay-log.info file
+ /* remove relay-log.info file */
removef("%s/relay-log.info", slave_dir);
- // init script
- if (slave_init_script[0] != NULL)
+ /* init script */
+ if (slave_init_script[0] != '\0')
{
- // run_init_script(slave_init_script);
+ /* run_init_script(slave_init_script); */
- // TODO: use the scripts
- if (strindex(slave_init_script, "rpl000016-slave.sh") != NULL)
+ /* TODO: use the scripts */
+ if (strstr(slave_init_script, "rpl000016-slave.sh") != NULL)
{
- // create empty master.info file
- snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
- close(open(temp, O_WRONLY | O_CREAT));
+ /* create empty master.info file */
+ my_snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
+ my_close(my_open(temp, O_WRONLY | O_CREAT, MYF(0)), MYF(0));
}
- else if (strindex(slave_init_script, "rpl000017-slave.sh") != NULL)
+ else if (strstr(slave_init_script, "rpl000017-slave.sh") != NullS)
{
FILE *fp;
- // create a master.info file
- snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
- fp = fopen(temp, "wb+");
+ /* create a master.info file */
+ my_snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
+ fp = my_fopen(temp, (int)(O_WRONLY | O_BINARY | O_CREAT), MYF(MY_WME));
fputs("master-bin.001\n", fp);
fputs("4\n", fp);
@@ -428,20 +448,20 @@ void start_slave()
fputs("1\n", fp);
fputs("0\n", fp);
- fclose(fp);
+ my_fclose(fp, MYF(MY_WME));
}
- else if (strindex(slave_init_script, "rpl_rotate_logs-slave.sh") != NULL)
+ else if (strstr(slave_init_script, "rpl_rotate_logs-slave.sh") != NullS)
{
- // create empty master.info file
- snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
- close(open(temp, O_WRONLY | O_CREAT));
+ /* create empty master.info file */
+ my_snprintf(temp, PATH_MAX, "%s/master.info", slave_dir);
+ my_close(my_open(temp, O_WRONLY | O_CREAT, MYF(0)), MYF(0));
}
}
- // redirection files
- snprintf(slave_out, PATH_MAX, "%s/var/run/slave%u.out",
+ /* redirection files */
+ my_snprintf(slave_out, PATH_MAX, "%s/var/run/slave%u.out",
mysql_test_dir, restarts);
- snprintf(slave_err, PATH_MAX, "%s/var/run/slave%u.err",
+ my_snprintf(slave_err, PATH_MAX, "%s/var/run/slave%u.err",
mysql_test_dir, restarts);
// args
@@ -467,15 +487,15 @@ void start_slave()
add_arg(&al, "--slave-load-tmpdir=../../var/tmp");
add_arg(&al, "--report-user=%s", user);
- add_arg(&al, "--report-host=127.0.0.1");
+ add_arg(&al, "--report-host=127.0.0.1");
add_arg(&al, "--report-port=%u", slave_port);
add_arg(&al, "--master-retry-count=10");
add_arg(&al, "-O");
add_arg(&al, "slave_net_timeout=10");
- // slave master info
- if (slave_master_info[0] != NULL)
+ /* slave master info */
+ if (slave_master_info[0] != '\0')
{
char *p;
@@ -499,7 +519,7 @@ void start_slave()
add_arg(&al, "--rpl-recovery-rank=2");
}
- // small server
+ /* small server */
add_arg(&al, "-O");
add_arg(&al, "key_buffer_size=1M");
add_arg(&al, "-O");
@@ -507,8 +527,8 @@ void start_slave()
add_arg(&al, "-O");
add_arg(&al, "max_heap_table_size=1M");
- // opt args
- if (slave_opt[0] != NULL)
+ /* opt args */
+ if (slave_opt[0] != '\0')
{
char *p;
@@ -521,9 +541,14 @@ void start_slave()
p = (char *)strtok(NULL, " \t");
}
}
-
- // remove the pid file if it exists
- remove(slave_pid);
+
+ /* remove the pid file if it exists */
+#ifndef __WIN__
+ my_delete(slave_pid, MYF(MY_WME));
+#else
+ pid_mode= SLAVE_PID;
+ run_server= TRUE;
+#endif
// spawn
if ((err = spawn(mysqld_file, &al, FALSE, NULL, slave_out, slave_err)) == 0)
@@ -536,12 +561,12 @@ void start_slave()
}
else
{
- log_error("The slave server went down early.");
+ mtr_log_error("The slave server went down early.");
}
}
else
{
- log_error("Unable to start slave server.");
+ mtr_log_error("Unable to start slave server.");
}
// free args
@@ -561,8 +586,10 @@ void mysql_start()
start_slave();
+#ifdef __NETWARE__
// activate the test screen
ActivateScreen(getscreenhandle());
+#endif
}
/******************************************************************************
@@ -576,17 +603,20 @@ void stop_slave()
{
int err;
- // running?
+ /* running? */
if (!slave_running) return;
+#ifdef __WIN__
+ pid_mode= SLAVE_PID;
+#endif
- // stop
+ /* stop */
if ((err = stop_server(bin_dir, user, password, slave_port, slave_pid)) == 0)
{
slave_running = FALSE;
}
else
{
- log_error("Unable to stop slave server.");
+ mtr_log_error("Unable to stop slave server.");
}
}
@@ -601,16 +631,18 @@ void stop_master()
{
int err;
- // running?
+ /* running? */
if (!master_running) return;
-
+#ifdef __WIN__
+ pid_mode= MASTER_PID;
+#endif
if ((err = stop_server(bin_dir, user, password, master_port, master_pid)) == 0)
{
master_running = FALSE;
}
else
{
- log_error("Unable to stop master server.");
+ mtr_log_error("Unable to stop master server.");
}
}
@@ -627,8 +659,10 @@ void mysql_stop()
stop_slave();
+#ifdef __NETWARE__
// activate the test screen
ActivateScreen(getscreenhandle());
+#endif
}
/******************************************************************************
@@ -640,7 +674,7 @@ void mysql_stop()
******************************************************************************/
void mysql_restart()
{
- log_info("Restarting the MySQL server(s): %u", ++restarts);
+ mtr_log_info("Restarting the MySQL server(s): %u", ++restarts);
mysql_stop();
@@ -656,45 +690,50 @@ void mysql_restart()
******************************************************************************/
int read_option(char *opt_file, char *opt)
{
- int fd, err;
- int result;
+ File fd;
+ int err;
char *p;
char buf[PATH_MAX];
- // copy current option
+ /* copy current option */
strncpy(buf, opt, PATH_MAX);
- // open options file
- fd = open(opt_file, O_RDONLY);
+#ifdef __WIN__
+ if (PathFileExistsA(opt_file))
+ {
+#endif
+
+ /* open options file */
+ fd = my_open(opt_file, O_RDONLY, MYF(MY_WME));
- err = read(fd, opt, PATH_MAX);
+ err = my_read(fd, opt, PATH_MAX, MYF(MY_WME));
- close(fd);
+ my_close(fd, MYF(MY_WME));
if (err > 0)
{
- // terminate string
+ /* terminate string */
if ((p = strchr(opt, '\n')) != NULL)
{
- *p = NULL;
+ *p = '\0';
- // check for a '\r'
+ /* check for a '\r' */
if ((p = strchr(opt, '\r')) != NULL)
{
- *p = NULL;
+ *p = '\0';
}
}
else
{
- opt[err] = NULL;
+ opt[err] = '\0';
}
- // check for $MYSQL_TEST_DIR
- if ((p = strstr(opt, "$MYSQL_TEST_DIR")) != NULL)
+ /* check for $MYSQL_TEST_DIR */
+ if ((p = strstr(opt, "$MYSQL_TEST_DIR")) != NullS)
{
char temp[PATH_MAX];
- *p = NULL;
+ *p = '\0';
strcpy(temp, p + strlen("$MYSQL_TEST_DIR"));
@@ -705,11 +744,14 @@ int read_option(char *opt_file, char *opt)
}
else
{
- // clear option
- *opt = NULL;
+ /* clear option */
+ *opt = '\0';
+ }
+#ifdef __WIN__
}
+#endif
- // compare current option with previous
+ /* compare current option with previous */
return strcmp(opt, buf);
}
@@ -724,21 +766,20 @@ void run_test(char *test)
{
char temp[PATH_MAX];
char *rstr;
- double elapsed = 0;
int skip = FALSE;
int restart = FALSE;
int flag = FALSE;
struct stat info;
-
- // single test?
+
+ /* single test? */
if (!single_test)
{
// skip tests in the skip list
- snprintf(temp, PATH_MAX, " %s ", test);
- skip = (strindex(skip_test, temp) != NULL);
+ my_snprintf(temp, PATH_MAX, " %s ", test);
+ skip = strinstr(skip_test, temp);
}
- // skip test?
+ /* skip test? */
if (!skip)
{
char test_file[PATH_MAX];
@@ -751,7 +792,6 @@ void run_test(char *test)
char err_file[PATH_MAX];
int err;
arg_list_t al;
- NXTime_t start, stop;
// skip slave?
flag = skip_slave;
@@ -759,36 +799,36 @@ void run_test(char *test)
if (flag != skip_slave) restart = TRUE;
// create files
- snprintf(master_opt_file, PATH_MAX, "%s/%s-master.opt", test_dir, test);
- snprintf(slave_opt_file, PATH_MAX, "%s/%s-slave.opt", test_dir, test);
- snprintf(slave_master_info_file, PATH_MAX, "%s/%s.slave-mi", test_dir, test);
- snprintf(reject_file, PATH_MAX, "%s/%s%s", result_dir, test, REJECT_SUFFIX);
- snprintf(out_file, PATH_MAX, "%s/%s%s", result_dir, test, OUT_SUFFIX);
- snprintf(err_file, PATH_MAX, "%s/%s%s", result_dir, test, ERR_SUFFIX);
+ my_snprintf(master_opt_file, PATH_MAX, "%s/%s-master.opt", test_dir, test);
+ my_snprintf(slave_opt_file, PATH_MAX, "%s/%s-slave.opt", test_dir, test);
+ my_snprintf(slave_master_info_file, PATH_MAX, "%s/%s.slave-mi", test_dir, test);
+ my_snprintf(reject_file, PATH_MAX, "%s/%s%s", result_dir, test, REJECT_SUFFIX);
+ my_snprintf(out_file, PATH_MAX, "%s/%s%s", result_dir, test, OUT_SUFFIX);
+ my_snprintf(err_file, PATH_MAX, "%s/%s%s", result_dir, test, ERR_SUFFIX);
// netware specific files
- snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, NW_TEST_SUFFIX);
+ my_snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, NW_TEST_SUFFIX);
if (stat(test_file, &info))
{
- snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, TEST_SUFFIX);
+ my_snprintf(test_file, PATH_MAX, "%s/%s%s", test_dir, test, TEST_SUFFIX);
}
- snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, NW_RESULT_SUFFIX);
+ my_snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, NW_RESULT_SUFFIX);
if (stat(result_file, &info))
{
- snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, RESULT_SUFFIX);
+ my_snprintf(result_file, PATH_MAX, "%s/%s%s", result_dir, test, RESULT_SUFFIX);
}
// init scripts
- snprintf(master_init_script, PATH_MAX, "%s/%s-master.sh", test_dir, test);
+ my_snprintf(master_init_script, PATH_MAX, "%s/%s-master.sh", test_dir, test);
if (stat(master_init_script, &info))
- master_init_script[0] = NULL;
+ master_init_script[0] = '\0';
else
restart = TRUE;
- snprintf(slave_init_script, PATH_MAX, "%s/%s-slave.sh", test_dir, test);
+ my_snprintf(slave_init_script, PATH_MAX, "%s/%s-slave.sh", test_dir, test);
if (stat(slave_init_script, &info))
- slave_init_script[0] = NULL;
+ slave_init_script[0] = '\0';
else
restart = TRUE;
@@ -810,7 +850,7 @@ void run_test(char *test)
sleep(1);
// show test
- log("%-46s ", test);
+ mtr_log("%-46s ", test);
// args
init_args(&al);
@@ -827,26 +867,16 @@ void run_test(char *test)
add_arg(&al, "-R");
add_arg(&al, "%s", result_file);
- // start timer
- NXGetTime(NX_SINCE_BOOT, NX_USECONDS, &start);
-
// spawn
err = spawn(mysqltest_file, &al, TRUE, test_file, out_file, err_file);
-
- // stop timer
- NXGetTime(NX_SINCE_BOOT, NX_USECONDS, &stop);
-
- // calculate
- elapsed = ((double)(stop - start)) / NX_USECONDS;
- total_time += elapsed;
-
+
// free args
free_args(&al);
if (err == 0)
{
// pass
- rstr = TEST_PASS;
+ rstr = (char *)TEST_PASS;
++total_pass;
// increment total
@@ -855,13 +885,13 @@ void run_test(char *test)
else if (err == 2)
{
// skip
- rstr = TEST_SKIP;
+ rstr = (char *)TEST_SKIP;
++total_skip;
}
else if (err == 1)
{
// fail
- rstr = TEST_FAIL;
+ rstr = (char *)TEST_FAIL;
++total_fail;
// increment total
@@ -869,21 +899,21 @@ void run_test(char *test)
}
else
{
- rstr = TEST_BAD;
+ rstr = (char *)TEST_BAD;
}
}
else // early skips
{
// show test
- log("%-46s ", test);
+ mtr_log("%-46s ", test);
// skip
- rstr = TEST_SKIP;
+ rstr = (char *)TEST_SKIP;
++total_skip;
}
// result
- log("%10.06f %-14s\n", elapsed, rstr);
+ mtr_log(" %-14s\n", rstr);
}
/******************************************************************************
@@ -893,7 +923,7 @@ void run_test(char *test)
Log the message.
******************************************************************************/
-void vlog(char *format, va_list ap)
+void vlog(const char *format, va_list ap)
{
vfprintf(stdout, format, ap);
fflush(stdout);
@@ -907,12 +937,12 @@ void vlog(char *format, va_list ap)
/******************************************************************************
- log()
+ mtr_log()
Log the message.
******************************************************************************/
-void log(char *format, ...)
+void mtr_log(const char *format, ...)
{
va_list ap;
@@ -925,60 +955,60 @@ void log(char *format, ...)
/******************************************************************************
- log_info()
+ mtr_log_info()
Log the given information.
******************************************************************************/
-void log_info(char *format, ...)
+void mtr_log_info(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- log("-- INFO : ");
+ mtr_log("-- INFO : ");
vlog(format, ap);
- log("\n");
+ mtr_log("\n");
va_end(ap);
}
/******************************************************************************
- log_error()
+ mtr_log_error()
Log the given error.
******************************************************************************/
-void log_error(char *format, ...)
+void mtr_log_error(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- log("-- ERROR: ");
+ mtr_log("-- ERROR: ");
vlog(format, ap);
- log("\n");
+ mtr_log("\n");
va_end(ap);
}
/******************************************************************************
- log_errno()
+ mtr_log_errno()
Log the given error and errno.
******************************************************************************/
-void log_errno(char *format, ...)
+void mtr_log_errno(const char *format, ...)
{
va_list ap;
va_start(ap, format);
- log("-- ERROR: (%003u) ", errno);
+ mtr_log("-- ERROR: (%003u) ", errno);
vlog(format, ap);
- log("\n");
+ mtr_log("\n");
va_end(ap);
}
@@ -990,11 +1020,13 @@ void log_errno(char *format, ...)
Exit the application.
******************************************************************************/
-void die(char *msg)
+void die(const char *msg)
{
- log_error(msg);
+ mtr_log_error(msg);
+#ifdef __NETWARE__
pressanykey();
+#endif
exit(-1);
}
@@ -1012,50 +1044,83 @@ void setup(char *file)
char *p;
// set the timezone for the timestamp test
+#ifdef __WIN__
+ _putenv( "TZ=GMT-3" );
+#else
setenv("TZ", "GMT-3", TRUE);
+#endif
// find base dir
- strcpy(temp, strlwr(file));
+ strcpy(temp, file);
+#ifndef __WIN__
+ casedn_str(temp);
+#endif
while((p = strchr(temp, '\\')) != NULL) *p = '/';
- if ((p = strindex(temp, "/mysql-test/")) != NULL)
+ if ((p = strstr(temp, "/mysql-test/")))
{
- *p = NULL;
- strcpy(base_dir, temp);
+ *p = '\0';
+ strcpy(base_dir, (const char *)temp);
}
// setup paths
- snprintf(bin_dir, PATH_MAX, "%s/bin", base_dir);
- snprintf(mysql_test_dir, PATH_MAX, "%s/mysql-test", base_dir);
- snprintf(test_dir, PATH_MAX, "%s/t", mysql_test_dir);
- snprintf(mysql_tmp_dir, PATH_MAX, "%s/var/tmp", mysql_test_dir);
- snprintf(result_dir, PATH_MAX, "%s/r", mysql_test_dir);
- snprintf(master_dir, PATH_MAX, "%s/var/master-data", mysql_test_dir);
- snprintf(slave_dir, PATH_MAX, "%s/var/slave-data", mysql_test_dir);
- snprintf(lang_dir, PATH_MAX, "%s/share/english", base_dir);
- snprintf(char_dir, PATH_MAX, "%s/share/charsets", base_dir);
+#ifdef __WIN__
+ my_snprintf(bin_dir, PATH_MAX, "%s/%s", base_dir,opt_exedir);
+#else
+ my_snprintf(bin_dir, PATH_MAX, "%s/bin", base_dir);
+#endif
+ my_snprintf(mysql_test_dir, PATH_MAX, "%s/mysql-test", base_dir);
+ my_snprintf(test_dir, PATH_MAX, "%s/t", mysql_test_dir);
+ my_snprintf(mysql_tmp_dir, PATH_MAX, "%s/var/tmp", mysql_test_dir);
+ my_snprintf(result_dir, PATH_MAX, "%s/r", mysql_test_dir);
+ my_snprintf(master_dir, PATH_MAX, "%s/var/master-data", mysql_test_dir);
+ my_snprintf(slave_dir, PATH_MAX, "%s/var/slave-data", mysql_test_dir);
+ my_snprintf(lang_dir, PATH_MAX, "%s/share/english", base_dir);
+ my_snprintf(char_dir, PATH_MAX, "%s/share/charsets", base_dir);
// setup files
- snprintf(mysqld_file, PATH_MAX, "%s/mysqld", bin_dir);
- snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest", bin_dir);
- snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
- snprintf(master_pid, PATH_MAX, "%s/var/run/master.pid", mysql_test_dir);
- snprintf(slave_pid, PATH_MAX, "%s/var/run/slave.pid", mysql_test_dir);
+#ifdef __WIN__
+ my_snprintf(mysqld_file, PATH_MAX, "%s/%s.exe", bin_dir, mysqld);
+ my_snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest.exe", bin_dir);
+ my_snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin.exe", bin_dir);
+ pid_mode= NOT_NEED_PID;
+#else
+ my_snprintf(mysqld_file, PATH_MAX, "%s/mysqld", bin_dir);
+ my_snprintf(mysqltest_file, PATH_MAX, "%s/mysqltest", bin_dir);
+ my_snprintf(mysqladmin_file, PATH_MAX, "%s/mysqladmin", bin_dir);
+#endif
+ my_snprintf(master_pid, PATH_MAX, "%s/var/run/master.pid", mysql_test_dir);
+ my_snprintf(slave_pid, PATH_MAX, "%s/var/run/slave.pid", mysql_test_dir);
// create log file
- snprintf(temp, PATH_MAX, "%s/mysql-test-run.log", mysql_test_dir);
+ my_snprintf(temp, PATH_MAX, "%s/mysql-test-run.log", mysql_test_dir);
if ((log_fd = fopen(temp, "w+")) == NULL)
{
- log_errno("Unable to create log file.");
+ mtr_log_errno("Unable to create log file.");
}
// prepare skip test list
while((p = strchr(skip_test, ',')) != NULL) *p = ' ';
- strcpy(temp, strlwr(skip_test));
- snprintf(skip_test, PATH_MAX, " %s ", temp);
+ strcpy(temp, skip_test);
+#ifndef __WIN__
+ casedn_str(temp);
+#endif
+ my_snprintf(skip_test, PATH_MAX, " %s ", temp);
// enviornment
- setenv("MYSQL_TEST_DIR", mysql_test_dir, 1);
+#ifdef __WIN__
+ my_snprintf(temp, PATH_MAX, "MYSQL_TEST_DIR=%s", mysql_test_dir);
+ _putenv(temp);
+#else
+ setenv("MYSQL_TEST_DIR", mysql_test_dir, 1);
+#endif
+}
+
+static my_bool
+get_one_option(int optid, const struct my_option *opt __attribute__((unused)),
+ char *argument)
+{
+ return(0);
}
/******************************************************************************
@@ -1065,31 +1130,59 @@ void setup(char *file)
******************************************************************************/
int main(int argc, char **argv)
{
+ int i;
+ uint ui;
+
+
+#ifdef __WIN__
+ int ho_error;
+ DWORD len= PATH_MAX;
+ char current_dir[PATH_MAX] = ".";
+
+ if ((ho_error=handle_options(&argc, &argv, my_long_options, get_one_option)))
+ exit(1);
+ if (len= GetCurrentDirectory(&len,current_dir))
+ {
+ current_dir[len]= '\\';
+ current_dir[len+1]='\0';
+ };
+ MY_INIT(current_dir);
+#endif
// setup
+#ifdef __WIN__
+ setup(current_dir);
+#else
setup(argv[0]);
-
+#endif
// header
- log("MySQL Server %s, for %s (%s)\n\n", VERSION, SYSTEM_TYPE, MACHINE_TYPE);
+ mtr_log("MySQL Server %s, for %s (%s)\n\n", VERSION, SYSTEM_TYPE, MACHINE_TYPE);
- log("Initializing Tests...\n");
+ mtr_log("Initializing Tests...\n");
// install test databases
mysql_install_db();
- log("Starting Tests...\n");
+ mtr_log("Starting Tests...\n");
- log("\n");
- log(HEADER);
- log(DASH);
+ mtr_log("\n");
+ mtr_log(HEADER);
+ mtr_log(DASH);
+#ifdef __WIN__
+ if (argc > 0)
+#else
if (argc > 1)
+#endif
{
- int i;
// single test
single_test = TRUE;
+#ifdef __WIN__
+ for (i = 0; i < argc; i++)
+#else
for (i = 1; i < argc; i++)
+#endif
{
// run given test
run_test(argv[i]);
@@ -1098,8 +1191,7 @@ int main(int argc, char **argv)
else
{
// run all tests
- DIR *dir = opendir(test_dir);
- DIR *entry;
+ MY_DIR *dir = my_dir(test_dir, MYF(MY_WME | MY_WANT_STAT));
char test[NAME_MAX];
char *p;
@@ -1111,14 +1203,16 @@ int main(int argc, char **argv)
die("Unable to open tests directory.");
}
- while((entry = readdir(dir)) != NULL)
+ for (ui = 0; ui < dir->number_off_files; ui++)
{
- if (!S_ISDIR(entry->d_type))
+ if (!MY_S_ISDIR(dir->dir_entry[ui].mystat.st_mode))
{
- strcpy(test, strlwr(entry->d_name));
-
+ strcpy(test, dir->dir_entry[ui].name);
+#ifndef __WIN__
+ casedn_str(test);
+#endif
// find the test suffix
- if ((p = strindex(test, TEST_SUFFIX)) != NULL)
+ if ((p = strstr(test, TEST_SUFFIX)))
{
// null terminate at the suffix
*p = '\0';
@@ -1128,14 +1222,14 @@ int main(int argc, char **argv)
}
}
}
-
- closedir(dir);
+
+ my_dirend(dir);
}
- log(DASH);
- log("\n");
+ mtr_log(DASH);
+ mtr_log("\n");
- log("Ending Tests...\n");
+ mtr_log("Ending Tests...\n");
// stop server
mysql_stop();
@@ -1146,8 +1240,10 @@ int main(int argc, char **argv)
// close log
if (log_fd) fclose(log_fd);
+#ifdef __NETWARE__
// keep results up
pressanykey();
+#endif
return 0;
}
diff --git a/scripts/make_binary_distribution.sh b/scripts/make_binary_distribution.sh
index 9d166fc1baa..f93d88dc025 100644
--- a/scripts/make_binary_distribution.sh
+++ b/scripts/make_binary_distribution.sh
@@ -85,8 +85,25 @@ do
fi
done
-for i in COPYING COPYING.LIB README Docs/INSTALL-BINARY \
- MySQLEULA.txt LICENSE.doc README.NW
+# Non platform-specific doc files:
+DOC_FILES=" \
+ COPYING COPYING.LIB README LICENSE.doc \
+ Docs/mysqlbug.txt \
+";
+
+# Platform-specific doc files:
+if [ $BASE_SYSTEM = "netware" ] ; then
+ DOC_FILES="$DOC_FILES \
+ README.NW \
+ ";
+# For all other platforms:
+else
+ DOC_FILES="$DOC_FILES \
+ Docs/INSTALL-BINARY MySQLEULA.txt \
+ ";
+fi
+
+for i in $DOC_FILES
do
if [ -f $i ]
then
@@ -94,7 +111,7 @@ do
fi
done
-# Non platform-specific bin dir files:
+# Non platform-specific bin files:
BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
extra/resolveip$BS extra/my_print_defaults$BS \
extra/resolve_stack_dump$BS extra/mysql_waitpid$BS \
@@ -104,18 +121,18 @@ BIN_FILES="extra/comp_err$BS extra/replace$BS extra/perror$BS \
client/mysql$BS client/mysqlshow$BS client/mysqladmin$BS \
client/mysqldump$BS client/mysqlimport$BS \
client/mysqltest$BS client/mysqlcheck$BS \
- client/mysqlbinlog$BS
+ client/mysqlbinlog$BS \
";
-# Platform-specific bin dir files:
+# Platform-specific bin files:
if [ $BASE_SYSTEM = "netware" ] ; then
BIN_FILES="$BIN_FILES \
netware/mysqld_safe$BS netware/mysql_install_db$BS \
netware/init_db.sql netware/test_db.sql netware/mysql_explain_log$BS \
- netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql
+ netware/mysqlhotcopy$BS netware/libmysql$BS netware/init_secure_db.sql \
";
+# For all other platforms:
else
- # For all other platforms:
BIN_FILES="$BIN_FILES \
client/mysqlmanagerc \
client/mysqlmanager-pwgen tools/mysqlmanager \
@@ -235,7 +252,9 @@ rm -f $BASE/bin/Makefile* $BASE/bin/*.in $BASE/bin/*.sh $BASE/bin/mysql_install_
# Make safe_mysqld a symlink to mysqld_safe for backwards portability
# To be removed in MySQL 4.1
-(cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
+if [ $BASE_SYSTEM != "netware" ] ; then
+ (cd $BASE/bin ; ln -s mysqld_safe safe_mysqld )
+fi
# Clean up if we did this from a bk tree
if [ -d $BASE/sql-bench/SCCS ] ; then
@@ -294,29 +313,48 @@ which_1 ()
exit 1
}
-#
-# Create the result tar file
-#
-
-tar=`which_1 gnutar gtar`
-if test "$?" = "1" -o "$tar" = ""
-then
- tar=tar
-fi
-
-echo "Using $tar to create archive"
cd $TMP
+
+if [ $BASE_SYSTEM = "netware" ] ; then
+
+ #
+ # Create a zip file for NetWare users
+ #
+
+ if test -e "$SOURCE/$NEW_NAME.zip"; then rm $SOURCE/$NEW_NAME.zip; fi
+ zip -r $SOURCE/$NEW_NAME.zip $NEW_NAME
+ echo "$NEW_NAME.zip created"
+
+else
+
+ #
+ # Create the result tar file
+ #
+
+ tar=`which_1 gnutar gtar`
+ if test "$?" = "1" -o "$tar" = ""
+ then
+ tar=tar
+ fi
+
+ echo "Using $tar to create archive"
+
+ OPT=cvf
+ if [ x$SILENT = x1 ] ; then
+ OPT=cf
+ fi
+
+ $tar $OPT $SOURCE/$NEW_NAME.tar $NEW_NAME
+ cd $SOURCE
+
+ echo "Compressing archive"
+ gzip -9 $NEW_NAME.tar
+
+ echo "$NEW_NAME.tar.gz created"
-OPT=cvf
-if [ x$SILENT = x1 ] ; then
- OPT=cf
fi
-$tar $OPT $SOURCE/$NEW_NAME.tar $NEW_NAME
-cd $SOURCE
-echo "Compressing archive"
-gzip -9 $NEW_NAME.tar
echo "Removing temporary directory"
rm -r -f $BASE
-echo "$NEW_NAME.tar.gz created"
+
diff --git a/scripts/mysql_create_system_tables.sh b/scripts/mysql_create_system_tables.sh
index a65004bec4c..c702a97f93d 100644
--- a/scripts/mysql_create_system_tables.sh
+++ b/scripts/mysql_create_system_tables.sh
@@ -39,6 +39,7 @@ c_hc=""
c_hr=""
c_hk=""
i_ht=""
+c_p=""
# Check for old tables
if test ! -f $mdata/db.frm
@@ -285,6 +286,22 @@ then
c_hr="$c_hr comment='keyword-topic relation';"
fi
+if test ! -f $mdata/proc.frm
+then
+ c_p="$c_p CREATE TABLE proc ("
+ c_p="$c_p name char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p type enum('function','procedure') NOT NULL,"
+ c_p="$c_p body blob DEFAULT '' NOT NULL,"
+ c_p="$c_p creator char(77) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p modified timestamp,"
+ c_p="$c_p created timestamp,"
+ c_p="$c_p suid enum ('N', 'Y') DEFAULT 'Y' NOT NULL,"
+ c_p="$c_p comment char(64) binary DEFAULT '' NOT NULL,"
+ c_p="$c_p PRIMARY KEY (name,type)"
+ c_p="$c_p )"
+ c_p="$c_p comment='Stored Procedures';"
+fi
+
cat << END_OF_DATA
use mysql;
$c_d
@@ -306,5 +323,8 @@ $c_ht
$c_hc
$c_hr
$c_hk
+
+$c_p
+
END_OF_DATA
diff --git a/scripts/mysql_fix_privilege_tables.sql b/scripts/mysql_fix_privilege_tables.sql
index de5779b9ac0..4b571722bac 100644
--- a/scripts/mysql_fix_privilege_tables.sql
+++ b/scripts/mysql_fix_privilege_tables.sql
@@ -134,3 +134,20 @@ name varchar(64) not null,
primary key (help_keyword_id),
unique index (name)
) comment='help keywords';
+
+#
+# Create proc table if it doesn't exists
+#
+
+CREATE TABLE IF NOT EXISTS proc (
+ name char(64) binary DEFAULT '' NOT NULL,
+ type enum('function','procedure') NOT NULL,
+ body blob DEFAULT '' NOT NULL,
+ creator char(77) binary DEFAULT '' NOT NULL,
+ modified timestamp,
+ created timestamp,
+ suid enum ('N', 'Y') DEFAULT 'Y' NOT NULL,
+ comment char(64) binary DEFAULT '' NOT NULL,
+ PRIMARY KEY (name,type)
+) comment='Stored Procedures';
+
diff --git a/sql/Makefile.am b/sql/Makefile.am
index 69b9c58dd6d..b809021b3b5 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -58,6 +58,7 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
log_event.h sql_repl.h slave.h \
stacktrace.h sql_sort.h sql_cache.h set_var.h \
spatial.h gstream.h client_settings.h
+ sp_head.h sp_pcontext.h sp_rcontext.h sp.h sp_cache.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
item_cmpfunc.cc item_strfunc.cc item_timefunc.cc \
@@ -86,7 +87,9 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc \
slave.cc sql_repl.cc sql_union.cc sql_derived.cc \
client.c sql_client.cc mini_client_errors.c pack.c\
stacktrace.c repl_failsafe.h repl_failsafe.cc sql_olap.cc\
- gstream.cc spatial.cc sql_help.cc protocol_cursor.cc
+ gstream.cc spatial.cc sql_help.cc protocol_cursor.cc \
+ sp_head.cc sp_pcontext.cc sp_rcontext.cc sp.cc \
+ sp_cache.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 1b481924690..a1c7b5f421f 100644
--- a/sql/filesort.cc
+++ b/sql/filesort.cc
@@ -330,7 +330,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")));
@@ -774,15 +774,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;
@@ -1128,7 +1128,7 @@ get_addon_fields(THD *thd, Field **ptabfield, uint sortlength, uint *plength)
The fact is the filter 'field->query_id != thd->query_id'
doesn't work for alter table
*/
- if (thd->lex.sql_command != SQLCOM_SELECT)
+ if (thd->lex->sql_command != SQLCOM_SELECT)
return 0;
for (pfield= ptabfield; (field= *pfield) ; pfield++)
{
diff --git a/sql/ha_berkeley.cc b/sql/ha_berkeley.cc
index 5b0fc95442c..64b3fdc1746 100644
--- a/sql/ha_berkeley.cc
+++ b/sql/ha_berkeley.cc
@@ -2109,7 +2109,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_innodb.cc b/sql/ha_innodb.cc
index 7f8c99f7c15..c10eec73274 100644
--- a/sql/ha_innodb.cc
+++ b/sql/ha_innodb.cc
@@ -2253,8 +2253,8 @@ ha_innobase::write_row(
skip_auto_inc_decr = FALSE;
if (error == DB_DUPLICATE_KEY
- && (user_thd->lex.sql_command == SQLCOM_REPLACE
- || user_thd->lex.sql_command
+ && (user_thd->lex->sql_command == SQLCOM_REPLACE
+ || user_thd->lex->sql_command
== SQLCOM_REPLACE_SELECT)) {
skip_auto_inc_decr= TRUE;
diff --git a/sql/ha_myisam.cc b/sql/ha_myisam.cc
index 864ee55a85f..53c1e89df4a 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,...)
@@ -390,7 +390,7 @@ int ha_myisam::analyze(THD *thd, HA_CHECK_OPT* check_opt)
int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
{
HA_CHECK_OPT tmp_check_opt;
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;
@@ -430,7 +430,7 @@ int ha_myisam::restore(THD* thd, HA_CHECK_OPT *check_opt)
int ha_myisam::backup(THD* thd, HA_CHECK_OPT *check_opt)
{
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
int error;
diff --git a/sql/item.cc b/sql/item.cc
index ea1eaf29373..46447e4d9ec 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"
static void mark_as_dependent(THD *thd,
SELECT_LEX *last, SELECT_LEX *current,
@@ -39,7 +40,7 @@ void item_init(void)
}
Item::Item():
- fixed(0)
+ name_length(0), fixed(0)
{
marker= 0;
maybe_null=null_value=with_sum_func=unsigned_flag=0;
@@ -54,13 +55,13 @@ Item::Item():
command => we should check thd->lex.current_select on zero (thd->lex
can be uninitialised)
*/
- if (thd->lex.current_select)
+ if (thd->lex->current_select)
{
SELECT_LEX_NODE::enum_parsing_place place=
- thd->lex.current_select->parsing_place;
+ thd->lex->current_select->parsing_place;
if (place == SELECT_LEX_NODE::SELECT_LIST ||
place == SELECT_LEX_NODE::IN_HAVING)
- thd->lex.current_select->select_n_having_items++;
+ thd->lex->current_select->select_n_having_items++;
}
}
@@ -122,6 +123,7 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
{
/* Empty string, used by AS or internal function like last_insert_id() */
name= (char*) str;
+ name_length= 0;
return;
}
while (length && !my_isgraph(cs,*str))
@@ -132,12 +134,12 @@ void Item::set_name(const char *str, uint length, CHARSET_INFO *cs)
if (!my_charset_same(cs, system_charset_info))
{
uint32 res_length;
- name= sql_strmake_with_convert(str, length, cs,
+ name= sql_strmake_with_convert(str, name_length= length, cs,
MAX_ALIAS_NAME, system_charset_info,
&res_length);
}
else
- name=sql_strmake(str, min(length,MAX_ALIAS_NAME));
+ name= sql_strmake(str, (name_length= min(length,MAX_ALIAS_NAME)));
}
@@ -207,6 +209,23 @@ CHARSET_INFO * Item::default_charset() const
return current_thd->variables.collation_connection;
}
+
+Item *
+Item_splocal::this_item()
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
+Item *
+Item_splocal::this_const_item() const
+{
+ THD *thd= current_thd;
+
+ return thd->spcont->get_item(m_offset);
+}
+
bool DTCollation::aggregate(DTCollation &dt)
{
if (!my_charset_same(collation, dt.collation))
@@ -777,7 +796,7 @@ static void mark_as_dependent(THD *thd, SELECT_LEX *last, SELECT_LEX *current,
// store pointer on SELECT_LEX from wich item is dependent
item->depended_from= last;
current->mark_as_dependent(last);
- if (thd->lex.describe)
+ if (thd->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED),
@@ -817,7 +836,7 @@ bool Item_field::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
Item **refer= (Item **)not_found_item;
uint counter;
// Prevent using outer fields in subselects, that is not supported now
- SELECT_LEX *cursel=(SELECT_LEX *) thd->lex.current_select;
+ SELECT_LEX *cursel=(SELECT_LEX *) thd->lex->current_select;
if (cursel->master_unit()->first_select()->linkage != DERIVED_TABLE_TYPE)
for (SELECT_LEX *sl= cursel->outer_select();
sl;
@@ -1360,17 +1379,17 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
if (!ref)
{
TABLE_LIST *where= 0, *table_list;
- SELECT_LEX *sl= thd->lex.current_select->outer_select();
+ SELECT_LEX *sl= thd->lex->current_select->outer_select();
/*
Finding only in current select will be performed for selects that have
not outer one and for derived tables (which not support using outer
fields for now)
*/
if ((ref= find_item_in_list(this,
- *(thd->lex.current_select->get_item_list()),
+ *(thd->lex->current_select->get_item_list()),
&counter,
((sl &&
- thd->lex.current_select->master_unit()->
+ thd->lex->current_select->master_unit()->
first_select()->linkage !=
DERIVED_TABLE_TYPE) ?
REPORT_EXCEPT_NOT_FOUND :
@@ -1420,7 +1439,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
{
// Call to report error
find_item_in_list(this,
- *(thd->lex.current_select->get_item_list()),
+ *(thd->lex->current_select->get_item_list()),
&counter,
REPORT_ALL_ERRORS);
ref= 0;
@@ -1432,7 +1451,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
Item_field* fld;
if (!((*reference)= fld= new Item_field(tmp)))
return 1;
- mark_as_dependent(thd, last, thd->lex.current_select, fld);
+ mark_as_dependent(thd, last, thd->lex->current_select, fld);
return 0;
}
else
@@ -1443,7 +1462,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
- mark_as_dependent(thd, last, thd->lex.current_select,
+ mark_as_dependent(thd, last, thd->lex->current_select,
this);
ref= last->ref_pointer_array + counter;
}
@@ -1458,7 +1477,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
"forward reference in item list");
return -1;
}
- ref= thd->lex.current_select->ref_pointer_array + counter;
+ ref= thd->lex->current_select->ref_pointer_array + counter;
}
}
@@ -1471,8 +1490,8 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference)
*/
if (((*ref)->with_sum_func && name &&
(depended_from ||
- !(thd->lex.current_select->linkage != GLOBAL_OPTIONS_TYPE &&
- thd->lex.current_select->having_fix_field))) ||
+ !(thd->lex->current_select->linkage != GLOBAL_OPTIONS_TYPE &&
+ thd->lex->current_select->having_fix_field))) ||
!(*ref)->fixed)
{
my_error(ER_ILLEGAL_REFERENCE, MYF(0), name,
diff --git a/sql/item.h b/sql/item.h
index c738f92124f..971279b9e1e 100644
--- a/sql/item.h
+++ b/sql/item.h
@@ -106,6 +106,7 @@ public:
my_string name; /* Name from select */
Item *next;
uint32 max_length;
+ uint name_length; /* Length of name */
uint8 marker,decimals;
my_bool maybe_null; /* If item may be null */
my_bool null_value; /* if item is null */
@@ -209,6 +210,9 @@ public:
virtual bool remove_dependence_processor(byte * arg) { return 0; }
virtual bool remove_fixed(byte * arg) { fixed= 0; return 0; }
+ virtual Item *this_item() { return this; } /* For SPs mostly. */
+ virtual Item *this_const_item() const { return const_cast<Item*>(this); } /* For SPs mostly. */
+
// Row emulation
virtual uint cols() { return 1; }
virtual Item* el(uint i) { return this; }
@@ -223,6 +227,73 @@ 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::maybe_null= TRUE;
+ }
+
+ 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);
+ }
+
+ inline Item_result result_type() const
+ {
+ return this_const_item()->result_type();
+ }
+
+ inline bool const_item() const
+ {
+ return FALSE;
+ }
+
+ inline int save_in_field(Field *field, bool no_conversions)
+ {
+ return this_item()->save_in_field(field, no_conversions);
+ }
+};
+
+
class st_select_lex;
class Item_ident :public Item
{
diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc
index 4e6301d2626..58a726840c5 100644
--- a/sql/item_cmpfunc.cc
+++ b/sql/item_cmpfunc.cc
@@ -1641,7 +1641,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref)
maybe_null=1;
}
if (thd)
- thd->lex.current_select->cond_count+=list.elements;
+ thd->lex->current_select->cond_count+=list.elements;
fix_length_and_dec();
fixed= 1;
return 0;
diff --git a/sql/item_create.cc b/sql/item_create.cc
index fce59d68c1f..e01fc793de1 100644
--- a/sql/item_create.cc
+++ b/sql/item_create.cc
@@ -76,7 +76,7 @@ Item *create_func_ceiling(Item* a)
Item *create_func_connection_id(void)
{
THD *thd=current_thd;
- thd->lex.safe_to_cache_query=0;
+ thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong)
((thd->slave_thread) ?
thd->variables.pseudo_thread_id :
@@ -148,7 +148,7 @@ Item *create_func_floor(Item* a)
Item *create_func_found_rows(void)
{
THD *thd=current_thd;
- thd->lex.safe_to_cache_query=0;
+ thd->lex->safe_to_cache_query=0;
return new Item_int(NullS,(longlong) thd->found_rows(),21);
}
@@ -159,7 +159,7 @@ Item *create_func_from_days(Item* a)
Item *create_func_get_lock(Item* a, Item *b)
{
- current_thd->lex.uncacheable();
+ current_thd->lex->uncacheable();
return new Item_func_get_lock(a, b);
}
@@ -324,7 +324,7 @@ Item *create_func_radians(Item *a)
Item *create_func_release_lock(Item* a)
{
- current_thd->lex.uncacheable();
+ current_thd->lex->uncacheable();
return new Item_func_release_lock(a);
}
@@ -445,7 +445,7 @@ Item *create_func_year(Item* a)
Item *create_load_file(Item* a)
{
- current_thd->lex.uncacheable();
+ current_thd->lex->uncacheable();
return new Item_load_file(a);
}
@@ -471,13 +471,13 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, CHARSET_INFO *cs
Item *create_func_is_free_lock(Item* a)
{
- current_thd->lex.uncacheable();
+ current_thd->lex->uncacheable();
return new Item_func_is_free_lock(a);
}
Item *create_func_is_used_lock(Item* a)
{
- current_thd->lex.uncacheable();
+ current_thd->lex->uncacheable();
return new Item_func_is_used_lock(a);
}
diff --git a/sql/item_func.cc b/sql/item_func.cc
index 929f85a112d..b3a72e8cb29 100644
--- a/sql/item_func.cc
+++ b/sql/item_func.cc
@@ -28,6 +28,9 @@
#include <time.h>
#include <ft_global.h>
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp.h"
static void my_coll_agg_error(DTCollation &c1, DTCollation &c2,
const char *fname)
@@ -1457,11 +1460,16 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
const_item_cache&=item->const_item();
f_args.arg_type[i]=item->result_type();
}
+ //TODO: why all folowing memory is not allocated with 1 call of sql_alloc?
if (!(buffers=new String[arg_count]) ||
!(f_args.args= (char**) sql_alloc(arg_count * sizeof(char *))) ||
- !(f_args.lengths=(ulong*) sql_alloc(arg_count * sizeof(long))) ||
- !(f_args.maybe_null=(char*) sql_alloc(arg_count * sizeof(char))) ||
- !(num_buffer= (char*) sql_alloc(ALIGN_SIZE(sizeof(double))*arg_count)))
+ !(f_args.lengths= (ulong*) sql_alloc(arg_count * sizeof(long))) ||
+ !(f_args.maybe_null= (char*) sql_alloc(arg_count * sizeof(char))) ||
+ !(num_buffer= (char*) sql_alloc(arg_count *
+ ALIGN_SIZE(sizeof(double)))) ||
+ !(f_args.attributes= (char**) sql_alloc(arg_count * sizeof(char *))) ||
+ !(f_args.attribute_lengths= (ulong*) sql_alloc(arg_count *
+ sizeof(long))))
{
free_udf(u_d);
DBUG_RETURN(1);
@@ -1480,8 +1488,10 @@ udf_handler::fix_fields(THD *thd, TABLE_LIST *tables, Item_result_field *func,
for (uint i=0; i < arg_count; i++)
{
f_args.args[i]=0;
- f_args.lengths[i]=arguments[i]->max_length;
- f_args.maybe_null[i]=(char) arguments[i]->maybe_null;
+ f_args.lengths[i]= arguments[i]->max_length;
+ f_args.maybe_null[i]= (char) arguments[i]->maybe_null;
+ f_args.attributes[i]= arguments[i]->name;
+ f_args.attribute_lengths[i]= arguments[i]->name_length;
switch(arguments[i]->type()) {
case Item::STRING_ITEM: // Constant string !
@@ -2389,8 +2399,7 @@ void Item_func_get_user_var::fix_length_and_dec()
if (!(var_entry= get_variable(&thd->user_vars, name, 0)))
null_value= 1;
-
- if (!(opt_bin_log && is_update_query(thd->lex.sql_command)))
+ if (!(opt_bin_log && is_update_query(thd->lex->sql_command)))
return;
if (!var_entry)
@@ -2842,7 +2851,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, LEX_STRING name,
}
if (!(item=var->item(thd, var_type, component_name)))
return 0; // Impossible
- thd->lex.uncacheable();
+ thd->lex->uncacheable();
buff[0]='@';
buff[1]='@';
pos=buff+2;
@@ -2882,7 +2891,7 @@ Item *get_system_var(THD *thd, enum_var_type var_type, const char *var_name,
DBUG_ASSERT(var != 0);
if (!(item=var->item(thd, var_type, &null_lex_string)))
return 0; // Impossible
- thd->lex.uncacheable();
+ thd->lex->uncacheable();
item->set_name(item_name, 0, system_charset_info); // Will use original name
return item;
}
@@ -2943,3 +2952,74 @@ longlong Item_func_is_used_lock::val_int()
null_value=0;
return ull->thread_id;
}
+
+int
+Item_func_sp::execute(Item **itp)
+{
+ DBUG_ENTER("Item_func_sp::execute");
+ THD *thd= current_thd;
+
+ if (! m_sp)
+ m_sp= sp_find_function(thd, &m_name);
+ if (! m_sp)
+ DBUG_RETURN(-1);
+
+ DBUG_RETURN(m_sp->execute_function(thd, args, arg_count, itp));
+}
+
+enum enum_field_types
+Item_func_sp::field_type() const
+{
+ DBUG_ENTER("Item_func_sp::field_type");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_PRINT("info", ("m_returns = %d", m_sp->m_returns));
+ DBUG_RETURN(m_sp->m_returns);
+ }
+ DBUG_RETURN(MYSQL_TYPE_STRING);
+}
+
+Item_result
+Item_func_sp::result_type() const
+{
+ DBUG_ENTER("Item_func_sp::result_type");
+ DBUG_PRINT("info", ("m_sp = %p", m_sp));
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, const_cast<LEX_STRING*>(&m_name));
+ if (m_sp)
+ {
+ DBUG_RETURN(m_sp->result());
+ }
+ DBUG_RETURN(STRING_RESULT);
+}
+
+void
+Item_func_sp::fix_length_and_dec()
+{
+ DBUG_ENTER("Item_func_sp::fix_length_and_dec");
+
+ if (! m_sp)
+ m_sp= sp_find_function(current_thd, &m_name);
+ if (m_sp)
+ {
+ switch (m_sp->result()) {
+ case STRING_RESULT:
+ maybe_null= 1;
+ max_length= 0;
+ break;
+ case REAL_RESULT:
+ decimals= NOT_FIXED_DEC;
+ max_length= float_length(decimals);
+ break;
+ case INT_RESULT:
+ decimals= 0;
+ max_length= 21;
+ break;
+ }
+ }
+ DBUG_VOID_RETURN;
+}
diff --git a/sql/item_func.h b/sql/item_func.h
index 8086e65786d..6d07d30b588 100644
--- a/sql/item_func.h
+++ b/sql/item_func.h
@@ -46,7 +46,8 @@ public:
SP_TOUCHES_FUNC,SP_CROSSES_FUNC,SP_WITHIN_FUNC,
SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC,
SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING,
- SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN};
+ SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN,
+ GUSERVAR_FUNC};
enum optimize_type { OPTIMIZE_NONE,OPTIMIZE_KEY,OPTIMIZE_OP, OPTIMIZE_NULL };
enum Type type() const { return FUNC_ITEM; }
virtual enum Functype functype() const { return UNKNOWN_FUNC; }
@@ -927,6 +928,8 @@ class Item_func_get_user_var :public Item_func
public:
Item_func_get_user_var(LEX_STRING a):
Item_func(), name(a) {}
+ enum Functype functype() const { return GUSERVAR_FUNC; }
+ LEX_STRING get_name() { return name; }
double val();
longlong val_int();
String *val_str(String* str);
@@ -1060,3 +1063,69 @@ enum Cast_target
ITEM_CAST_BINARY, ITEM_CAST_SIGNED_INT, ITEM_CAST_UNSIGNED_INT,
ITEM_CAST_DATE, ITEM_CAST_TIME, ITEM_CAST_DATETIME, ITEM_CAST_CHAR
};
+
+
+/*
+ *
+ * Stored FUNCTIONs
+ *
+ */
+
+class sp_head;
+
+class Item_func_sp :public Item_func
+{
+private:
+ LEX_STRING m_name;
+ mutable sp_head *m_sp;
+
+ int execute(Item **itp);
+
+public:
+
+ Item_func_sp(LEX_STRING name)
+ :Item_func(), m_name(name), m_sp(NULL)
+ {}
+
+ Item_func_sp(LEX_STRING name, List<Item> &list)
+ :Item_func(list), m_name(name), m_sp(NULL)
+ {}
+
+ virtual ~Item_func_sp()
+ {}
+
+ const char *func_name() const
+ {
+ return m_name.str;
+ }
+
+ enum enum_field_types field_type() const;
+
+ Item_result result_type() const;
+
+ longlong val_int()
+ {
+ return (longlong)Item_func_sp::val();
+ }
+
+ double val()
+ {
+ Item *it;
+
+ if (execute(&it))
+ return 0.0;
+ return it->val();
+ }
+
+ String *val_str(String *str)
+ {
+ Item *it;
+
+ if (execute(&it))
+ return NULL;
+ return it->val_str(str);
+ }
+
+ void fix_length_and_dec();
+
+};
diff --git a/sql/item_subselect.cc b/sql/item_subselect.cc
index 5e0221ad6c7..07d2d0ec0df 100644
--- a/sql/item_subselect.cc
+++ b/sql/item_subselect.cc
@@ -203,7 +203,7 @@ Item_singlerow_subselect::select_transformer(JOIN *join)
{
have_to_be_excluded= 1;
- if (join->thd->lex.describe)
+ if (join->thd->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
@@ -562,16 +562,16 @@ Item_in_subselect::single_value_transformer(JOIN *join,
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
- SELECT_LEX *current= thd->lex.current_select, *up;
+ SELECT_LEX *current= thd->lex->current_select, *up;
- thd->lex.current_select= up= current->return_after_parsing();
+ thd->lex->current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0))
{
- thd->lex.current_select= current;
+ thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
}
- thd->lex.current_select= current;
+ thd->lex->current_select= current;
/*
As far as Item_ref_in_optimizer do not substitude itself on fix_fields
@@ -665,7 +665,7 @@ Item_in_subselect::single_value_transformer(JOIN *join,
// fix_field of item will be done in time of substituting
substitution= item;
have_to_be_excluded= 1;
- if (thd->lex.describe)
+ if (thd->lex->describe)
{
char warn_buff[MYSQL_ERRMSG_SIZE];
sprintf(warn_buff, ER(ER_SELECT_REDUCED), select_lex->select_number);
@@ -696,15 +696,15 @@ Item_in_subselect::row_value_transformer(JOIN *join,
SELECT_LEX_UNIT *unit= select_lex->master_unit();
substitution= optimizer= new Item_in_optimizer(left_expr, this);
- SELECT_LEX *current= thd->lex.current_select, *up;
- thd->lex.current_select= up= current->return_after_parsing();
+ SELECT_LEX *current= thd->lex->current_select, *up;
+ thd->lex->current_select= up= current->return_after_parsing();
//optimizer never use Item **ref => we can pass 0 as parameter
if (!optimizer || optimizer->fix_left(thd, up->get_table_list(), 0))
{
- thd->lex.current_select= current;
+ thd->lex->current_select= current;
DBUG_RETURN(RES_ERROR);
}
- thd->lex.current_select= current;
+ thd->lex->current_select= current;
unit->dependent= 1;
}
@@ -816,8 +816,8 @@ int subselect_single_select_engine::prepare()
return 1;
}
prepared= 1;
- SELECT_LEX *save_select= thd->lex.current_select;
- thd->lex.current_select= select_lex;
+ SELECT_LEX *save_select= thd->lex->current_select;
+ thd->lex->current_select= select_lex;
if (join->prepare(&select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
select_lex->with_wild,
@@ -830,7 +830,7 @@ int subselect_single_select_engine::prepare()
(ORDER*) 0, select_lex,
select_lex->master_unit(), 0))
return 1;
- thd->lex.current_select= save_select;
+ thd->lex->current_select= save_select;
return 0;
}
@@ -936,8 +936,8 @@ int subselect_single_select_engine::exec()
{
DBUG_ENTER("subselect_single_select_engine::exec");
char const *save_where= join->thd->where;
- SELECT_LEX *save_select= join->thd->lex.current_select;
- join->thd->lex.current_select= select_lex;
+ SELECT_LEX *save_select= join->thd->lex->current_select;
+ join->thd->lex->current_select= select_lex;
if (!optimized)
{
optimized=1;
@@ -945,7 +945,7 @@ int subselect_single_select_engine::exec()
{
join->thd->where= save_where;
executed= 1;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error?join->error:1);
}
if (item->engine_changed)
@@ -958,7 +958,7 @@ int subselect_single_select_engine::exec()
if (join->reinit())
{
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(1);
}
item->reset();
@@ -969,11 +969,11 @@ int subselect_single_select_engine::exec()
join->exec();
executed= 1;
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(join->error||thd->is_fatal_error);
}
join->thd->where= save_where;
- join->thd->lex.current_select= save_select;
+ join->thd->lex->current_select= save_select;
DBUG_RETURN(0);
}
diff --git a/sql/item_sum.cc b/sql/item_sum.cc
index 672126a2664..00798b13225 100644
--- a/sql/item_sum.cc
+++ b/sql/item_sum.cc
@@ -57,7 +57,7 @@ Item_sum::Item_sum(THD *thd, Item_sum &item):
void Item_sum::mark_as_sum_func()
{
- current_thd->lex.current_select->with_sum_func= 1;
+ current_thd->lex->current_select->with_sum_func= 1;
with_sum_func= 1;
}
@@ -1140,7 +1140,7 @@ void Item_sum_count_distinct::make_unique()
bool Item_sum_count_distinct::setup(THD *thd)
{
List<Item> list;
- SELECT_LEX *select_lex= thd->lex.current_select;
+ SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
return 1;
@@ -1635,7 +1635,7 @@ Item_func_group_concat::Item_func_group_concat(bool is_distinct,
quick_group= 0;
mark_as_sum_func();
item_thd= current_thd;
- SELECT_LEX *select_lex= item_thd->lex.current_select;
+ SELECT_LEX *select_lex= item_thd->lex->current_select;
order= 0;
group_concat_max_len= item_thd->variables.group_concat_max_len;
@@ -1816,7 +1816,7 @@ bool Item_func_group_concat::setup(THD *thd)
{
DBUG_ENTER("Item_func_group_concat::setup");
List<Item> list;
- SELECT_LEX *select_lex= thd->lex.current_select;
+ SELECT_LEX *select_lex= thd->lex->current_select;
if (select_lex->linkage == GLOBAL_OPTIONS_TYPE)
DBUG_RETURN(1);
diff --git a/sql/lex.h b/sql/lex.h
index 5d79e378d4f..e30bb186c6d 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,13 +104,17 @@ static SYMBOL symbols[] = {
{ "COMMITTED", SYM(COMMITTED_SYM),0,0},
{ "COMPRESSED", SYM(COMPRESSED_SYM),0,0},
{ "CONCURRENT", SYM(CONCURRENT),0,0},
+ { "CONDITION", SYM(CONDITION_SYM),0,0},
+ { "CONNECTION", SYM(CONNECTION_SYM),0,0},
{ "CONSTRAINT", SYM(CONSTRAINT),0,0},
+ { "CONTINUE", SYM(CONTINUE_SYM),0,0},
{ "CREATE", SYM(CREATE),0,0},
{ "CROSS", SYM(CROSS),0,0},
{ "CUBE", SYM(CUBE_SYM),0,0},
{ "CURRENT_DATE", SYM(CURDATE),0,0},
{ "CURRENT_TIME", SYM(CURTIME),0,0},
{ "CURRENT_TIMESTAMP", SYM(NOW_SYM),0,0},
+ { "CURSOR", SYM(CURSOR_SYM),0,0},
{ "DATA", SYM(DATA_SYM),0,0},
{ "DATABASE", SYM(DATABASE),0,0},
{ "DATABASES", SYM(DATABASES),0,0},
@@ -121,6 +127,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},
@@ -144,6 +151,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},
@@ -151,10 +159,13 @@ static SYMBOL symbols[] = {
{ "ENUM", SYM(ENUM),0,0},
{ "EVENTS", SYM(EVENTS_SYM),0,0},
{ "EXECUTE", SYM(EXECUTE_SYM),0,0},
- { "EXPLAIN", SYM(DESCRIBE),0,0},
{ "EXISTS", SYM(EXISTS),0,0},
+ { "EXIT", SYM(EXIT_SYM),0,0},
+ { "EXPLAIN", SYM(DESCRIBE),0,0},
{ "EXTENDED", SYM(EXTENDED_SYM),0,0},
+ { "FALSE", SYM(FALSE_SYM),0,0},
{ "FAST", SYM(FAST_SYM),0,0},
+ { "FETCH", SYM(FETCH_SYM),0,0},
{ "FIELDS", SYM(COLUMNS),0,0},
{ "FILE", SYM(FILE_SYM),0,0},
{ "FIRST", SYM(FIRST_SYM),0,0},
@@ -163,9 +174,9 @@ static SYMBOL symbols[] = {
{ "FLOAT4", SYM(FLOAT_SYM),0,0},
{ "FLOAT8", SYM(DOUBLE_SYM),0,0},
{ "FLUSH", SYM(FLUSH_SYM),0,0},
- { "FALSE", SYM(FALSE_SYM),0,0},
{ "FOREIGN", SYM(FOREIGN),0,0},
{ "FORCE", SYM(FORCE_SYM),0,0},
+ { "FOUND", SYM(FOUND_SYM),0,0},
{ "RAID_TYPE", SYM(RAID_TYPE),0,0},
{ "RAID_CHUNKS", SYM(RAID_CHUNKS),0,0},
{ "RAID_CHUNKSIZE", SYM(RAID_CHUNKSIZE),0,0},
@@ -174,7 +185,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},
{ "GET_FORMAT", SYM(GET_FORMAT),0,0},
@@ -203,6 +214,8 @@ static SYMBOL symbols[] = {
{ "INNOBASE", SYM(INNOBASE_SYM),0,0},
{ "INNODB", SYM(INNOBASE_SYM),0,0},
{ "IMPORT", SYM(IMPORT),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},
@@ -220,12 +233,14 @@ static SYMBOL symbols[] = {
{ "ISOLATION", SYM(ISOLATION),0,0},
{ "ISAM", SYM(ISAM_SYM),0,0},
{ "ISSUER", SYM(ISSUER_SYM),0,0},
+ { "ITERATE", SYM(ITERATE_SYM),0,0},
{ "JOIN", SYM(JOIN_SYM),0,0},
{ "KEY", SYM(KEY_SYM),0,0},
{ "KEYS", SYM(KEYS),0,0},
{ "KILL", SYM(KILL_SYM),0,0},
{ "LAST", SYM(LAST_SYM),0,0},
{ "LEADING", SYM(LEADING),0,0},
+ { "LEAVE", SYM(LEAVE_SYM),0,0},
{ "LEAVES", SYM(LEAVES),0,0},
{ "LEFT", SYM(LEFT),0,0},
{ "LEVEL", SYM(LEVEL_SYM),0,0},
@@ -242,6 +257,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},
@@ -306,6 +322,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},
@@ -336,13 +353,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 */
@@ -356,6 +375,7 @@ static SYMBOL symbols[] = {
{ "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0},
{ "SEPARATOR", SYM(SEPARATOR_SYM),0,0},
{ "SELECT", SYM(SELECT_SYM),0,0},
+ { "SENSITIVE", SYM(SENSITIVE_SYM),0,0},
{ "SERIAL", SYM(SERIAL_SYM),0,0},
{ "SERIALIZABLE", SYM(SERIALIZABLE_SYM),0,0},
{ "SESSION", SYM(SESSION_SYM),0,0},
@@ -370,6 +390,9 @@ 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},
+ { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM),0,0},
+ { "SQLWARNING", SYM(SQLWARNING_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},
@@ -408,15 +431,16 @@ static SYMBOL symbols[] = {
{ "TYPE", SYM(TYPE_SYM),0,0},
{ "TYPES", SYM(TYPES_SYM),0,0},
{ "UNCOMMITTED", SYM(UNCOMMITTED_SYM),0,0},
+ { "UNDO", SYM(UNDO_SYM),0,0},
{ "UNICODE", SYM(UNICODE_SYM),0,0},
{ "UNION", SYM(UNION_SYM),0,0},
{ "UNIQUE", SYM(UNIQUE_SYM),0,0},
{ "UNLOCK", SYM(UNLOCK_SYM),0,0},
{ "UNSIGNED", SYM(UNSIGNED),0,0},
+ { "UNTIL", SYM(UNTIL_SYM),0,0},
{ "USE", SYM(USE_SYM),0,0},
{ "USE_FRM", SYM(USE_FRM),0,0},
{ "USER", SYM(USER),0,0},
- { "UNTIL", SYM(UNTIL_SYM),0,0},
{ "USING", SYM(USING),0,0},
{ "UTC_DATE", SYM(UTC_DATE_SYM),0,0},
{ "UTC_TIME", SYM(UTC_TIME_SYM),0,0},
@@ -436,6 +460,7 @@ static SYMBOL symbols[] = {
{ "WRITE", SYM(WRITE_SYM),0,0},
{ "WHEN", SYM(WHEN_SYM),0,0},
{ "WHERE", SYM(WHERE),0,0},
+ { "WHILE", SYM(WHILE_SYM),0,0},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG),0,0},
{ "XOR", SYM(XOR),0,0},
{ "X509", SYM(X509_SYM),0,0},
@@ -622,7 +647,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 29795415720..0a2f91812a7 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -152,7 +152,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 27c7c64f9c8..f8e7c5ecb4e 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;
static bool test_if_number(const char *str,
long *res, bool allow_wildcards);
@@ -1069,7 +1069,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,
@@ -1538,8 +1538,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,
@@ -1555,15 +1554,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)
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- && (thd->master_access & SUPER_ACL)
-#endif
- )
- {
- VOID(pthread_mutex_unlock(&LOCK_log));
- return 0;
- }
if (!(specialflag & SPECIAL_SHORT_LOG_FORMAT) || query_start_arg)
{
current_time=time(NULL);
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 62dc56e7fd2..90f56834973 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -794,7 +794,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 83cd303b438..bf2acd5550d 100644
--- a/sql/mysql_priv.h
+++ b/sql/mysql_priv.h
@@ -49,7 +49,7 @@ char *sql_strmake_with_convert(const char *str, uint32 arg_length,
CHARSET_INFO *from_cs,
uint32 max_res_length,
CHARSET_INFO *to_cs, uint32 *result_length);
-void kill_one_thread(THD *thd, ulong id);
+void kill_one_thread(THD *thd, ulong id, bool only_kill_query);
bool net_request_file(NET* net, const char* fname);
char* query_table_status(THD *thd,const char *db,const char *table_name);
@@ -395,7 +395,7 @@ bool is_update_query(enum enum_sql_command command);
void free_items(Item *item);
bool alloc_query(THD *thd, char *packet, ulong packet_length);
void mysql_init_select(LEX *lex);
-void mysql_init_query(THD *thd);
+void mysql_init_query(THD *thd, bool lexonly=0);
bool mysql_new_select(LEX *lex, bool move_down);
void create_select_for_variable(const char *var_name);
void mysql_init_multi_delete(LEX *lex);
@@ -406,7 +406,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);
@@ -1013,22 +1013,22 @@ SQL_CRYPT *get_crypt_for_frm(void);
inline bool add_item_to_list(THD *thd, Item *item)
{
- return thd->lex.current_select->add_item_to_list(thd, item);
+ return thd->lex->current_select->add_item_to_list(thd, item);
}
inline bool add_value_to_list(THD *thd, Item *value)
{
- return thd->lex.value_list.push_back(value);
+ return thd->lex->value_list.push_back(value);
}
inline bool add_order_to_list(THD *thd, Item *item, bool asc)
{
- return thd->lex.current_select->add_order_to_list(thd, item, asc);
+ return thd->lex->current_select->add_order_to_list(thd, item, asc);
}
inline bool add_group_to_list(THD *thd, Item *item, bool asc)
{
- return thd->lex.current_select->add_group_to_list(thd, item, asc);
+ return thd->lex->current_select->add_group_to_list(thd, item, asc);
}
inline void mark_as_null_row(TABLE *table)
diff --git a/sql/mysqld.cc b/sql/mysqld.cc
index da7d3df0251..2cbd40b21ba 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
@@ -36,6 +37,8 @@
#include <thr_alarm.h>
#include <ft_global.h>
#include <errmsg.h>
+#include "sp_rcontext.h"
+#include "sp_cache.h"
#define mysqld_charset &my_charset_latin1
@@ -610,7 +613,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;
@@ -793,6 +796,7 @@ static void __cdecl kill_server(int sig_ptr)
unireg_abort(1); /* purecov: inspected */
else
unireg_end();
+
#ifdef __NETWARE__
pthread_join(select_thread, NULL); // wait for main thread
#endif /* __NETWARE__ */
@@ -884,7 +888,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
@@ -1262,12 +1265,12 @@ static void server_init(void)
void yyerror(const char *s)
{
THD *thd=current_thd;
- char *yytext=(char*) thd->lex.tok_start;
+ char *yytext=(char*) thd->lex->tok_start;
/* "parse error" changed into "syntax error" between bison 1.75 and 1.875 */
if (strcmp(s,"parse error") == 0 || strcmp(s,"syntax error") == 0)
s=ER(ER_SYNTAX_ERROR);
net_printf(thd,ER_PARSE_ERROR, s, yytext ? (char*) yytext : "",
- thd->lex.yylineno);
+ thd->lex->yylineno);
}
@@ -1397,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
@@ -1880,12 +1883,16 @@ extern "C" int my_message_sql(uint error, const char *str,
DBUG_PRINT("error", ("Message: '%s'", str));
if ((thd= current_thd))
{
+ if (thd->spcont && thd->spcont->find_handler(error))
+ {
+ DBUG_RETURN(0);
+ }
/*
thd->lex.current_select equel to zero if lex structure is not inited
(not query command (COM_QUERY))
*/
- if (thd->lex.current_select &&
- thd->lex.current_select->no_error && !thd->is_fatal_error)
+ if (thd->lex->current_select &&
+ thd->lex->current_select->no_error && !thd->is_fatal_error)
{
DBUG_PRINT("error", ("above error converted to warning"));
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_ERROR, error, str);
@@ -2073,7 +2080,6 @@ static int init_common_variables(const char *conf_file_name, int argc,
before MY_INIT(). So we do it here.
*/
mysql_log.init_pthread_objects();
- mysql_update_log.init_pthread_objects();
mysql_slow_log.init_pthread_objects();
mysql_bin_log.init_pthread_objects();
@@ -2196,6 +2202,7 @@ static int init_thread_environment()
(void) pthread_mutex_init(&LOCK_rpl_status, MY_MUTEX_INIT_FAST);
(void) pthread_cond_init(&COND_rpl_status, NULL);
#endif
+ sp_cache_init();
/* Parameter for threads created for connections */
(void) pthread_attr_init(&connection_attrib);
(void) pthread_attr_setdetachstate(&connection_attrib,
@@ -2253,9 +2260,55 @@ static int init_server_components()
LOG_NORMAL, 0, 0, 0);
if (opt_update_log)
{
- open_log(&mysql_update_log, glob_hostname, opt_update_logname, "",
- NullS, LOG_NEW, 0, 0, 0);
- using_update_log=1;
+ /*
+ Update log is removed since 5.0. But we still accept the option.
+ The idea is if the user already uses the binlog and the update log,
+ we completely ignore any option/variable related to the update log, like
+ if the update log did not exist. But if the user uses only the update log,
+ then we translate everything into binlog for him (with warnings).
+ Implementation of the above :
+ - If mysqld is started with --log-update and --log-bin,
+ ignore --log-update (print a warning), push a warning when SQL_LOG_UPDATE
+ is used, and turn off --sql-bin-update-same.
+ This will completely ignore SQL_LOG_UPDATE
+ - If mysqld is started with --log-update only,
+ change it to --log-bin (with the filename passed to log-update,
+ plus '-bin') (print a warning), push a warning when SQL_LOG_UPDATE is
+ used, and turn on --sql-bin-update-same.
+ This will translate SQL_LOG_UPDATE to SQL_LOG_BIN.
+
+ Note that we tell the user that --sql-bin-update-same is deprecated and
+ does nothing, and we don't take into account if he used this option or
+ not; but internally we give this variable a value to have the behaviour we
+ want (i.e. have SQL_LOG_UPDATE influence SQL_LOG_BIN or not).
+ As sql-bin-update-same, log-update and log-bin cannot be changed by the user
+ after starting the server (they are not variables), the user will not
+ later interfere with the settings we do here.
+ */
+ if (opt_bin_log)
+ {
+ opt_sql_bin_update= 0;
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log.");
+ }
+ else
+ {
+ opt_sql_bin_update= 1;
+ opt_bin_log= 1;
+ if (opt_update_logname)
+ {
+ // as opt_bin_log==0, no need to free opt_bin_logname
+ if (!(opt_bin_logname= my_strdup(opt_update_logname, MYF(MY_WME))))
+ exit(EXIT_OUT_OF_MEMORY);
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin='%s' instead.",opt_bin_logname);
+ }
+ else
+ sql_print_error("The update log is no longer supported by MySQL in \
+version 5.0 and above. It is replaced by the binary log. Now starting MySQL \
+with --log-bin instead.");
+ }
}
if (opt_slow_log)
open_log(&mysql_slow_log, glob_hostname, opt_slow_logname, "-slow.log",
@@ -2940,7 +2993,7 @@ static void create_new_thread(THD *thd)
("Can't create thread to handle request (error %d)",
error));
thread_count--;
- thd->killed=1; // Safety
+ thd->killed= THD::KILL_CONNECTION; // Safety
(void) pthread_mutex_unlock(&LOCK_thread_count);
statistic_increment(aborted_connects,&LOCK_status);
net_printf(thd,ER_CANT_CREATE_THREAD,error);
@@ -3833,7 +3886,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,
@@ -4111,9 +4165,9 @@ replicating a LOAD DATA INFILE command.",
(gptr*) &mysqld_unix_port, (gptr*) &mysqld_unix_port, 0, GET_STR,
REQUIRED_ARG, 0, 0, 0, 0, 0, 0},
{"sql-bin-update-same", OPT_SQL_BIN_UPDATE_SAME,
- "If set, setting SQL_LOG_BIN to a value will automatically set SQL_LOG_UPDATE to the same value and vice versa.",
- (gptr*) &opt_sql_bin_update, (gptr*) &opt_sql_bin_update, 0, GET_BOOL,
- NO_ARG, 0, 0, 0, 0, 0, 0},
+ "The update log is deprecated since version 5.0, is replaced by the binary \
+log and this option does nothing anymore.",
+ 0, 0, 0, GET_BOOL, NO_ARG, 0, 0, 0, 0, 0, 0},
{"sql-mode", OPT_SQL_MODE,
"Syntax: sql-mode=option[,option[,option...]] where option can be one of: REAL_AS_FLOAT, PIPES_AS_CONCAT, ANSI_QUOTES, IGNORE_SPACE, ONLY_FULL_GROUP_BY, NO_UNSIGNED_SUBTRACTION.",
(gptr*) &sql_mode_str, (gptr*) &sql_mode_str, 0, GET_STR, REQUIRED_ARG, 0,
diff --git a/sql/protocol.cc b/sql/protocol.cc
index 0fe759cff67..44c1cedf0fd 100644
--- a/sql/protocol.cc
+++ b/sql/protocol.cc
@@ -24,6 +24,7 @@
#endif
#include "mysql_priv.h"
+#include "sp_rcontext.h"
#include <stdarg.h>
#ifndef EMBEDDED_LIBRARY
@@ -62,6 +63,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
err ? err : net->last_error[0] ?
net->last_error : "NULL"));
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
query_cache_abort(net);
#endif
@@ -141,6 +146,10 @@ void send_error(THD *thd, uint sql_errno, const char *err)
void send_warning(THD *thd, uint sql_errno, const char *err)
{
DBUG_ENTER("send_warning");
+ if (thd->spcont && thd->spcont->find_handler(sql_errno))
+ {
+ DBUG_VOID_RETURN;
+ }
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_WARN, sql_errno,
err ? err : ER(sql_errno));
send_ok(thd);
@@ -171,6 +180,10 @@ net_printf(THD *thd, uint errcode, ...)
DBUG_ENTER("net_printf");
DBUG_PRINT("enter",("message: %u",errcode));
+ if (thd->spcont && thd->spcont->find_handler(errcode))
+ {
+ DBUG_VOID_RETURN;
+ }
thd->query_error= 1; // needed to catch query errors during replication
#ifndef EMBEDDED_LIBRARY
query_cache_abort(net); // Safety
@@ -338,7 +351,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));
}
@@ -1125,12 +1138,3 @@ bool Protocol_prep::store_time(TIME *tm)
buff[0]=(char) length; // Length is stored first
return packet->append(buff, length+1, PACKET_BUFFET_EXTRA_ALLOC);
}
-
-#ifdef EMBEDDED_LIBRARY
-/* Should be removed when we define the Protocol_cursor's future */
-bool Protocol_cursor::write()
-{
- return Protocol_simple::write();
-}
-#endif
-
diff --git a/sql/protocol.h b/sql/protocol.h
index 94fd303e259..6aa9b6414ae 100644
--- a/sql/protocol.h
+++ b/sql/protocol.h
@@ -47,17 +47,13 @@ public:
Protocol(THD *thd) { init(thd); }
virtual ~Protocol() {}
void init(THD* thd);
- bool send_fields(List<Item> *list, uint flag);
+ virtual bool send_fields(List<Item> *list, uint flag);
bool send_records_num(List<Item> *list, ulonglong records);
bool store(I_List<i_string> *str_list);
bool store(const char *from, CHARSET_INFO *cs);
String *storage_packet() { return packet; }
inline void free() { packet->free(); }
-#ifndef EMBEDDED_LIBRARY
- bool write();
-#else
virtual bool write();
-#endif
inline bool store(uint32 from)
{ return store_long((longlong) from); }
inline bool store(longlong from)
@@ -158,6 +154,7 @@ public:
Protocol_cursor(THD *thd, MEM_ROOT *ini_alloc) :Protocol_simple(thd), alloc(ini_alloc) {}
bool prepare_for_send(List<Item> *item_list)
{
+ row_count= 0;
fields= NULL;
data= NULL;
prev_record= &data;
@@ -165,6 +162,7 @@ public:
}
bool send_fields(List<Item> *list, uint flag);
bool write();
+ uint get_field_count() { return field_count; }
};
void send_warning(THD *thd, uint sql_errno, const char *err=0);
diff --git a/sql/protocol_cursor.cc b/sql/protocol_cursor.cc
index 19e3bb06d74..abb391fbd67 100644
--- a/sql/protocol_cursor.cc
+++ b/sql/protocol_cursor.cc
@@ -51,6 +51,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name= strdup_root(alloc, server_field.col_name);
client_field->org_table= strdup_root(alloc, server_field.org_table_name);
client_field->org_name= strdup_root(alloc, server_field.org_col_name);
+ client_field->catalog= strdup_root(alloc, "");
client_field->length= server_field.length;
client_field->type= server_field.type;
client_field->flags= server_field.flags;
@@ -60,6 +61,7 @@ bool Protocol_cursor::send_fields(List<Item> *list, uint flag)
client_field->name_length= strlen(client_field->name);
client_field->org_name_length= strlen(client_field->org_name);
client_field->org_table_length= strlen(client_field->org_table);
+ client_field->catalog_length= 0;
client_field->charsetnr= server_field.charsetnr;
if (INTERNAL_NUM_FIELD(client_field))
@@ -100,17 +102,17 @@ bool Protocol_cursor::write()
byte *to;
new_record= (MYSQL_ROWS *)alloc_root(alloc,
- sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
+ sizeof(MYSQL_ROWS) + (field_count + 1)*sizeof(char *) + packet->length());
if (!new_record)
goto err;
data= (byte **)(new_record + 1);
new_record->data= (char **)data;
- to= (byte *)(fields + field_count + 1);
+ to= (byte *)data + (field_count + 1)*sizeof(char *);
for (; cur_field < fields_end; ++cur_field, ++data)
{
- if ((len=net_field_length((uchar **)&cp)))
+ if ((len= net_field_length((uchar **)&cp)) == 0)
{
*data= 0;
}
@@ -121,6 +123,7 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, CR_MALFORMED_PACKET);
return TRUE;
}
+ *data= to;
memcpy(to,(char*) cp,len);
to[len]=0;
to+=len+1;
@@ -129,6 +132,7 @@ bool Protocol_cursor::write()
cur_field->max_length=len;
}
}
+ *data= 0;
*prev_record= new_record;
prev_record= &new_record->next;
@@ -139,5 +143,3 @@ bool Protocol_cursor::write()
// TODO error signal send_error(thd, ER_OUT_OF_RESOURCES);
return TRUE;
}
-
-
diff --git a/sql/records.cc b/sql/records.cc
index 7ba9ff0f42f..b29b113a1bc 100644
--- a/sql/records.cc
+++ b/sql/records.cc
@@ -174,7 +174,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/repl_failsafe.cc b/sql/repl_failsafe.cc
index aba887be070..4d8d1380139 100644
--- a/sql/repl_failsafe.cc
+++ b/sql/repl_failsafe.cc
@@ -435,7 +435,7 @@ int show_new_master(THD* thd)
DBUG_ENTER("show_new_master");
List<Item> field_list;
char errmsg[SLAVE_ERRMSG_SIZE];
- LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO* lex_mi = &thd->lex->mi;
errmsg[0]=0; // Safety
if (translate_master(thd, lex_mi, errmsg))
diff --git a/sql/set_var.cc b/sql/set_var.cc
index 48580960399..e312ec952ef 100644
--- a/sql/set_var.cc
+++ b/sql/set_var.cc
@@ -79,6 +79,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);
@@ -320,7 +321,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,
@@ -1355,7 +1356,7 @@ byte *sys_var_thd_enum::value_ptr(THD *thd, enum_var_type type,
bool sys_var_thd_bit::update(THD *thd, set_var *var)
{
int res= (*update_func)(thd, var);
- thd->lex.select_lex.options=thd->options;
+ thd->lex->select_lex.options=thd->options;
return res;
}
@@ -1948,6 +1949,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);
@@ -1955,6 +1980,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 bb7cd90b4c4..7e992880c45 100644
--- a/sql/share/czech/errmsg.txt
+++ b/sql/share/czech/errmsg.txt
@@ -292,3 +292,30 @@ character-set=latin2
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt
index 48a4fee22f8..b2bb5f58525 100644
--- a/sql/share/danish/errmsg.txt
+++ b/sql/share/danish/errmsg.txt
@@ -286,3 +286,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt
index 98fb9278cc2..f8e5fafa198 100644
--- a/sql/share/dutch/errmsg.txt
+++ b/sql/share/dutch/errmsg.txt
@@ -294,3 +294,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt
index 9a266c41669..085a1e7389f 100644
--- a/sql/share/english/errmsg.txt
+++ b/sql/share/english/errmsg.txt
@@ -283,3 +283,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt
index e596863907f..b70649390b6 100644
--- a/sql/share/estonian/errmsg.txt
+++ b/sql/share/estonian/errmsg.txt
@@ -288,3 +288,30 @@ character-set=latin7
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt
index a81b8859692..10c3c72c371 100644
--- a/sql/share/french/errmsg.txt
+++ b/sql/share/french/errmsg.txt
@@ -283,3 +283,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt
index 9554822a5ff..37d15882798 100644
--- a/sql/share/german/errmsg.txt
+++ b/sql/share/german/errmsg.txt
@@ -295,3 +295,30 @@ character-set=latin1
"Es wird empfohlen, mit --skip-slave-start zu starten, wenn mit START SLAVE UNTIL eine Schritt-für-Schritt-Replikation ausgeführt wird. Ansonsten gibt es Probleme, wenn der Slave-Server unerwartet neu startet",
"SQL-Thread soll nicht gestartet werden. Daher werden UNTIL-Optionen ignoriert"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt
index 073289562b3..9ea7b97f07f 100644
--- a/sql/share/greek/errmsg.txt
+++ b/sql/share/greek/errmsg.txt
@@ -283,3 +283,30 @@ character-set=greek
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt
index c62a7c7b1ae..99246b28298 100644
--- a/sql/share/hungarian/errmsg.txt
+++ b/sql/share/hungarian/errmsg.txt
@@ -285,3 +285,30 @@ character-set=latin2
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt
index 004d7d00994..18ecc9e4539 100644
--- a/sql/share/italian/errmsg.txt
+++ b/sql/share/italian/errmsg.txt
@@ -283,3 +283,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt
index 888ac449a8b..e17cfa9c315 100644
--- a/sql/share/japanese/errmsg.txt
+++ b/sql/share/japanese/errmsg.txt
@@ -285,3 +285,30 @@ character-set=ujis
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt
index a72d6c3fdee..9e1e85c959b 100644
--- a/sql/share/korean/errmsg.txt
+++ b/sql/share/korean/errmsg.txt
@@ -283,3 +283,30 @@ character-set=euckr
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt
index 3c6dca27016..798434f9720 100644
--- a/sql/share/norwegian-ny/errmsg.txt
+++ b/sql/share/norwegian-ny/errmsg.txt
@@ -285,3 +285,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt
index bafc635a184..58f607b91ae 100644
--- a/sql/share/norwegian/errmsg.txt
+++ b/sql/share/norwegian/errmsg.txt
@@ -285,3 +285,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt
index 0aa25aabb44..4770cd5332e 100644
--- a/sql/share/polish/errmsg.txt
+++ b/sql/share/polish/errmsg.txt
@@ -287,3 +287,30 @@ character-set=latin2
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt
index 33d8f68291d..e0d93563665 100644
--- a/sql/share/portuguese/errmsg.txt
+++ b/sql/share/portuguese/errmsg.txt
@@ -244,7 +244,7 @@ character-set=latin1
"Slave SQL thread ignorado a consulta devido às normas de replicação-*-tabela"
"Definição errada da chave estrangeira para '%-.64s': %s",
"Referência da chave e referência da tabela não coincidem",
-"Operand should contain %d column(s)",
+"Error de cardinalidade (mais/menos que %d colunas)",
"Subconsulta retorna mais que 1 registro",
"Desconhecido manipulador de declaração preparado (%ld) determinado para %s",
"Banco de dado de ajuda corrupto ou não existente",
@@ -284,3 +284,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt
index ff46caf2c6c..d715847c27e 100644
--- a/sql/share/romanian/errmsg.txt
+++ b/sql/share/romanian/errmsg.txt
@@ -287,3 +287,30 @@ character-set=latin2
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt
index f8bdb8bf4a5..d96a4fca293 100644
--- a/sql/share/russian/errmsg.txt
+++ b/sql/share/russian/errmsg.txt
@@ -285,3 +285,30 @@ character-set=koi8r
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt
index cede04e36ed..cb6645dd67f 100644
--- a/sql/share/serbian/errmsg.txt
+++ b/sql/share/serbian/errmsg.txt
@@ -278,3 +278,30 @@ character-set=cp1250
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt
index 0759f68a1bb..6f1f6a7e267 100644
--- a/sql/share/slovak/errmsg.txt
+++ b/sql/share/slovak/errmsg.txt
@@ -291,3 +291,30 @@ character-set=latin2
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt
index b9c1b297e43..ceb0458a2ac 100644
--- a/sql/share/spanish/errmsg.txt
+++ b/sql/share/spanish/errmsg.txt
@@ -285,3 +285,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt
index 456b93fbc4d..bb2711fd0b3 100644
--- a/sql/share/swedish/errmsg.txt
+++ b/sql/share/swedish/errmsg.txt
@@ -283,3 +283,30 @@ character-set=latin1
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt
index dba62fd60c8..5cdac39ce93 100644
--- a/sql/share/ukrainian/errmsg.txt
+++ b/sql/share/ukrainian/errmsg.txt
@@ -288,3 +288,30 @@ character-set=koi8u
"It is recommended to run with --skip-slave-start when doing step-by-step replication with START SLAVE UNTIL, otherwise you are not safe in case of unexpected slave's mysqld restart"
"SQL thread is not to be started so UNTIL options are ignored"
"Incorrect index name '%-.100s'",
+"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"
+"Undefined CONDITION: %s"
+"No RETURN found in FUNCTION %s"
+"FUNCTION %s ended without RETURN"
+"Cursor statement must be a SELECT"
+"Cursor SELECT must not have INTO"
+"Undefined CURSOR: %s"
+"Cursor is already open"
+"Cursor is not open"
+"Undeclared variable: %s"
+"Wrong number of FETCH variables"
+"No data to FETCH"
diff --git a/sql/slave.cc b/sql/slave.cc
index bb190762855..348c3e29c8c 100644
--- a/sql/slave.cc
+++ b/sql/slave.cc
@@ -567,7 +567,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());
}
}
}
@@ -2313,7 +2313,7 @@ err:
pthread_mutex_unlock(&data_lock);
DBUG_PRINT("exit",("killed: %d abort: %d slave_running: %d \
improper_arguments: %d timed_out: %d",
- (int) thd->killed,
+ thd->killed_errno(),
(int) (init_abort_pos_wait != abort_pos_wait),
(int) slave_running,
(int) (error == -2),
@@ -2718,7 +2718,7 @@ static int exec_relay_log_event(THD* thd, RELAY_LOG_INFO* rli)
thd->server_id = ev->server_id; // use the original server id for logging
thd->set_time(); // time the query
- thd->lex.current_select= 0;
+ thd->lex->current_select= 0;
if (!ev->when)
ev->when = time(NULL);
ev->thd = thd;
diff --git a/sql/sp.cc b/sql/sp.cc
new file mode 100644
index 00000000000..7076778532f
--- /dev/null
+++ b/sql/sp.cc
@@ -0,0 +1,444 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+
+#include "mysql_priv.h"
+#include "sp.h"
+#include "sp_head.h"
+#include "sp_cache.h"
+
+/*
+ *
+ * DB storage of Stored PROCEDUREs and FUNCTIONs
+ *
+ */
+
+// *opened=true means we opened ourselves
+static int
+db_find_routine_aux(THD *thd, int type, char *name, uint namelen,
+ enum thr_lock_type ltype, TABLE **tablep, bool *opened)
+{
+ DBUG_ENTER("db_find_routine_aux");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ byte key[65]; // We know name is 64 and the enum is 1 byte
+ uint keylen;
+ int ret;
+
+ // Put the key together
+ keylen= namelen;
+ if (keylen > sizeof(key)-1)
+ keylen= sizeof(key)-1;
+ memcpy(key, name, keylen);
+ memset(key+keylen, (int)' ', sizeof(key)-1 - keylen); // Pad with space
+ key[sizeof(key)-1]= type;
+ keylen= sizeof(key);
+
+ for (table= thd->open_tables ; table ; table= table->next)
+ if (strcmp(table->table_cache_key, "mysql") == 0 &&
+ strcmp(table->real_name, "proc") == 0)
+ break;
+ if (table)
+ *opened= FALSE;
+ else
+ {
+ TABLE_LIST tables;
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+ if (! (table= open_ltable(thd, &tables, ltype)))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_OPEN_TABLE_FAILED);
+ }
+ *opened= TRUE;
+ }
+
+ if (table->file->index_read_idx(table->record[0], 0,
+ key, keylen,
+ HA_READ_KEY_EXACT))
+ {
+ *tablep= NULL;
+ DBUG_RETURN(SP_KEY_NOT_FOUND);
+ }
+ *tablep= table;
+
+ DBUG_RETURN(SP_OK);
+}
+
+static int
+db_find_routine(THD *thd, int type, char *name, uint namelen, sp_head **sphp)
+{
+ DBUG_ENTER("db_find_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ extern int yyparse(void *thd);
+ TABLE *table;
+ const char *defstr;
+ int ret;
+ bool opened;
+ const char *creator;
+ longlong created;
+ longlong modified;
+ bool suid= 1;
+ char *ptr;
+ uint length;
+ char buff[65];
+ String str(buff, sizeof(buff), &my_charset_bin);
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_READ, &table, &opened);
+ if (ret != SP_OK)
+ goto done;
+ if ((defstr= get_field(&thd->mem_root, table->field[2])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ // Get additional information
+ if ((creator= get_field(&thd->mem_root, table->field[3])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+
+ modified= table->field[4]->val_int();
+ created= table->field[5]->val_int();
+
+ if ((ptr= get_field(&thd->mem_root, table->field[6])) == NULL)
+ {
+ ret= SP_GET_FIELD_FAILED;
+ goto done;
+ }
+ if (ptr[0] == 'N')
+ suid= 0;
+
+ table->field[7]->val_str(&str, &str);
+ ptr= 0;
+ if ((length= str.length()))
+ ptr= strmake_root(&thd->mem_root, str.ptr(), length);
+
+ if (opened)
+ {
+ close_thread_tables(thd, 0, 1);
+ opened= FALSE;
+ }
+
+ {
+ LEX *oldlex= thd->lex;
+ enum enum_sql_command oldcmd= thd->lex->sql_command;
+
+ lex_start(thd, (uchar*)defstr, strlen(defstr));
+ if (yyparse(thd) || thd->is_fatal_error || thd->lex->sphead == NULL)
+ {
+ LEX *newlex= thd->lex;
+ sp_head *sp= newlex->sphead;
+
+ if (sp)
+ {
+ if (oldlex != newlex)
+ sp->restore_lex(thd);
+ delete sp;
+ newlex->sphead= NULL;
+ }
+ ret= SP_PARSE_ERROR;
+ }
+ else
+ {
+ *sphp= thd->lex->sphead;
+ (*sphp)->sp_set_info((char *) creator, (uint) strlen(creator),
+ created, modified, suid,
+ ptr, length);
+ }
+ thd->lex->sql_command= oldcmd;
+ }
+
+ done:
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_create_routine(THD *thd, int type,
+ char *name, uint namelen, char *def, uint deflen,
+ char *comment, uint commentlen, bool suid)
+{
+ DBUG_ENTER("db_create_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s def: %*s", type, namelen, name, deflen, def));
+ int ret;
+ TABLE *table;
+ TABLE_LIST tables;
+ char creator[HOSTNAME_LENGTH+USERNAME_LENGTH+2];
+
+ memset(&tables, 0, sizeof(tables));
+ tables.db= (char*)"mysql";
+ tables.real_name= tables.alias= (char*)"proc";
+
+ if (! (table= open_ltable(thd, &tables, TL_WRITE)))
+ ret= SP_OPEN_TABLE_FAILED;
+ else
+ {
+ restore_record(table, default_values); // Get default values for fields
+ strxmov(creator, thd->user, "@", thd->host_or_ip, NullS);
+
+ table->field[0]->store(name, namelen, system_charset_info);
+ table->field[1]->store((longlong)type);
+ table->field[2]->store(def, deflen, system_charset_info);
+ table->field[3]->store(creator, (uint)strlen(creator), system_charset_info);
+ ((Field_timestamp *)table->field[5])->set_time();
+ if (suid)
+ table->field[6]->store((longlong)suid);
+ if (comment)
+ table->field[7]->store(comment, commentlen, system_charset_info);
+
+ if (table->file->write_row(table->record[0]))
+ ret= SP_WRITE_ROW_FAILED;
+ else
+ ret= SP_OK;
+ }
+
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+static int
+db_drop_routine(THD *thd, int type, char *name, uint namelen)
+{
+ DBUG_ENTER("db_drop_routine");
+ DBUG_PRINT("enter", ("type: %d name: %*s", type, namelen, name));
+ TABLE *table;
+ int ret;
+ bool opened;
+
+ ret= db_find_routine_aux(thd, type, name, namelen, TL_WRITE, &table, &opened);
+ if (ret == SP_OK)
+ {
+ if (table->file->delete_row(table->record[0]))
+ ret= SP_DELETE_ROW_FAILED;
+ }
+
+ if (opened)
+ close_thread_tables(thd);
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ *
+ * PROCEDURE
+ *
+ */
+
+sp_head *
+sp_find_procedure(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_procedure");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ sp= sp_cache_lookup(&thd->sp_proc_cache, name->str, name->length);
+ if (! sp)
+ {
+ if (db_find_routine(thd, TYPE_ENUM_PROCEDURE,
+ name->str, name->length, &sp) == SP_OK)
+ {
+ sp_cache_insert(&thd->sp_proc_cache, sp);
+ }
+ }
+
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_procedure(THD *thd, char *name, uint namelen, char *def, uint deflen,
+ char *comment, uint commentlen, bool suid)
+{
+ DBUG_ENTER("sp_create_procedure");
+ DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, def, deflen,
+ comment, commentlen, suid);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_procedure");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ sp_head *sp;
+ int ret;
+
+ sp= sp_cache_remove(&thd->sp_proc_cache, name, namelen);
+ if (sp)
+ delete sp;
+ ret= db_drop_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+
+/*
+ *
+ * FUNCTION
+ *
+ */
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name)
+{
+ DBUG_ENTER("sp_find_function");
+ sp_head *sp;
+
+ DBUG_PRINT("enter", ("name: %*s", name->length, name->str));
+
+ sp= sp_cache_lookup(&thd->sp_func_cache, name->str, name->length);
+ if (! sp)
+ {
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, &sp) != SP_OK)
+ sp= NULL;
+ }
+ DBUG_RETURN(sp);
+}
+
+int
+sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen,
+ char *comment, uint commentlen, bool suid)
+{
+ DBUG_ENTER("sp_create_function");
+ DBUG_PRINT("enter", ("name: %*s def: %*s", namelen, name, deflen, def));
+ int ret;
+
+ ret= db_create_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, def, deflen,
+ comment, commentlen, suid);
+
+ DBUG_RETURN(ret);
+}
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen)
+{
+ DBUG_ENTER("sp_drop_function");
+ DBUG_PRINT("enter", ("name: %*s", namelen, name));
+ sp_head *sp;
+ int ret;
+
+ sp= sp_cache_remove(&thd->sp_func_cache, name, namelen);
+ if (sp)
+ delete sp;
+ ret= db_drop_routine(thd, TYPE_ENUM_FUNCTION, name, namelen);
+
+ DBUG_RETURN(ret);
+}
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name)
+{
+ TABLE *table;
+ bool ret= FALSE;
+ bool opened= FALSE;
+
+ if (sp_cache_lookup(&thd->sp_func_cache, name->str, name->length) ||
+ db_find_routine_aux(thd, TYPE_ENUM_FUNCTION,
+ name->str, name->length, TL_READ,
+ &table, &opened) == SP_OK)
+ {
+ ret= TRUE;
+ }
+ if (opened)
+ close_thread_tables(thd, 0, 1);
+ return ret;
+}
+
+
+byte *
+sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first)
+{
+ LEX_STRING *lsp= (LEX_STRING *)ptr;
+ *plen= lsp->length;
+ return (byte *)lsp->str;
+}
+
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun)
+{
+ if (! hash_search(&lex->spfuns, (byte *)fun.str, fun.length))
+ {
+ LEX_STRING *ls= (LEX_STRING *)sql_alloc(sizeof(LEX_STRING));
+ ls->str= sql_strmake(fun.str, fun.length);
+ ls->length= fun.length;
+
+ my_hash_insert(&lex->spfuns, (byte *)ls);
+ }
+}
+
+void
+sp_merge_funs(LEX *dst, LEX *src)
+{
+ for (uint i=0 ; i < src->spfuns.records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(&src->spfuns, i);
+
+ if (! hash_search(&dst->spfuns, (byte *)ls->str, ls->length))
+ my_hash_insert(&dst->spfuns, (byte *)ls);
+ }
+}
+
+int
+sp_cache_functions(THD *thd, LEX *lex)
+{
+ HASH *h= &lex->spfuns;
+ int ret= 0;
+
+ for (uint i=0 ; i < h->records ; i++)
+ {
+ LEX_STRING *ls= (LEX_STRING *)hash_element(h, i);
+
+ if (! sp_cache_lookup(&thd->sp_func_cache, ls->str, ls->length))
+ {
+ sp_head *sp;
+ LEX *oldlex= thd->lex;
+ LEX *newlex= new st_lex;
+
+ thd->lex= newlex;
+ if (db_find_routine(thd, TYPE_ENUM_FUNCTION, ls->str, ls->length, &sp)
+ == SP_OK)
+ {
+ ret= sp_cache_functions(thd, newlex);
+ delete newlex;
+ thd->lex= oldlex;
+ if (ret)
+ break;
+ sp_cache_insert(&thd->sp_func_cache, sp);
+ }
+ else
+ {
+ delete newlex;
+ thd->lex= oldlex;
+ net_printf(thd, ER_SP_DOES_NOT_EXIST, "FUNCTION", ls->str);
+ ret= 1;
+ break;
+ }
+ }
+ }
+ return ret;
+}
diff --git a/sql/sp.h b/sql/sp.h
new file mode 100644
index 00000000000..bbf8832d55d
--- /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,
+ char *comment, uint commentlen, bool suid);
+
+int
+sp_drop_procedure(THD *thd, char *name, uint namelen);
+
+
+sp_head *
+sp_find_function(THD *thd, LEX_STRING *name);
+
+int
+sp_create_function(THD *thd, char *name, uint namelen, char *def, uint deflen,
+ char *comment, uint commentlen, bool suid);
+
+int
+sp_drop_function(THD *thd, char *name, uint namelen);
+
+// QQ Temporary until the function call detection in sql_lex has been reworked.
+bool
+sp_function_exists(THD *thd, LEX_STRING *name);
+
+
+// This is needed since we have to read the functions before we
+// do anything else.
+void
+sp_add_fun_to_lex(LEX *lex, LEX_STRING fun);
+void
+sp_merge_funs(LEX *dst, LEX *src);
+int
+sp_cache_functions(THD *thd, LEX *lex);
+
+#endif /* _SP_H_ */
diff --git a/sql/sp_cache.cc b/sql/sp_cache.cc
new file mode 100644
index 00000000000..7e9d5f92ef0
--- /dev/null
+++ b/sql/sp_cache.cc
@@ -0,0 +1,148 @@
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef __GNUC__
+#pragma implementation
+#endif
+
+#include "mysql_priv.h"
+#include "sp_cache.h"
+#include "sp_head.h"
+
+static pthread_mutex_t Cversion_lock;
+static ulong Cversion = 0;
+
+void
+sp_cache_init()
+{
+ pthread_mutex_init(&Cversion_lock, MY_MUTEX_INIT_FAST);
+}
+
+void
+sp_cache_clear(sp_cache **cp)
+{
+ sp_cache *c= *cp;
+
+ if (c)
+ {
+ delete c;
+ *cp= NULL;
+ }
+}
+
+void
+sp_cache_insert(sp_cache **cp, sp_head *sp)
+{
+ sp_cache *c= *cp;
+
+ if (! c)
+ c= new sp_cache();
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ if (*cp)
+ c->remove_all();
+ c->version= v;
+ }
+ c->insert(sp);
+ if (*cp == NULL)
+ *cp= c;
+ }
+}
+
+sp_head *
+sp_cache_lookup(sp_cache **cp, char *name, uint namelen)
+{
+ ulong v;
+ sp_cache *c= *cp;
+
+ if (! c)
+ return NULL;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ {
+ c->remove_all();
+ c->version= v;
+ return NULL;
+ }
+ return c->lookup(name, namelen);
+}
+
+sp_head *
+sp_cache_remove(sp_cache **cp, char *name, uint namelen)
+{
+ sp_cache *c= *cp;
+ sp_head *sp= NULL;
+
+ if (c)
+ {
+ ulong v;
+
+ pthread_mutex_lock(&Cversion_lock); // LOCK
+ v= Cversion++;
+ pthread_mutex_unlock(&Cversion_lock); // UNLOCK
+
+ if (c->version < v)
+ c->remove_all();
+ else
+ sp= c->remove(name, namelen);
+ c->version= v+1;
+ }
+ return sp;
+}
+
+
+static byte *
+hash_get_key_for_sp_head(const byte *ptr, uint *plen,
+ my_bool first)
+{
+ return ((sp_head*)ptr)->name(plen);
+}
+
+sp_cache::sp_cache()
+{
+ init();
+}
+
+sp_cache::~sp_cache()
+{
+ hash_free(&m_hashtable);
+}
+
+void
+sp_cache::init()
+{
+ hash_init(&m_hashtable, system_charset_info, 0, 0, 0,
+ hash_get_key_for_sp_head, 0, 0);
+ version= 0;
+}
+
+void
+sp_cache::cleanup()
+{
+ hash_free(&m_hashtable);
+}
diff --git a/sql/sp_cache.h b/sql/sp_cache.h
new file mode 100644
index 00000000000..3d38d6f1d1f
--- /dev/null
+++ b/sql/sp_cache.h
@@ -0,0 +1,101 @@
+/* -*- C++ -*- */
+/* Copyright (C) 2002 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifndef _SP_CACHE_H_
+#define _SP_CACHE_H_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+class sp_head;
+class sp_cache;
+
+/* Initialize the SP caching once at startup */
+void sp_cache_init();
+
+/* Clear the cache *cp and set *cp to NULL */
+void sp_cache_clear(sp_cache **cp);
+
+/* Insert an SP to cache. If 'cp' points to NULL, it's set to a new cache */
+void sp_cache_insert(sp_cache **cp, sp_head *sp);
+
+/* Lookup an SP in cache */
+sp_head *sp_cache_lookup(sp_cache **cp, char *name, uint namelen);
+
+/* Remove an SP from cache */
+sp_head *sp_cache_remove(sp_cache **cp, char *name, uint namelen);
+
+
+/*
+ *
+ * The cache class. Don't use this directly, use the C API above
+ *
+ */
+
+class sp_cache
+{
+public:
+
+ ulong version;
+
+ sp_cache();
+
+ ~sp_cache();
+
+ void
+ init();
+
+ void
+ cleanup();
+
+ inline void
+ insert(sp_head *sp)
+ {
+ my_hash_insert(&m_hashtable, (const byte *)sp);
+ }
+
+ inline sp_head *
+ lookup(char *name, uint namelen)
+ {
+ return (sp_head *)hash_search(&m_hashtable, (const byte *)name, namelen);
+ }
+
+ inline sp_head *
+ remove(char *name, uint namelen)
+ {
+ sp_head *sp= lookup(name, namelen);
+
+ if (sp)
+ hash_delete(&m_hashtable, (byte *)sp);
+ return sp;
+ }
+
+ inline void
+ remove_all()
+ {
+ cleanup();
+ init();
+ }
+
+private:
+
+ HASH m_hashtable;
+
+}; // class sp_cache
+
+#endif /* _SP_CACHE_H_ */
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
new file mode 100644
index 00000000000..88d81f25a76
--- /dev/null
+++ b/sql/sp_head.cc
@@ -0,0 +1,917 @@
+/* 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.
+*/
+Item *
+sp_eval_func_item(THD *thd, Item *it, enum enum_field_types type)
+{
+ DBUG_ENTER("sp_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->collation.collation);
+ String *s= it->val_str(&tmp);
+
+ DBUG_PRINT("info",("default result: %*s",s->length(),s->c_ptr_quick()));
+ it= new Item_string(thd->strmake(s->c_ptr_quick(), s->length()),
+ s->length(), it->collation.collation);
+ break;
+ }
+ }
+ }
+
+ DBUG_RETURN(it);
+}
+
+void *
+sp_head::operator new(size_t size)
+{
+ DBUG_ENTER("sp_head::operator new");
+ MEM_ROOT own_root;
+ sp_head *sp;
+
+ bzero((char *)&own_root, sizeof(own_root));
+ init_alloc_root(&own_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ sp= (sp_head *)alloc_root(&own_root, size);
+ sp->m_mem_root= own_root;
+
+ DBUG_RETURN(sp);
+}
+
+void
+sp_head::operator delete(void *ptr, size_t size)
+{
+ DBUG_ENTER("sp_head::operator delete");
+ MEM_ROOT own_root;
+ sp_head *sp= (sp_head *)ptr;
+
+ memcpy(&own_root, (const void *)&sp->m_mem_root, sizeof(MEM_ROOT));
+ free_root(&own_root, MYF(0));
+
+ DBUG_VOID_RETURN;
+}
+
+sp_head::sp_head()
+ : Sql_alloc(), m_has_return(FALSE), m_simple_case(FALSE),
+ m_multi_results(FALSE), m_free_list(NULL)
+{
+ DBUG_ENTER("sp_head::sp_head");
+
+ m_backpatch.empty();
+ m_lex.empty();
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid)
+{
+ DBUG_ENTER("sp_head::init");
+ const char *dstr = (const char*)lex->buf;
+
+ DBUG_PRINT("info", ("name: %*s", name->length, name->str));
+ m_name.length= name->length;
+ m_name.str= lex->thd->strmake(name->str, name->length);
+ m_defstr.length= lex->end_of_query - lex->buf;
+ m_defstr.str= lex->thd->strmake(dstr, m_defstr.length);
+
+ m_comment.length= 0;
+ m_comment.str= 0;
+ if (comment)
+ {
+ m_comment.length= comment->length;
+ m_comment.str= comment->str;
+ }
+
+ m_suid= suid;
+ lex->spcont= m_pcont= new sp_pcontext();
+ my_init_dynamic_array(&m_instr, sizeof(sp_instr *), 16, 8);
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::create(THD *thd)
+{
+ DBUG_ENTER("sp_head::create");
+ int ret;
+
+ DBUG_PRINT("info", ("type: %d name: %s def: %s",
+ m_type, m_name.str, m_defstr.str));
+ if (m_type == TYPE_ENUM_FUNCTION)
+ ret= sp_create_function(thd,
+ m_name.str, m_name.length,
+ m_defstr.str, m_defstr.length,
+ m_comment.str, m_comment.length,
+ m_suid);
+ else
+ ret= sp_create_procedure(thd,
+ m_name.str, m_name.length,
+ m_defstr.str, m_defstr.length,
+ m_comment.str, m_comment.length,
+ m_suid);
+
+ DBUG_RETURN(ret);
+}
+
+sp_head::~sp_head()
+{
+ destroy();
+ if (m_thd)
+ restore_thd_mem_root(m_thd);
+}
+
+void
+sp_head::destroy()
+{
+ DBUG_ENTER("sp_head::destroy");
+ DBUG_PRINT("info", ("name: %s", m_name.str));
+ sp_instr *i;
+ LEX *lex;
+
+ for (uint ip = 0 ; (i = get_instr(ip)) ; ip++)
+ delete i;
+ delete_dynamic(&m_instr);
+ m_pcont->destroy();
+ free_items(m_free_list);
+ while ((lex= (LEX *)m_lex.pop()))
+ {
+ if (lex != &m_thd->main_lex) // We got interrupted and have lex'es left
+ delete lex;
+ }
+ DBUG_VOID_RETURN;
+}
+
+int
+sp_head::execute(THD *thd)
+{
+ DBUG_ENTER("sp_head::execute");
+ char olddbname[128];
+ char *olddbptr= thd->db;
+ sp_rcontext *ctx= thd->spcont;
+ 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';
+ }
+
+ if (ctx)
+ ctx->clear_handler();
+ do
+ {
+ sp_instr *i;
+ uint hip; // Handler ip
+
+ 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);
+ // Check if an exception has occurred and a handler has been found
+ if (ret && !thd->killed && ctx)
+ {
+ uint hf;
+
+ switch (ctx->found_handler(&hip, &hf))
+ {
+ case SP_HANDLER_NONE:
+ break;
+ case SP_HANDLER_CONTINUE:
+ ctx->save_variables(hf);
+ ctx->push_hstack(ip);
+ // Fall through
+ default:
+ ip= hip;
+ ret= 0;
+ ctx->clear_handler();
+ continue;
+ }
+ }
+ } 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();
+ uint hmax = m_pcont->handlers();
+ uint cmax = m_pcont->cursors();
+ 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, hmax, cmax);
+ for (i= 0 ; i < params && i < argcount ; i++)
+ {
+ sp_pvar_t *pvar = m_pcont->find_pvar(i);
+
+ nctx->push_item(sp_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.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ }
+ thd->spcont= nctx;
+
+ ret= execute(thd);
+ if (ret == 0)
+ {
+ Item *it= nctx->get_result();
+
+ if (it)
+ *resp= it;
+ else
+ {
+ my_printf_error(ER_SP_NORETURNEND, ER(ER_SP_NORETURNEND), MYF(0),
+ m_name.str);
+ ret= -1;
+ }
+ }
+
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ 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();
+ uint hmax = m_pcont->handlers();
+ uint cmax = m_pcont->cursors();
+ 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 || hmax > 0 || cmax > 0)
+ {
+ uint i;
+ List_iterator_fast<Item> li(*args);
+ Item *it;
+
+ nctx= new sp_rcontext(csize, hmax, cmax);
+ if (! octx)
+ { // Create a temporary old context
+ octx= new sp_rcontext(csize, hmax, cmax);
+ 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(sp_eval_func_item(thd, it,pvar->type)); // IN or INOUT
+ // Note: If it's OUT or INOUT, it must be a variable.
+ // QQ: We can check for global variables here, or should we do it
+ // while parsing?
+ 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.
+ // Default all variables to null (those with default clauses will
+ // be set by an set instruction).
+ {
+ Item_null *nit= NULL; // Re-use this, and only create if needed
+ for (; i < csize ; i++)
+ {
+ if (! nit)
+ nit= new Item_null();
+ nctx->push_item(nit);
+ }
+ }
+ 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
+ {
+ // QQ Currently we just silently ignore non-user-variable arguments.
+ // We should check this during parsing, when setting up the call
+ // above
+ if (it->type() == Item::FUNC_ITEM)
+ {
+ Item_func *fi= static_cast<Item_func*>(it);
+
+ if (fi->functype() == Item_func::GUSERVAR_FUNC)
+ { // A global user variable
+ 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*>(fi);
+
+ suv= new Item_func_set_user_var(guv->get_name(), item);
+ suv->fix_fields(thd, NULL, &item);
+ suv->fix_length_and_dec();
+ suv->update();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (tmp_octx)
+ octx= NULL;
+ if (nctx)
+ nctx->pop_all_cursors(); // To avoid memory leaks after an error
+ thd->spcont= octx;
+
+ DBUG_RETURN(ret);
+}
+
+
+// Reset lex during parsing, before we parse a sub statement.
+void
+sp_head::reset_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::reset_lex");
+ LEX *sublex;
+ LEX *oldlex= thd->lex;
+
+ (void)m_lex.push_front(oldlex);
+ thd->lex= sublex= new st_lex;
+ sublex->yylineno= oldlex->yylineno;
+ /* Reset most stuff. The length arguments doesn't matter here. */
+ lex_start(thd, oldlex->buf, oldlex->end_of_query - oldlex->ptr);
+ /* We must reset ptr and end_of_query again */
+ sublex->ptr= oldlex->ptr;
+ sublex->end_of_query= oldlex->end_of_query;
+ sublex->tok_start= oldlex->tok_start;
+ /* And keep the SP stuff too */
+ sublex->sphead= oldlex->sphead;
+ sublex->spcont= oldlex->spcont;
+ mysql_init_query(thd, true); // Only init lex
+ sublex->sp_lex_in_use= FALSE;
+ DBUG_VOID_RETURN;
+}
+
+// Restore lex during parsing, after we have parsed a sub statement.
+void
+sp_head::restore_lex(THD *thd)
+{
+ DBUG_ENTER("sp_head::restore_lex");
+ LEX *sublex= thd->lex;
+ LEX *oldlex= (LEX *)m_lex.pop();
+
+ if (! oldlex)
+ return; // Nothing to restore
+
+ // Update some state in the old one first
+ oldlex->ptr= sublex->ptr;
+ oldlex->next_state= sublex->next_state;
+ // Save WHERE clause pointers to avoid damaging by optimisation
+ for (SELECT_LEX *sl= sublex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ sl->prep_where= sl->where;
+ }
+
+ // Collect some data from the sub statement lex.
+ sp_merge_funs(oldlex, sublex);
+#ifdef NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ if (sublex.sql_command == SQLCOM_CALL)
+ {
+ // It would be slightly faster to keep the list sorted, but we need
+ // an "insert before" method to do that.
+ char *proc= sublex.udf.name.str;
+
+ List_iterator_fast<char *> li(m_calls);
+ char **it;
+
+ while ((it= li++))
+ if (my_strcasecmp(system_charset_info, proc, *it) == 0)
+ break;
+ if (! it)
+ m_calls.push_back(&proc);
+
+ }
+ // Merge used tables
+ // QQ ...or just open tables in thd->open_tables?
+ // This is not entirerly clear at the moment, but for now, we collect
+ // tables here.
+ for (SELECT_LEX *sl= sublex.all_selects_list ;
+ sl ;
+ sl= sl->next_select())
+ {
+ for (TABLE_LIST *tables= sl->get_table_list() ;
+ tables ;
+ tables= tables->next)
+ {
+ List_iterator_fast<char *> li(m_tables);
+ char **tb;
+
+ while ((tb= li++))
+ if (my_strcasecmp(system_charset_info, tables->real_name, *tb) == 0)
+ break;
+ if (! tb)
+ m_tables.push_back(&tables->real_name);
+ }
+ }
+#endif
+ if (! sublex->sp_lex_in_use)
+ delete sublex;
+ thd->lex= oldlex;
+ DBUG_VOID_RETURN;
+}
+
+void
+sp_head::push_backpatch(sp_instr *i, sp_label_t *lab)
+{
+ bp_t *bp= (bp_t *)sql_alloc(sizeof(bp_t));
+
+ if (bp)
+ {
+ bp->lab= lab;
+ bp->instr= i;
+ (void)m_backpatch.push_front(bp);
+ }
+}
+
+void
+sp_head::backpatch(sp_label_t *lab)
+{
+ bp_t *bp;
+ uint dest= instructions();
+ List_iterator_fast<bp_t> li(m_backpatch);
+
+ while ((bp= li++))
+ if (bp->lab == lab)
+ {
+ sp_instr_jump *i= static_cast<sp_instr_jump *>(bp->instr);
+
+ i->set_destination(dest);
+ }
+}
+
+
+// ------------------------------------------------------------------
+
+//
+// sp_instr_stmt
+//
+sp_instr_stmt::~sp_instr_stmt()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+int
+sp_instr_stmt::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_stmt::execute");
+ DBUG_PRINT("info", ("command: %d", m_lex->sql_command));
+ int res= exec_stmt(thd, m_lex);
+ *nextp = m_ip+1;
+ DBUG_RETURN(res);
+}
+
+int
+sp_instr_stmt::exec_stmt(THD *thd, LEX *lex)
+{
+ LEX *olex; // The other lex
+ Item *freelist;
+ int res;
+
+ olex= thd->lex; // Save the other lex
+ thd->lex= lex; // Use my own lex
+ thd->lex->thd = thd; // QQ Not reentrant!
+ thd->lex->unit.thd= thd; // QQ Not reentrant
+ freelist= thd->free_list;
+ thd->free_list= NULL;
+ thd->query_id= query_id++;
+
+ // Copy WHERE clause pointers to avoid damaging by optimisation
+ // Also clear ref_pointer_arrays.
+ for (SELECT_LEX *sl= lex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ if (sl->with_wild)
+ {
+ List_iterator_fast<Item> li(sl->item_list);
+
+ // Copy item_list
+ sl->item_list_copy.empty();
+ while (Item *it= li++)
+ sl->item_list_copy.push_back(it);
+ }
+ sl->ref_pointer_array= 0;
+ if (sl->prep_where)
+ sl->where= sl->prep_where->copy_andor_structure(thd);
+ for (ORDER *order= (ORDER *)sl->order_list.first ;
+ order ;
+ order= order->next)
+ {
+ order->item_copy= order->item;
+ }
+ for (ORDER *group= (ORDER *)sl->group_list.first ;
+ group ;
+ group= group->next)
+ {
+ group->item_copy= group->item;
+ }
+ }
+
+ 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 */
+ }
+
+ for (SELECT_LEX *sl= lex->all_selects_list ;
+ sl ;
+ sl= sl->next_select_in_list())
+ {
+ if (sl->with_wild)
+ {
+ // Restore item_list
+ sl->item_list.empty();
+ while (Item *it= sl->item_list_copy.pop())
+ sl->item_list.push_back(it);
+ }
+ for (ORDER *order= (ORDER *)sl->order_list.first ;
+ order ;
+ order= order->next)
+ {
+ order->item= order->item_copy;
+ }
+ for (ORDER *group= (ORDER *)sl->group_list.first ;
+ group ;
+ group= group->next)
+ {
+ group->item= group->item_copy;
+ }
+ }
+ thd->lex= olex; // Restore the other lex
+ thd->free_list= freelist;
+
+ 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, sp_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= sp_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= sp_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_freturn
+//
+int
+sp_instr_freturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_freturn::execute");
+ thd->spcont->set_result(sp_eval_func_item(thd, m_value, m_type));
+ *nextp= UINT_MAX;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hpush_jump
+//
+int
+sp_instr_hpush_jump::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpush_jump::execute");
+ List_iterator_fast<sp_cond_type_t> li(m_cond);
+ sp_cond_type_t *p;
+
+ while ((p= li++))
+ thd->spcont->push_handler(p, m_handler, m_type, m_frame);
+
+ *nextp= m_dest;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hpop
+//
+int
+sp_instr_hpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hpop::execute");
+ thd->spcont->pop_handlers(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_hreturn
+//
+int
+sp_instr_hreturn::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_hreturn::execute");
+ thd->spcont->restore_variables(m_frame);
+ *nextp= thd->spcont->pop_hstack();
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_cpush
+//
+int
+sp_instr_cpush::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpush::execute");
+ thd->spcont->push_cursor(m_lex);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+sp_instr_cpush::~sp_instr_cpush()
+{
+ if (m_lex)
+ delete m_lex;
+}
+
+//
+// sp_instr_cpop
+//
+int
+sp_instr_cpop::execute(THD *thd, uint *nextp)
+{
+ DBUG_ENTER("sp_instr_cpop::execute");
+ thd->spcont->pop_cursors(m_count);
+ *nextp= m_ip+1;
+ DBUG_RETURN(0);
+}
+
+//
+// sp_instr_copen
+//
+int
+sp_instr_copen::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_copen::execute");
+
+ if (! c)
+ res= -1;
+ else
+ {
+ LEX *lex= c->pre_open(thd);
+
+ if (! lex)
+ res= -1;
+ else
+ res= exec_stmt(thd, lex);
+ c->post_open(thd, (res == 0 ? TRUE : FALSE));
+ }
+
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+//
+// sp_instr_cclose
+//
+int
+sp_instr_cclose::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cclose::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->close(thd);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
+
+//
+// sp_instr_cfetch
+//
+int
+sp_instr_cfetch::execute(THD *thd, uint *nextp)
+{
+ sp_cursor *c= thd->spcont->get_cursor(m_cursor);
+ int res;
+ DBUG_ENTER("sp_instr_cfetch::execute");
+
+ if (! c)
+ res= -1;
+ else
+ res= c->fetch(thd, &m_varlist);
+ *nextp= m_ip+1;
+ DBUG_RETURN(res);
+}
diff --git a/sql/sp_head.h b/sql/sp_head.h
new file mode 100644
index 00000000000..b582d37a185
--- /dev/null
+++ b/sql/sp_head.h
@@ -0,0 +1,631 @@
+/* -*- 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;
+struct sp_cond_type;
+struct sp_pvar;
+
+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_has_return; // For FUNCTIONs only
+ my_bool m_simple_case; // TRUE if parsing simple case, FALSE otherwise
+ my_bool m_multi_results; // TRUE if a procedure with SELECT(s)
+ uint m_old_cmq; // Old CLIENT_MULTI_QUERIES value
+#if NOT_USED_NOW
+ // QQ We're not using this at the moment.
+ List<char *> m_calls; // Called procedures.
+ List<char *> m_tables; // Used tables.
+#endif
+
+ static void *
+ operator new(size_t size);
+
+ static void
+ operator delete(void *ptr, size_t size);
+
+ sp_head();
+
+ // Initialize after we have reset mem_root
+ void
+ init(LEX_STRING *name, LEX *lex, LEX_STRING *comment, char suid);
+
+ int
+ create(THD *thd);
+
+ virtual ~sp_head();
+
+ // Free memory
+ void
+ destroy();
+
+ int
+ execute_function(THD *thd, Item **args, uint argcount, Item **resp);
+
+ int
+ execute_procedure(THD *thd, List<Item> *args);
+
+ inline void
+ add_instr(sp_instr *i)
+ {
+ insert_dynamic(&m_instr, (gptr)&i);
+ }
+
+ inline uint
+ instructions()
+ {
+ return m_instr.elements;
+ }
+
+ inline sp_instr *
+ last_instruction()
+ {
+ sp_instr *i;
+
+ get_dynamic(&m_instr, (gptr)&i, m_instr.elements-1);
+ return i;
+ }
+
+ // Resets lex in 'thd' and keeps a copy of the old one.
+ void
+ reset_lex(THD *thd);
+
+ // Restores lex in 'thd' from our copy, but keeps some status from the
+ // one in 'thd', like ptr, tables, fields, etc.
+ void
+ restore_lex(THD *thd);
+
+ // Put the instruction on the backpatch list, associated with the label.
+ void
+ push_backpatch(sp_instr *, struct sp_label *);
+
+ // Update all instruction with this label in the backpatch list to
+ // the current position.
+ void
+ backpatch(struct sp_label *);
+
+ char *name(uint *lenp = 0) const
+ {
+ if (lenp)
+ *lenp= m_name.length;
+ return m_name.str;
+ }
+
+ inline Item_result result()
+ {
+ return sp_map_result_type(m_returns);
+ }
+
+ void sp_set_info(char *creator, uint creatorlen,
+ longlong created, longlong modified,
+ bool suid, char *comment, uint commentlen)
+ {
+ m_creator= creator;
+ m_creatorlen= creatorlen;
+ m_created= created;
+ m_modified= modified;
+ m_comment.length= commentlen;
+ m_comment.str= comment;
+ m_suid= suid;
+ }
+
+ inline void reset_thd_mem_root(THD *thd)
+ {
+ m_thd_root= thd->mem_root;
+ thd->mem_root= m_mem_root;
+ m_free_list= thd->free_list; // Keep the old list
+ thd->free_list= NULL; // Start a new one
+ m_thd= thd;
+ }
+
+ inline void restore_thd_mem_root(THD *thd)
+ {
+ Item *flist= m_free_list; // The old list
+ m_free_list= thd->free_list; // Get the new one
+ thd->free_list= flist; // Restore the old one
+ m_mem_root= thd->mem_root;
+ thd->mem_root= m_thd_root;
+ m_thd= NULL;
+ }
+
+private:
+
+ MEM_ROOT m_mem_root; // My own mem_root
+ MEM_ROOT m_thd_root; // Temp. store for thd's mem_root
+ Item *m_free_list; // Where the items go
+ THD *m_thd; // Set if we have reset mem_root
+
+ LEX_STRING m_name;
+ LEX_STRING m_defstr;
+ LEX_STRING m_comment;
+ char *m_creator;
+ uint m_creatorlen;
+ longlong m_created;
+ longlong m_modified;
+ bool m_suid;
+
+ sp_pcontext *m_pcont; // Parse context
+ List<LEX> m_lex; // Temp. store for the other lex
+ DYNAMIC_ARRAY m_instr; // The "instructions"
+ typedef struct
+ {
+ struct sp_label *lab;
+ sp_instr *instr;
+ } bp_t;
+ List<bp_t> m_backpatch; // Instructions needing backpatching
+
+ inline sp_instr *
+ get_instr(uint i)
+ {
+ sp_instr *ip;
+
+ if (i < m_instr.elements)
+ get_dynamic(&m_instr, (gptr)&ip, i);
+ else
+ ip= NULL;
+ return ip;
+ }
+
+ int
+ execute(THD *thd);
+
+}; // class sp_head : public Sql_alloc
+
+
+//
+// "Instructions"...
+//
+
+class sp_instr : public Sql_alloc
+{
+ sp_instr(const sp_instr &); /* Prevent use of these */
+ void operator=(sp_instr &);
+
+public:
+
+ // Should give each a name or type code for debugging purposes?
+ sp_instr(uint ip)
+ : Sql_alloc(), m_ip(ip)
+ {}
+
+ virtual ~sp_instr()
+ {}
+
+ // Execute this instrution. '*nextp' will be set to the index of the next
+ // instruction to execute. (For most instruction this will be the
+ // instruction following this one.)
+ // Returns 0 on success, non-zero if some error occured.
+ virtual int
+ execute(THD *thd, uint *nextp)
+ { // Default is a no-op.
+ *nextp = m_ip+1; // Next instruction
+ return 0;
+ }
+
+protected:
+
+ uint m_ip; // My index
+
+}; // class sp_instr : public Sql_alloc
+
+
+//
+// Call out to some prepared SQL statement.
+//
+class sp_instr_stmt : public sp_instr
+{
+ sp_instr_stmt(const sp_instr_stmt &); /* Prevent use of these */
+ void operator=(sp_instr_stmt &);
+
+public:
+
+ sp_instr_stmt(uint ip)
+ : sp_instr(ip), m_lex(NULL)
+ {}
+
+ virtual ~sp_instr_stmt();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ inline void
+ set_lex(LEX *lex)
+ {
+ m_lex= lex;
+ }
+
+ inline LEX *
+ get_lex()
+ {
+ return m_lex;
+ }
+
+protected:
+
+ int exec_stmt(THD *thd, LEX *lex); // Execute a statement
+
+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_freturn : public sp_instr
+{
+ sp_instr_freturn(const sp_instr_freturn &); /* Prevent use of these */
+ void operator=(sp_instr_freturn &);
+
+public:
+
+ sp_instr_freturn(uint ip, Item *val, enum enum_field_types type)
+ : sp_instr(ip), m_value(val), m_type(type)
+ {}
+
+ virtual ~sp_instr_freturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+protected:
+
+ Item *m_value;
+ enum enum_field_types m_type;
+
+}; // class sp_instr_freturn : public sp_instr
+
+
+class sp_instr_hpush_jump : public sp_instr_jump
+{
+ sp_instr_hpush_jump(const sp_instr_hpush_jump &); /* Prevent use of these */
+ void operator=(sp_instr_hpush_jump &);
+
+public:
+
+ sp_instr_hpush_jump(uint ip, int htype, uint fp)
+ : sp_instr_jump(ip), m_type(htype), m_frame(fp)
+ {
+ m_handler= ip+1;
+ m_cond.empty();
+ }
+
+ virtual ~sp_instr_hpush_jump()
+ {
+ m_cond.empty();
+ }
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ inline void add_condition(struct sp_cond_type *cond)
+ {
+ m_cond.push_front(cond);
+ }
+
+private:
+
+ int m_type; // Handler type
+ uint m_frame;
+ uint m_handler; // Location of handler
+ List<struct sp_cond_type> m_cond;
+
+}; // class sp_instr_hpush_jump : public sp_instr_jump
+
+
+class sp_instr_hpop : public sp_instr
+{
+ sp_instr_hpop(const sp_instr_hpop &); /* Prevent use of these */
+ void operator=(sp_instr_hpop &);
+
+public:
+
+ sp_instr_hpop(uint ip, uint count)
+ : sp_instr(ip), m_count(count)
+ {}
+
+ virtual ~sp_instr_hpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_hpop : public sp_instr
+
+
+class sp_instr_hreturn : public sp_instr
+{
+ sp_instr_hreturn(const sp_instr_hreturn &); /* Prevent use of these */
+ void operator=(sp_instr_hreturn &);
+
+public:
+
+ sp_instr_hreturn(uint ip, uint fp)
+ : sp_instr(ip), m_frame(fp)
+ {}
+
+ virtual ~sp_instr_hreturn()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_frame;
+
+}; // class sp_instr_hreturn : public sp_instr
+
+
+class sp_instr_cpush : public sp_instr
+{
+ sp_instr_cpush(const sp_instr_cpush &); /* Prevent use of these */
+ void operator=(sp_instr_cpush &);
+
+public:
+
+ sp_instr_cpush(uint ip, LEX *lex)
+ : sp_instr(ip), m_lex(lex)
+ {}
+
+ virtual ~sp_instr_cpush();
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ LEX *m_lex;
+
+}; // class sp_instr_cpush : public sp_instr
+
+
+class sp_instr_cpop : public sp_instr
+{
+ sp_instr_cpop(const sp_instr_cpop &); /* Prevent use of these */
+ void operator=(sp_instr_cpop &);
+
+public:
+
+ sp_instr_cpop(uint ip, uint count)
+ : sp_instr(ip), m_count(count)
+ {}
+
+ virtual ~sp_instr_cpop()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_count;
+
+}; // class sp_instr_cpop : public sp_instr
+
+
+class sp_instr_copen : public sp_instr_stmt
+{
+ sp_instr_copen(const sp_instr_copen &); /* Prevent use of these */
+ void operator=(sp_instr_copen &);
+
+public:
+
+ sp_instr_copen(uint ip, uint c)
+ : sp_instr_stmt(ip), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_copen()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_cursor; // Stack index
+
+}; // class sp_instr_copen : public sp_instr_stmt
+
+
+class sp_instr_cclose : public sp_instr
+{
+ sp_instr_cclose(const sp_instr_cclose &); /* Prevent use of these */
+ void operator=(sp_instr_cclose &);
+
+public:
+
+ sp_instr_cclose(uint ip, uint c)
+ : sp_instr(ip), m_cursor(c)
+ {}
+
+ virtual ~sp_instr_cclose()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+private:
+
+ uint m_cursor;
+
+}; // class sp_instr_cclose : public sp_instr
+
+
+class sp_instr_cfetch : public sp_instr
+{
+ sp_instr_cfetch(const sp_instr_cfetch &); /* Prevent use of these */
+ void operator=(sp_instr_cfetch &);
+
+public:
+
+ sp_instr_cfetch(uint ip, uint c)
+ : sp_instr(ip), m_cursor(c)
+ {
+ m_varlist.empty();
+ }
+
+ virtual ~sp_instr_cfetch()
+ {}
+
+ virtual int execute(THD *thd, uint *nextp);
+
+ void add_to_varlist(struct sp_pvar *var)
+ {
+ m_varlist.push_back(var);
+ }
+
+private:
+
+ uint m_cursor;
+ List<struct sp_pvar> m_varlist;
+
+}; // class sp_instr_cfetch : 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..3730230d47d
--- /dev/null
+++ b/sql/sp_pcontext.cc
@@ -0,0 +1,193 @@
+/* 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_handlers(0), m_cursmax(0)
+{
+ VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8));
+ VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8));
+ m_label.empty();
+}
+
+void
+sp_pcontext::destroy()
+{
+ delete_dynamic(&m_pvar);
+ delete_dynamic(&m_cond);
+ delete_dynamic(&m_cursor);
+ 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;
+
+ get_dynamic(&m_pvar, (gptr)&p, 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_pvar(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;
+}
+
+void
+sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val)
+{
+ sp_cond_t *p= (sp_cond_t *)sql_alloc(sizeof(sp_cond_t));
+
+ if (p)
+ {
+ p->name.str= name->str;
+ p->name.length= name->length;
+ p->val= val;
+ insert_dynamic(&m_cond, (gptr)&p);
+ }
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+sp_cond_type_t *
+sp_pcontext::find_cond(LEX_STRING *name)
+{
+ uint i = m_cond.elements;
+
+ while (i-- > 0)
+ {
+ sp_cond_t *p;
+
+ get_dynamic(&m_cond, (gptr)&p, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)p->name.str, p->name.length) == 0)
+ {
+ return p->val;
+ }
+ }
+ return NULL;
+}
+
+void
+sp_pcontext::push_cursor(LEX_STRING *name)
+{
+ LEX_STRING n;
+
+ n.str= name->str;
+ n.length= name->length;
+ insert_dynamic(&m_cursor, (gptr)&n);
+ if (m_cursor.elements > m_cursmax)
+ m_cursmax= m_cursor.elements;
+}
+
+/*
+ * See comment for find_pvar() above
+ */
+my_bool
+sp_pcontext::find_cursor(LEX_STRING *name, uint *poff)
+{
+ uint i = m_cursor.elements;
+
+ while (i-- > 0)
+ {
+ LEX_STRING n;
+
+ get_dynamic(&m_cursor, (gptr)&n, i);
+ if (my_strnncoll(system_charset_info,
+ (const uchar *)name->str, name->length,
+ (const uchar *)n.str, n.length) == 0)
+ {
+ *poff= i;
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
new file mode 100644
index 00000000000..23be38edcbf
--- /dev/null
+++ b/sql/sp_pcontext.h
@@ -0,0 +1,242 @@
+/* -*- 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 sp_pvar
+{
+ 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;
+
+typedef struct sp_cond_type
+{
+ enum { number, state, warning, notfound, exception } type;
+ char sqlstate[6];
+ uint mysqlerr;
+} sp_cond_type_t;
+
+typedef struct sp_cond
+{
+ LEX_STRING name;
+ sp_cond_type_t *val;
+} sp_cond_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();
+
+ //
+ // Parameters and variables
+ //
+
+ 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_pvar(LEX_STRING *name, enum enum_field_types type, sp_param_mode_t mode);
+
+ // Pop the last 'num' slots of the frame
+ inline void
+ pop_pvar(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;
+ }
+
+ //
+ // Labels
+ //
+
+ 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();
+ }
+
+ //
+ // Conditions
+ //
+
+ void
+ push_cond(LEX_STRING *name, sp_cond_type_t *val);
+
+ inline void
+ pop_cond(uint num)
+ {
+ while (num--)
+ pop_dynamic(&m_cond);
+ }
+
+ sp_cond_type_t *
+ find_cond(LEX_STRING *name);
+
+ //
+ // Handlers
+ //
+
+ inline void
+ add_handler()
+ {
+ m_handlers+= 1;
+ }
+
+ inline uint
+ handlers()
+ {
+ return m_handlers;
+ }
+
+ //
+ // Cursors
+ //
+
+ void
+ push_cursor(LEX_STRING *name);
+
+ my_bool
+ find_cursor(LEX_STRING *name, uint *poff);
+
+ inline void
+ pop_cursor(uint num)
+ {
+ while (num--)
+ pop_dynamic(&m_cursor);
+ }
+
+ inline uint
+ cursors()
+ {
+ return m_cursmax;
+ }
+
+private:
+
+ uint m_params; // The number of parameters
+ uint m_framesize; // The maximum framesize
+ uint m_handlers; // The total number of handlers
+ uint m_cursmax; // The maximum number of cursors
+
+ DYNAMIC_ARRAY m_pvar; // Parameters/variables
+ DYNAMIC_ARRAY m_cond; // Conditions
+ DYNAMIC_ARRAY m_cursor; // Cursors
+
+ List<sp_label_t> m_label; // The label list
+
+}; // class sp_pcontext : public Sql_alloc
+
+
+#endif /* _SP_PCONTEXT_H_ */
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
new file mode 100644
index 00000000000..07fd08b0074
--- /dev/null
+++ b/sql/sp_rcontext.cc
@@ -0,0 +1,245 @@
+/* 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 "mysql.h"
+#include "sp_head.h"
+#include "sp_rcontext.h"
+#include "sp_pcontext.h"
+
+sp_rcontext::sp_rcontext(uint fsize, uint hmax, uint cmax)
+ : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0),
+ m_hfound(-1), m_ccount(0)
+{
+ m_frame= (Item **)sql_alloc(fsize * sizeof(Item*));
+ m_outs= (int *)sql_alloc(fsize * sizeof(int));
+ m_handler= (sp_handler_t *)sql_alloc(hmax * sizeof(sp_handler_t));
+ m_hstack= (uint *)sql_alloc(hmax * sizeof(uint));
+ m_cstack= (sp_cursor **)sql_alloc(cmax * sizeof(sp_cursor *));
+ m_saved.empty();
+}
+
+void
+sp_rcontext::set_item_eval(uint idx, Item *i, enum_field_types type)
+{
+ extern Item *sp_eval_func_item(THD *thd, Item *it, enum_field_types type);
+
+ set_item(idx, sp_eval_func_item(current_thd, i, type));
+}
+
+int
+sp_rcontext::find_handler(uint sql_errno)
+{
+ if (m_hfound >= 0)
+ return 1; // Already got one
+
+ const char *sqlstate= mysql_errno_to_sqlstate(sql_errno);
+ int i= m_hcount, found= 0;
+
+ while (!found && i--)
+ {
+ sp_cond_type_t *cond= m_handler[i].cond;
+
+ switch (cond->type)
+ {
+ case sp_cond_type_t::number:
+ if (sql_errno == cond->mysqlerr)
+ found= 1;
+ break;
+ case sp_cond_type_t::state:
+ if (strcmp(sqlstate, cond->sqlstate) == 0)
+ found= 1;
+ break;
+ case sp_cond_type_t::warning:
+ if (sqlstate[0] == '0' && sqlstate[0] == '1')
+ found= 1;
+ break;
+ case sp_cond_type_t::notfound:
+ if (sqlstate[0] == '0' && sqlstate[0] == '2')
+ found= 1;
+ break;
+ case sp_cond_type_t::exception:
+ if (sqlstate[0] != '0' || sqlstate[0] > '2')
+ found= 1;
+ break;
+ }
+ }
+ if (found)
+ m_hfound= i;
+ return found;
+}
+
+void
+sp_rcontext::save_variables(uint fp)
+{
+ while (fp < m_count)
+ m_saved.push_front(m_frame[fp++]);
+}
+
+void
+sp_rcontext::restore_variables(uint fp)
+{
+ uint i= m_count;
+
+ while (i-- > fp)
+ m_frame[i]= m_saved.pop();
+}
+
+void
+sp_rcontext::push_cursor(LEX *lex)
+{
+ m_cstack[m_ccount++]= new sp_cursor(lex);
+}
+
+void
+sp_rcontext::pop_cursors(uint count)
+{
+ while (count--)
+ {
+ delete m_cstack[--m_ccount];
+ }
+}
+
+
+/*
+ *
+ * sp_cursor
+ *
+ */
+
+// We have split this in two to make it easy for sp_instr_copen
+// to reuse the sp_instr::exec_stmt() code.
+LEX *
+sp_cursor::pre_open(THD *thd)
+{
+ int res;
+
+ if (m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_ALREADY_OPEN);
+ return NULL;
+ }
+
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ init_alloc_root(&m_mem_root, MEM_ROOT_BLOCK_SIZE, MEM_ROOT_PREALLOC);
+ if ((m_prot= new Protocol_cursor(thd, &m_mem_root)) == NULL)
+ return NULL;
+
+ m_oprot= thd->protocol; // Save the original protocol
+ thd->protocol= m_prot;
+
+ m_ovio= thd->net.vio; // Prevent send_eof()
+ thd->net.vio= 0;
+ return m_lex;
+}
+
+void
+sp_cursor::post_open(THD *thd, my_bool isopen)
+{
+ thd->net.vio= m_ovio; // Restore the originals
+ thd->protocol= m_oprot;
+ m_isopen= isopen;
+ m_current_row= m_prot->data;
+}
+
+int
+sp_cursor::close(THD *thd)
+{
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+ destroy();
+ return 0;
+}
+
+void
+sp_cursor::destroy()
+{
+ delete m_prot;
+ m_prot= NULL;
+ free_root(&m_mem_root, MYF(0));
+ bzero((char *)&m_mem_root, sizeof(m_mem_root));
+ m_isopen= FALSE;
+}
+
+int
+sp_cursor::fetch(THD *thd, List<struct sp_pvar> *vars)
+{
+ List_iterator_fast<struct sp_pvar> li(*vars);
+ sp_pvar_t *pv;
+ MYSQL_ROW row;
+ uint fldcount;
+ MYSQL_FIELD *fields= m_prot->fields;
+
+ if (! m_isopen)
+ {
+ send_error(thd, ER_SP_CURSOR_NOT_OPEN);
+ return -1;
+ }
+
+ if (m_current_row == NULL)
+ {
+ send_error(thd, ER_SP_FETCH_NO_DATA);
+ return -1;
+ }
+
+ row= m_current_row->data;
+ for (fldcount= 0 ; (pv= li++) ; fldcount++)
+ {
+ Item *it;
+ const char *s;
+
+ if (fldcount >= m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ s= row[fldcount];
+ switch (sp_map_result_type(pv->type))
+ {
+ case INT_RESULT:
+ it= new Item_int(s);
+ break;
+ case REAL_RESULT:
+ it= new Item_real(s, strlen(s));
+ break;
+ default:
+ {
+ uint len= strlen(s);
+ it= new Item_string(thd->strmake(s, len), len, thd->db_charset);
+ break;
+ }
+ }
+ thd->spcont->set_item(pv->offset, it);
+ }
+ if (fldcount < m_prot->get_field_count())
+ {
+ send_error(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS);
+ return -1;
+ }
+ m_current_row= m_current_row->next;
+ return 0;
+}
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
new file mode 100644
index 00000000000..e69ac9bf4b4
--- /dev/null
+++ b/sql/sp_rcontext.h
@@ -0,0 +1,252 @@
+/* -*- 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_
+
+#ifdef __GNUC__
+#pragma interface /* gcc class implementation */
+#endif
+
+struct sp_cond_type;
+struct sp_cursor;
+struct sp_pvar;
+
+#define SP_HANDLER_NONE 0
+#define SP_HANDLER_EXIT 1
+#define SP_HANDLER_CONTINUE 2
+#define SP_HANDLER_UNDO 3
+
+typedef struct
+{
+ struct sp_cond_type *cond;
+ uint handler; // Location of handler
+ int type;
+ uint foffset; // Frame offset for the handlers declare level
+} sp_handler_t;
+
+class sp_rcontext : public Sql_alloc
+{
+ sp_rcontext(const sp_rcontext &); /* Prevent use of these */
+ void operator=(sp_rcontext &);
+
+ public:
+
+ sp_rcontext(uint fsize, uint hmax, uint cmax);
+
+ ~sp_rcontext()
+ {
+ // Not needed?
+ //sql_element_free(m_frame);
+ //m_saved.empty();
+ }
+
+ inline void
+ push_item(Item *i)
+ {
+ if (m_count < m_fsize)
+ m_frame[m_count++] = i;
+ }
+
+ inline void
+ set_item(uint idx, Item *i)
+ {
+ if (idx < m_count)
+ m_frame[idx] = i;
+ }
+
+ void
+ set_item_eval(uint idx, Item *i, enum_field_types type);
+
+ 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;
+ }
+
+ inline void
+ push_handler(struct sp_cond_type *cond, uint h, int type, uint f)
+ {
+ m_handler[m_hcount].cond= cond;
+ m_handler[m_hcount].handler= h;
+ m_handler[m_hcount].type= type;
+ m_handler[m_hcount].foffset= f;
+ m_hcount+= 1;
+ }
+
+ inline void
+ pop_handlers(uint count)
+ {
+ m_hcount-= count;
+ }
+
+ // Returns 1 if a handler was found, 0 otherwise.
+ int
+ find_handler(uint sql_errno);
+
+ // Returns handler type and sets *ip to location if one was found
+ inline int
+ found_handler(uint *ip, uint *fp)
+ {
+ if (m_hfound < 0)
+ return SP_HANDLER_NONE;
+ *ip= m_handler[m_hfound].handler;
+ *fp= m_handler[m_hfound].foffset;
+ return m_handler[m_hfound].type;
+ }
+
+ // Clears the handler find state
+ inline void
+ clear_handler()
+ {
+ m_hfound= -1;
+ }
+
+ inline void
+ push_hstack(uint h)
+ {
+ m_hstack[m_hsp++]= h;
+ }
+
+ inline uint
+ pop_hstack()
+ {
+ return m_hstack[--m_hsp];
+ }
+
+ // Save variables starting at fp and up
+ void
+ save_variables(uint fp);
+
+ // Restore variables down to fp
+ void
+ restore_variables(uint fp);
+
+ void
+ push_cursor(LEX *lex);
+
+ void
+ pop_cursors(uint count);
+
+ void
+ pop_all_cursors()
+ {
+ pop_cursors(m_ccount);
+ }
+
+ inline sp_cursor *
+ get_cursor(uint i)
+ {
+ return m_cstack[i];
+ }
+
+private:
+
+ uint m_count;
+ uint m_fsize;
+ Item **m_frame;
+ int *m_outs;
+
+ Item *m_result; // For FUNCTIONs
+
+ sp_handler_t *m_handler;
+ uint m_hcount;
+ uint *m_hstack;
+ uint m_hsp;
+ int m_hfound; // Set by find_handler; -1 if not found
+ List<Item> m_saved; // Saved variables
+
+ sp_cursor **m_cstack;
+ uint m_ccount;
+
+}; // class sp_rcontext : public Sql_alloc
+
+
+class sp_cursor : public Sql_alloc
+{
+public:
+
+ sp_cursor(LEX *lex)
+ : m_lex(lex), m_isopen(0), m_current_row(NULL)
+ {
+ /* Empty */
+ }
+
+ virtual ~sp_cursor()
+ {
+ destroy();
+ }
+
+ // We have split this in two to make it easy for sp_instr_copen
+ // to reuse the sp_instr::exec_stmt() code.
+ LEX *
+ pre_open(THD *thd);
+ void
+ post_open(THD *thd, my_bool isopen);
+
+ int
+ close(THD *thd);
+
+ inline my_bool
+ is_open()
+ {
+ return m_isopen;
+ }
+
+ int
+ fetch(THD *, List<struct sp_pvar> *vars);
+
+private:
+
+ MEM_ROOT m_mem_root; // My own mem_root
+ LEX *m_lex;
+ Protocol_cursor *m_prot;
+ my_bool m_isopen;
+ Vio *m_ovio; // Original vio
+ Protocol *m_oprot; // Original protcol
+ MYSQL_ROWS *m_current_row;
+
+ void
+ destroy();
+
+}; // class sp_cursor : public Sql_alloc
+
+#endif /* _SP_RCONTEXT_H_ */
diff --git a/sql/sql_acl.cc b/sql/sql_acl.cc
index a27dd3999fe..482008478cc 100644
--- a/sql/sql_acl.cc
+++ b/sql/sql_acl.cc
@@ -1162,7 +1162,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);
@@ -1427,7 +1426,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
if (table->fields >= 31) /* From 4.0.0 we have more fields */
{
/* We write down SSL related ACL stuff */
- switch (thd->lex.ssl_type) {
+ switch (thd->lex->ssl_type) {
case SSL_TYPE_ANY:
table->field[24]->store("ANY",3, &my_charset_latin1);
table->field[25]->store("", 0, &my_charset_latin1);
@@ -1445,15 +1444,15 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
table->field[25]->store("", 0, &my_charset_latin1);
table->field[26]->store("", 0, &my_charset_latin1);
table->field[27]->store("", 0, &my_charset_latin1);
- if (thd->lex.ssl_cipher)
- table->field[25]->store(thd->lex.ssl_cipher,
- strlen(thd->lex.ssl_cipher), &my_charset_latin1);
- if (thd->lex.x509_issuer)
- table->field[26]->store(thd->lex.x509_issuer,
- strlen(thd->lex.x509_issuer), &my_charset_latin1);
- if (thd->lex.x509_subject)
- table->field[27]->store(thd->lex.x509_subject,
- strlen(thd->lex.x509_subject), &my_charset_latin1);
+ if (thd->lex->ssl_cipher)
+ table->field[25]->store(thd->lex->ssl_cipher,
+ strlen(thd->lex->ssl_cipher), &my_charset_latin1);
+ if (thd->lex->x509_issuer)
+ table->field[26]->store(thd->lex->x509_issuer,
+ strlen(thd->lex->x509_issuer), &my_charset_latin1);
+ if (thd->lex->x509_subject)
+ table->field[27]->store(thd->lex->x509_subject,
+ strlen(thd->lex->x509_subject), &my_charset_latin1);
break;
case SSL_TYPE_NOT_SPECIFIED:
break;
@@ -1465,7 +1464,7 @@ static int replace_user_table(THD *thd, TABLE *table, const LEX_USER &combo,
break;
}
- USER_RESOURCES mqh = thd->lex.mqh;
+ USER_RESOURCES mqh = thd->lex->mqh;
if (mqh.bits & 1)
table->field[28]->store((longlong) mqh.questions);
if (mqh.bits & 2)
@@ -1506,19 +1505,19 @@ end:
acl_cache->clear(1); // Clear privilege cache
if (old_row_exists)
acl_update_user(combo.user.str, combo.host.str, password, password_len,
- thd->lex.ssl_type,
- thd->lex.ssl_cipher,
- thd->lex.x509_issuer,
- thd->lex.x509_subject,
- &thd->lex.mqh,
+ thd->lex->ssl_type,
+ thd->lex->ssl_cipher,
+ thd->lex->x509_issuer,
+ thd->lex->x509_subject,
+ &thd->lex->mqh,
rights);
else
acl_insert_user(combo.user.str, combo.host.str, password, password_len,
- thd->lex.ssl_type,
- thd->lex.ssl_cipher,
- thd->lex.x509_issuer,
- thd->lex.x509_subject,
- &thd->lex.mqh,
+ thd->lex->ssl_type,
+ thd->lex->ssl_cipher,
+ thd->lex->x509_issuer,
+ thd->lex->x509_subject,
+ &thd->lex->mqh,
rights);
}
table->file->index_end();
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index a926c6e66fe..eaa1271338b 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -2162,7 +2162,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
DBUG_ENTER("setup_conds");
thd->set_query_id=1;
- thd->lex.current_select->cond_count= 0;
+ thd->lex->current_select->cond_count= 0;
if (*conds)
{
thd->where="where clause";
@@ -2181,7 +2181,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
if (table->on_expr->fix_fields(thd, tables, &table->on_expr) ||
table->on_expr->check_cols(1))
DBUG_RETURN(1);
- thd->lex.current_select->cond_count++;
+ thd->lex->current_select->cond_count++;
/*
If it's a normal join or a LEFT JOIN which can be optimized away
@@ -2233,7 +2233,7 @@ int setup_conds(THD *thd,TABLE_LIST *tables,COND **conds)
}
}
cond_and->used_tables_cache= t1->map | t2->map;
- thd->lex.current_select->cond_count+=cond_and->list.elements;
+ thd->lex->current_select->cond_count+=cond_and->list.elements;
if (!table->outer_join) // Not left join
{
if (!(*conds=and_conds(*conds, cond_and)))
@@ -2441,7 +2441,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 0bae41da002..361bdf144ac 100644
--- a/sql/sql_cache.cc
+++ b/sql/sql_cache.cc
@@ -761,7 +761,7 @@ void Query_cache::store_query(THD *thd, TABLE_LIST *tables_used)
uint8 tables_type= 0;
if ((local_tables= is_cacheable(thd, thd->query_length,
- thd->query, &thd->lex, tables_used,
+ thd->query, thd->lex, tables_used,
&tables_type)))
{
NET *net= &thd->net;
@@ -910,7 +910,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/* Check that we haven't forgot to reset the query cache variables */
DBUG_ASSERT(thd->net.query_cache_query == 0);
- if (!thd->lex.safe_to_cache_query)
+ if (!thd->lex->safe_to_cache_query)
{
DBUG_PRINT("qcache", ("SELECT is non-cacheable"));
goto err;
@@ -918,11 +918,16 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
/*
Test if the query is a SELECT
- (pre-space is removed in dispatch_command)
+ (pre-space is removed in dispatch_command).
+
+ First '/' looks like comment before command it is not
+ frequently appeared in real lihe, consequently we can
+ check all such queries, too.
*/
- if (my_toupper(system_charset_info, sql[0]) != 'S' ||
- my_toupper(system_charset_info, sql[1]) != 'E' ||
- my_toupper(system_charset_info,sql[2]) !='L')
+ if ((my_toupper(system_charset_info, sql[0]) != 'S' ||
+ my_toupper(system_charset_info, sql[1]) != 'E' ||
+ my_toupper(system_charset_info,sql[2]) !='L') &&
+ sql[0] != '/')
{
DBUG_PRINT("qcache", ("The statement is not a SELECT; Not cached"));
goto err;
@@ -1014,7 +1019,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
table_list.db, table_list.alias));
refused++; // This is actually a hit
STRUCT_UNLOCK(&structure_guard_mutex);
- thd->lex.safe_to_cache_query=0; // Don't try to cache this
+ thd->lex->safe_to_cache_query=0; // Don't try to cache this
BLOCK_UNLOCK_RD(query_block);
DBUG_RETURN(-1); // Privilege error
}
@@ -1023,7 +1028,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("Need to check column privileges for %s.%s",
table_list.db, table_list.alias));
BLOCK_UNLOCK_RD(query_block);
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
goto err_unlock; // Parse query
}
#endif /*!NO_EMBEDDED_ACCESS_CHECKS*/
@@ -1034,7 +1039,7 @@ Query_cache::send_result_to_client(THD *thd, char *sql, uint query_length)
DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
table_list.db, table_list.alias));
BLOCK_UNLOCK_RD(query_block);
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
goto err_unlock; // Parse query
}
else
@@ -2589,7 +2594,7 @@ my_bool Query_cache::ask_handler_allowance(THD *thd,
{
DBUG_PRINT("qcache", ("Handler does not allow caching for %s.%s",
tables_used->db, tables_used->alias));
- thd->lex.safe_to_cache_query= 0; // Don't try to cache this
+ thd->lex->safe_to_cache_query= 0; // Don't try to cache this
DBUG_RETURN(1);
}
}
@@ -3007,7 +3012,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 588031643d4..f3e57a0abbf 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -36,6 +36,9 @@
#endif
#include <mysys_err.h>
+#include <sp_rcontext.h>
+#include <sp_cache.h>
+
/*
The following is used to initialise Table_ident with a internal
table name
@@ -86,13 +89,15 @@ 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)
{
+ lex= &main_lex;
host=user=priv_user=db=query=ip=0;
host_or_ip= "connecting host";
- locked=killed=some_tables_deleted=no_errors=password=
+ locked=some_tables_deleted=no_errors=password=
query_start_used=prepare_command=0;
count_cuted_fields= CHECK_FIELD_IGNORE;
+ killed= NOT_KILLED;
db_length=query_length=col_access=0;
query_error= tmp_table_used= 0;
next_insert_id=last_insert_id=0;
@@ -150,9 +155,12 @@ THD::THD():user_time(0), is_fatal_error(0),
bzero((char*) &warn_root,sizeof(warn_root));
init_alloc_root(&warn_root, WARN_ALLOC_BLOCK_SIZE, WARN_ALLOC_PREALLOC_SIZE);
user_connect=(USER_CONN *)0;
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
- (hash_free_key) free_user_var,0);
+ (hash_free_key) free_user_var, 0);
+
+ sp_proc_cache= NULL;
+ sp_func_cache= NULL;
/* For user vars replication*/
if (opt_bin_log)
@@ -182,7 +190,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
@@ -255,9 +263,11 @@ void THD::change_user(void)
cleanup();
cleanup_done= 0;
init();
- hash_init(&user_vars, &my_charset_bin, USER_VARS_HASH_SIZE, 0, 0,
+ hash_init(&user_vars, system_charset_info, USER_VARS_HASH_SIZE, 0, 0,
(hash_get_key) get_var_key,
(hash_free_key) free_user_var, 0);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
}
@@ -284,6 +294,8 @@ void THD::cleanup(void)
variables.datetime_formats[DATETIME_FORMAT_TYPE].clean();
delete_dynamic(&user_var_events);
hash_free(&user_vars);
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
if (global_read_lock)
unlock_global_read_lock(this);
if (ull)
@@ -293,6 +305,7 @@ void THD::cleanup(void)
pthread_mutex_unlock(&LOCK_user_locks);
ull= 0;
}
+
cleanup_done=1;
DBUG_VOID_RETURN;
}
@@ -324,6 +337,9 @@ THD::~THD()
}
#endif
+ sp_cache_clear(&sp_proc_cache);
+ sp_cache_clear(&sp_func_cache);
+
DBUG_PRINT("info", ("freeing host"));
if (host != my_localhost) // If not pointer to constant
safeFree(host);
@@ -344,14 +360,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
@@ -521,7 +537,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;
}
@@ -1160,30 +1176,52 @@ 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;
- LEX_STRING *ls;
+
+ local_vars.empty(); // Clear list if SP
+ unit= u;
+ row_count= 0;
+
if (var_list.elements != list.elements)
{
my_error(ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT, MYF(0));
return 1;
}
- 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);
+ my_var *mv= gl++;
+ 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(mv->s, 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
@@ -1195,8 +1233,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_eval(yy->get_offset(), item, zz->type);
+ }
+ }
+ else
+ {
+ if ((xx=li++))
+ xx->update();
+ }
+ }
DBUG_RETURN(0);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 06bc29dbb2a..75031ab768c 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -26,6 +26,8 @@
class Query_log_event;
class Load_log_event;
class Slave_log_event;
+class sp_rcontext;
+class sp_cache;
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY };
@@ -443,7 +445,8 @@ public:
ulong extra_length;
#endif
NET net; // client connection descriptor
- LEX lex; // parse tree descriptor
+ LEX main_lex;
+ LEX *lex; // parse tree descriptor
MEM_ROOT mem_root; // 1 command-life memory pool
MEM_ROOT con_root; // connection-life memory
MEM_ROOT warn_root; // For warnings and errors
@@ -592,11 +595,26 @@ 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;
bool charset_is_system_charset, charset_is_collation_connection;
+ sp_rcontext *spcont; // SP runtime context
+ sp_cache *sp_proc_cache;
+ sp_cache *sp_func_cache;
+
/*
If we do a purge of binary logs, log index info of the threads
that are currently reading it needs to be adjusted. To do that
@@ -634,7 +652,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)
{
@@ -1130,13 +1148,25 @@ public:
bool send_eof();
};
+class my_var : public Sql_alloc {
+public:
+ LEX_STRING s;
+ bool local;
+ uint offset;
+ enum_field_types type;
+ my_var (LEX_STRING& j, bool i, uint o, enum_field_types t)
+ :s(j), local(i), offset(o), type(t)
+ {}
+ ~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 cd0445b42e2..2d7f3cf0d98 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -238,7 +238,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);
@@ -292,7 +291,6 @@ int mysql_alter_db(THD *thd, const char *db, HA_CREATE_INFO *create_info)
thd->variables.collation_database= thd->db_charset;
}
- 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);
@@ -378,7 +376,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 070d4cbbce9..3a5cc6e5e06 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -38,18 +38,18 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
bool transactional_table, log_delayed, safe_update, const_cond;
ha_rows deleted;
TABLE_LIST *delete_table_list= (TABLE_LIST*)
- thd->lex.select_lex.table_list.first;
+ thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_delete");
if ((open_and_lock_tables(thd, table_list)))
DBUG_RETURN(-1);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
thd->proc_info="init";
table->map=1;
if (setup_conds(thd, delete_table_list, &conds) ||
- setup_ftfuncs(&thd->lex.select_lex))
+ setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1);
if (find_real_table_in_list(table_list->next,
table_list->db, table_list->real_name))
@@ -92,7 +92,7 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
if ((select && select->check_quick(thd, safe_update, limit)) || !limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
send_ok(thd,0L);
DBUG_RETURN(0); // Nothing to delete
}
@@ -100,11 +100,11 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
/* If running in safe sql mode, don't allow updates without keys */
if (!table->quick_keys)
{
- thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
+ thd->lex->select_lex.options|=QUERY_NO_INDEX_USED;
if (safe_update && !using_limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
send_error(thd,ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE);
DBUG_RETURN(1);
}
@@ -126,8 +126,8 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE),
MYF(MY_FAE | MY_ZEROFILL));
- if (thd->lex.select_lex.setup_ref_array(thd, 0) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array, &tables,
+ if (thd->lex->select_lex.setup_ref_array(thd, 0) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array, &tables,
fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
(table->sort.found_records = filesort(thd, table, sortorder, length,
@@ -136,14 +136,14 @@ int mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, ORDER *order,
== HA_POS_ERROR)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); // This will force out message
}
}
init_read_record(&info,thd,table,select,1,1);
deleted=0L;
- init_ftfuncs(thd, &thd->lex.select_lex, 1);
+ init_ftfuncs(thd, &thd->lex->select_lex, 1);
thd->proc_info="updating";
while (!(error=info.read_record(&info)) && !thd->killed &&
!thd->net.report_error)
@@ -198,7 +198,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,
@@ -221,9 +220,9 @@ cleanup:
thd->lock=0;
}
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ 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);
@@ -311,7 +310,7 @@ multi_delete::initialize_tables(JOIN *join)
table->file->ref_length,
MEM_STRIP_BUF_SIZE);
}
- init_ftfuncs(thd, thd->lex.current_select, 1);
+ init_ftfuncs(thd, thd->lex->current_select, 1);
DBUG_RETURN(thd->is_fatal_error != 0);
}
@@ -503,7 +502,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,
@@ -615,7 +613,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 7fd2b751c1d..9cfa58ec424 100644
--- a/sql/sql_derived.cc
+++ b/sql/sql_derived.cc
@@ -222,7 +222,13 @@ int mysql_derived(THD *thd, LEX *lex, SELECT_LEX_UNIT *unit,
else
unit->exclude_tree();
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_error.cc b/sql/sql_error.cc
index db0dbe0dedc..3676f4644d2 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -184,7 +184,7 @@ my_bool mysqld_show_warnings(THD *thd, ulong levels_to_show)
DBUG_RETURN(1);
MYSQL_ERROR *err;
- SELECT_LEX *sel= &thd->lex.select_lex;
+ SELECT_LEX *sel= &thd->lex->select_lex;
ha_rows offset= sel->offset_limit, limit= sel->select_limit;
Protocol *protocol=thd->protocol;
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 9077d4c6a2d..28324de11da 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
@@ -118,8 +115,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;
@@ -131,18 +127,9 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
char *query=thd->query;
thr_lock_type lock_type = table_list->lock_type;
TABLE_LIST *insert_table_list= (TABLE_LIST*)
- thd->lex.select_lex.table_list.first;
+ thd->lex->select_lex.table_list.first;
DBUG_ENTER("mysql_insert");
-#ifndef NO_EMBEDDED_ACCESS_CHECKS
- if (thd->master_access & SUPER_ACL)
-#endif
- {
- 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
@@ -188,7 +175,7 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
res= open_and_lock_tables(thd, table_list);
if (res)
DBUG_RETURN(-1);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
thd->proc_info="init";
@@ -377,7 +364,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,
@@ -427,14 +413,14 @@ int mysql_insert(THD *thd,TABLE_LIST *table_list,
(ulong) info.deleted, (ulong) thd->cuted_fields);
::send_ok(thd,info.copied+info.deleted,(ulonglong)id,buff);
}
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0;
DBUG_RETURN(0);
abort:
if (lock_type == TL_WRITE_DELAYED)
end_delayed_insert(thd);
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
table->insert_values=0;
DBUG_RETURN(-1);
}
@@ -603,13 +589,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()
{
@@ -642,7 +627,7 @@ public:
thd.current_tablenr=0;
thd.version=refresh_version;
thd.command=COM_DELAYED_INSERT;
- thd.lex.current_select= 0; /* for my_message_sql */
+ thd.lex->current_select= 0; /* for my_message_sql */
bzero((char*) &thd.net,sizeof(thd.net)); // Safety
thd.system_thread=1;
@@ -916,7 +901,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;
@@ -996,7 +981,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);
@@ -1035,7 +1020,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);
@@ -1088,7 +1073,7 @@ extern "C" pthread_handler_decl(handle_delayed_insert,arg)
for (;;)
{
- if (thd->killed)
+ if (thd->killed == THD::KILL_CONNECTION)
{
uint lock_count;
/*
@@ -1136,7 +1121,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;
}
}
@@ -1155,7 +1140,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);
}
@@ -1163,7 +1148,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;
@@ -1190,7 +1175,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);
@@ -1259,7 +1244,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
}
@@ -1303,15 +1288,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);
@@ -1468,7 +1448,6 @@ void select_insert::send_error(uint errcode,const char *err)
{
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
- 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,
@@ -1507,7 +1486,6 @@ bool select_insert::send_eof()
if (last_insert_id)
thd->insert_id(last_insert_id); // For binary log
/* Write to binlog before commiting transaction */
- 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_lex.cc b/sql/sql_lex.cc
index 5b05bf096cb..19d0ad69454 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};
@@ -106,9 +108,10 @@ void lex_free(void)
LEX *lex_start(THD *thd, uchar *buf,uint length)
{
- LEX *lex= &thd->lex;
+ 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.parsing_place= SELECT_LEX_NODE::NO_MATTER;
@@ -123,6 +126,14 @@ LEX *lex_start(THD *thd, uchar *buf,uint length)
lex->ignore_space=test(thd->variables.sql_mode & MODE_IGNORE_SPACE);
lex->sql_command=SQLCOM_END;
lex->duplicates= DUP_ERROR;
+ lex->sphead= NULL;
+ lex->spcont= NULL;
+
+ extern byte *sp_lex_spfuns_key(const byte *ptr, uint *plen, my_bool first);
+ hash_free(&lex->spfuns);
+ hash_init(&lex->spfuns, system_charset_info, 0, 0, 0,
+ sp_lex_spfuns_key, 0, 0);
+
return lex;
}
@@ -146,6 +157,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)))
@@ -421,7 +443,7 @@ int yylex(void *arg, void *yythd)
int tokval;
uint length;
enum my_lex_states state,prev_state;
- LEX *lex= &(((THD *)yythd)->lex);
+ LEX *lex= (((THD *)yythd)->lex);
YYSTYPE *yylval=(YYSTYPE*) arg;
CHARSET_INFO *cs= ((THD *) yythd)->charset();
uchar *state_map= cs->state_map;
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 7545f525082..463205e78b0 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
@@ -74,6 +77,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_DROP_USER, SQLCOM_REVOKE_ALL, SQLCOM_CHECKSUM,
+ SQLCOM_CREATE_PROCEDURE, SQLCOM_CREATE_SPFUNCTION, SQLCOM_CALL,
+ SQLCOM_DROP_PROCEDURE, SQLCOM_ALTER_PROCEDURE,SQLCOM_ALTER_FUNCTION,
/* This should be the last !!! */
SQLCOM_END
@@ -328,7 +333,7 @@ public:
int exec();
int cleanup();
- friend void mysql_init_query(THD *thd);
+ friend void mysql_init_query(THD *thd, bool lexonly);
friend int subselect_union_engine::exec();
private:
bool create_total_list_n_last_return(THD *thd, st_lex *lex,
@@ -349,6 +354,7 @@ public:
enum olap_type olap;
SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */
List<Item> item_list; /* list of fields & expressions */
+ List<Item> item_list_copy; /* For SPs */
List<String> interval_list, use_index, *use_index_ptr,
ignore_index, *ignore_index_ptr;
/*
@@ -460,7 +466,7 @@ public:
bool test_limit();
- friend void mysql_init_query(THD *thd);
+ friend void mysql_init_query(THD *thd, bool lexonly);
st_select_lex() {}
void make_empty_select()
{
@@ -484,6 +490,7 @@ typedef struct st_lex
SELECT_LEX *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 *help_arg;
@@ -546,7 +553,22 @@ typedef struct st_lex
bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog;
bool derived_tables, describe;
bool safe_to_cache_query;
- st_lex() {}
+ sp_head *sphead;
+ bool sp_lex_in_use; /* Keep track on lex usage in SPs for error handling */
+ sp_pcontext *spcont;
+ HASH spfuns; /* Called functions */
+
+ st_lex()
+ {
+ bzero((char *)&spfuns, sizeof(spfuns));
+ }
+
+ ~st_lex()
+ {
+ if (spfuns.array.buffer)
+ hash_free(&spfuns);
+ }
+
inline void uncacheable()
{
safe_to_cache_query= 0;
@@ -577,4 +599,4 @@ extern pthread_key(LEX*,THR_LEX);
extern LEX_STRING tmp_table_alias;
-#define current_lex (&current_thd->lex)
+#define current_lex (current_thd->lex)
diff --git a/sql/sql_load.cc b/sql/sql_load.cc
index 0e7895689b5..c09101ba5d0 100644
--- a/sql/sql_load.cc
+++ b/sql/sql_load.cc
@@ -344,9 +344,6 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
sprintf(name, ER(ER_LOAD_INFO), (ulong) info.records, (ulong) info.deleted,
(ulong) (info.records - info.copied), (ulong) 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;
@@ -412,7 +409,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();
@@ -500,7 +497,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 e8870b20d99..b659806ec85 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 "C" pthread_mutex_t THR_LOCK_keycache;
#ifdef SOLARIS
extern "C" int gethostname(char *name, int namelen);
@@ -894,7 +906,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;
@@ -1091,7 +1103,7 @@ bool do_command(THD *thd)
indicator of uninitialized lex => normal flow of errors handling
(see my_message_sql)
*/
- thd->lex.current_select= 0;
+ thd->lex->current_select= 0;
packet=0;
old_timeout=net->read_timeout;
@@ -1117,6 +1129,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)
@@ -1152,7 +1167,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thread_running++;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
- thd->lex.select_lex.options=0; // We store status here
+ thd->lex->select_lex.options=0; // We store status here
switch (command) {
case COM_INIT_DB:
{
@@ -1294,16 +1309,16 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
DBUG_PRINT("query",("%-.4096s",thd->query));
mysql_parse(thd,thd->query, thd->query_length);
- while (!thd->killed && !thd->is_fatal_error && thd->lex.found_colon)
+ while (!thd->killed && !thd->is_fatal_error && thd->lex->found_colon)
{
- char *packet= thd->lex.found_colon;
+ char *packet= thd->lex->found_colon;
/*
Multiple queries exits, execute them individually
*/
if (thd->lock || thd->open_tables || thd->derived_tables)
close_thread_tables(thd);
- ulong length= thd->query_length-(ulong)(thd->lex.found_colon-thd->query);
+ ulong length= thd->query_length-(ulong)(thd->lex->found_colon-thd->query);
/* Remove garbage at start of query */
while (my_isspace(thd->charset(), *packet) && length > 0)
@@ -1525,7 +1540,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
{
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:
@@ -1564,7 +1579,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
if ((ulong) (thd->start_time - thd->time_after_lock) >
thd->variables.long_query_time ||
- ((thd->lex.select_lex.options &
+ ((thd->lex->select_lex.options &
(QUERY_NO_INDEX_USED | QUERY_NO_GOOD_INDEX_USED)) &&
(specialflag & SPECIAL_LOG_QUERIES_NOT_USING_INDEXES)))
{
@@ -1580,6 +1595,7 @@ bool dispatch_command(enum enum_server_command command, THD *thd,
thread_running--;
VOID(pthread_mutex_unlock(&LOCK_thread_count));
thd->packet.shrink(thd->variables.net_buffer_length); // Reclaim some memory
+
free_root(&thd->mem_root,MYF(MY_KEEP_PREALLOC));
DBUG_RETURN(error);
}
@@ -1635,16 +1651,23 @@ 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;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
TABLE_LIST *tables= (TABLE_LIST*) lex->select_lex.table_list.first;
SELECT_LEX *select_lex= &lex->select_lex;
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
@@ -1670,7 +1693,7 @@ mysql_execute_command(THD *thd)
{
/* we warn the slave SQL thread */
my_error(ER_SLAVE_IGNORED_TABLE, MYF(0));
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
}
#ifndef TO_BE_DELETED
/*
@@ -1706,14 +1729,14 @@ mysql_execute_command(THD *thd)
{
if (res < 0 || thd->net.report_error)
send_error(thd,thd->killed ? ER_SERVER_SHUTDOWN : 0);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(res);
}
}
}
}
if (&lex->select_lex != lex->all_selects_list &&
lex->unit.create_total_list(thd, lex, &tables, 0))
- DBUG_VOID_RETURN;
+ DBUG_RETURN(0);
/*
When option readonly is set deny operations which change tables.
@@ -1728,7 +1751,7 @@ mysql_execute_command(THD *thd)
(uc_update_queries[lex->sql_command] > 0))
{
send_error(thd, ER_CANT_UPDATE_WITH_READLOCK);
- DBUG_VOID_RETURN;
+ DBUG_RETURN(-1);
}
statistic_increment(com_stat[lex->sql_command],&LOCK_status);
@@ -1773,12 +1796,12 @@ 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);
fix_tables_pointers(lex->all_selects_list);
- res= mysql_explain_union(thd, &thd->lex.unit, result);
+ res= mysql_explain_union(thd, &thd->lex->unit, result);
MYSQL_LOCK *save_lock= thd->lock;
thd->lock= (MYSQL_LOCK *)0;
result->send_eof();
@@ -2053,7 +2076,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);
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (tables->next)
@@ -2141,7 +2164,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;
@@ -2154,7 +2177,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;
@@ -2252,7 +2275,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))
@@ -2265,7 +2288,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) ||
@@ -2293,7 +2316,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- 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);
@@ -2319,7 +2341,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- 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);
@@ -2358,7 +2379,6 @@ mysql_execute_command(THD *thd)
/* ! we write after unlocking the table */
if (!res && !lex->no_write_to_binlog)
{
- 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);
@@ -2376,7 +2396,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,
@@ -2399,7 +2419,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;
@@ -2438,7 +2458,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,
@@ -2538,7 +2558,7 @@ mysql_execute_command(THD *thd)
}
case SQLCOM_DELETE_MULTI:
{
- TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex.auxilliary_table_list.first;
+ TABLE_LIST *aux_tables=(TABLE_LIST *)thd->lex->auxilliary_table_list.first;
TABLE_LIST *auxi;
uint table_count=0;
multi_delete *result;
@@ -2664,7 +2684,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))
@@ -2707,7 +2727,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
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -2722,7 +2742,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;
@@ -2771,7 +2791,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;
@@ -2798,7 +2818,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;
@@ -2990,26 +3010,22 @@ 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,0))
- break;
-#ifdef HAVE_DLOPEN
- if (!(res = mysql_create_function(thd,&lex->udf)))
- send_ok(thd);
-#else
- res= -1;
-#endif
- break;
- case SQLCOM_DROP_FUNCTION:
- if (check_access(thd,DELETE_ACL,"mysql",0,1,0))
- break;
+ case SQLCOM_CREATE_FUNCTION: // UDF function
+ {
+ if (check_access(thd,INSERT_ACL,"mysql",0,1,0))
+ break;
#ifdef HAVE_DLOPEN
- if (!(res = mysql_drop_function(thd,&lex->udf.name)))
- send_ok(thd);
+ sp_head *sph= sp_find_function(thd, &lex->udf.name);
+ if (sph)
+ {
+ net_printf(thd, ER_UDF_EXISTS, lex->udf.name.str);
+ goto error;
+ }
+ if (!(res = mysql_create_function(thd,&lex->udf)))
+ send_ok(thd);
#else
- res= -1;
-#endif
break;
+ }
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_DROP_USER:
{
@@ -3017,7 +3033,6 @@ mysql_execute_command(THD *thd)
break;
if (!(res= mysql_drop_user(thd, lex->users_list)))
{
- 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);
@@ -3033,7 +3048,6 @@ mysql_execute_command(THD *thd)
break;
if (!(res = mysql_revoke_all(thd, lex->users_list)))
{
- 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);
@@ -3084,14 +3098,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
@@ -3106,7 +3117,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);
@@ -3149,7 +3159,6 @@ mysql_execute_command(THD *thd)
*/
if (!lex->no_write_to_binlog && write_to_binlog)
{
- 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);
@@ -3161,7 +3170,7 @@ mysql_execute_command(THD *thd)
break;
}
case SQLCOM_KILL:
- kill_one_thread(thd,lex->thread_id);
+ kill_one_thread(thd,lex->thread_id, lex->type & ONLY_KILL_QUERY);
break;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
case SQLCOM_SHOW_GRANTS:
@@ -3273,16 +3282,200 @@ mysql_execute_command(THD *thd)
else
res= -1;
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
+ if (lex->sphead->m_type == TYPE_ENUM_FUNCTION &&
+ !lex->sphead->m_has_return)
+ {
+ net_printf(thd, ER_SP_NORETURN, name);
+ goto error;
+ }
+
+ res= lex->sphead->create(thd);
+
+ 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))))
+ {
+ 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_results)
+ {
+ if (! (thd->client_capabilities & CLIENT_MULTI_RESULTS))
+ {
+ send_error(thd, ER_SP_BADSELECT);
+#ifndef EMBEDDED_LIBRARY
+ thd->net.no_send_ok= nsok;
+#endif
+ 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_results)
+ {
+ if (! smrx)
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ }
+
+ 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. */
+ 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);
}
@@ -3591,10 +3784,10 @@ bool my_yyoverflow(short **yyss, YYSTYPE **yyvs, int *yystacksize)
****************************************************************************/
void
-mysql_init_query(THD *thd)
+mysql_init_query(THD *thd, bool lexonly)
{
DBUG_ENTER("mysql_init_query");
- LEX *lex=&thd->lex;
+ LEX *lex=thd->lex;
lex->unit.init_query();
lex->unit.init_select();
lex->unit.thd= thd;
@@ -3610,22 +3803,27 @@ mysql_init_query(THD *thd)
lex->select_lex.prev= &lex->unit.slave;
lex->select_lex.link_next= lex->select_lex.slave= lex->select_lex.next= 0;
lex->select_lex.link_prev= (st_select_lex_node**)&(lex->all_selects_list);
+ lex->select_lex.init_order();
+ lex->select_lex.group_list.empty();
lex->describe= 0;
lex->derived_tables= FALSE;
lex->lock_option= TL_READ;
lex->found_colon= 0;
lex->safe_to_cache_query= 1;
- thd->select_number= lex->select_lex.select_number= 1;
- thd->free_list= 0;
- thd->total_warn_count=0; // Warnings for this query
- thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
- thd->sent_row_count= thd->examined_row_count= 0;
- thd->is_fatal_error= thd->rand_used= 0;
- thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
- thd->tmp_table_used= 0;
- if (opt_bin_log)
- reset_dynamic(&thd->user_var_events);
- thd->clear_error();
+ if (! lexonly)
+ {
+ thd->select_number= lex->select_lex.select_number= 1;
+ thd->free_list= 0;
+ thd->total_warn_count=0; // Warnings for this query
+ thd->last_insert_id_used= thd->query_start_used= thd->insert_id_used=0;
+ thd->sent_row_count= thd->examined_row_count= 0;
+ thd->is_fatal_error= thd->rand_used= 0;
+ thd->server_status &= ~SERVER_MORE_RESULTS_EXISTS;
+ thd->tmp_table_used= 0;
+ if (opt_bin_log)
+ reset_dynamic(&thd->user_var_events);
+ thd->clear_error();
+ }
DBUG_VOID_RETURN;
}
@@ -3719,7 +3917,7 @@ void create_select_for_variable(const char *var_name)
DBUG_ENTER("create_select_for_variable");
thd= current_thd;
- lex= &thd->lex;
+ lex= thd->lex;
mysql_init_select(lex);
lex->sql_command= SQLCOM_SELECT;
tmp.str= (char*) var_name;
@@ -3762,7 +3960,16 @@ mysql_parse(THD *thd, char *inBuf, uint length)
#endif
{
if (thd->net.report_error)
+ {
send_error(thd, 0, NullS);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
else
{
mysql_execute_command(thd);
@@ -3778,6 +3985,13 @@ mysql_parse(THD *thd, char *inBuf, uint length)
thd->is_fatal_error));
#ifndef EMBEDDED_LIBRARY /* TODO query cache in embedded library*/
query_cache_abort(&thd->net);
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
#endif
}
thd->proc_info="freeing items";
@@ -3801,7 +4015,7 @@ bool add_field_to_list(THD *thd, char *field_name, enum_field_types type,
uint uint_geom_type)
{
register create_field *new_field;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
uint allowed_type_modifier=0;
char warn_buff[MYSQL_ERRMSG_SIZE];
DBUG_ENTER("add_field_to_list");
@@ -4129,7 +4343,7 @@ add_proc_to_list(THD* thd, Item *item)
*item_ptr= item;
order->item=item_ptr;
order->free_me=0;
- thd->lex.proc_list.link_in_list((byte*) order,(byte**) &order->next);
+ thd->lex->proc_list.link_in_list((byte*) order,(byte**) &order->next);
return 0;
}
@@ -4413,7 +4627,6 @@ bool reload_acl_and_cache(THD *thd, ulong options, TABLE_LIST *tables,
*/
tmp_write_to_binlog= 0;
mysql_log.new_file(1);
- mysql_update_log.new_file(1);
mysql_bin_log.new_file(1);
mysql_slow_log.new_file(1);
#ifdef HAVE_REPLICATION
@@ -4513,7 +4726,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;
@@ -4535,7 +4748,7 @@ void kill_one_thread(THD *thd, ulong id)
!strcmp(thd->user,tmp->user))
#endif
{
- tmp->awake(1 /*prepare to die*/);
+ tmp->awake(only_kill_query ? THD::KILL_QUERY : THD::KILL_CONNECTION);
error=0;
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -4607,11 +4820,11 @@ static bool append_file_to_dir(THD *thd, char **filename_ptr, char *table_name)
bool check_simple_select()
{
THD *thd= current_thd;
- if (thd->lex.current_select != &thd->lex.select_lex)
+ if (thd->lex->current_select != &thd->lex->select_lex)
{
char command[80];
- strmake(command, thd->lex.yylval->symbol.str,
- min(thd->lex.yylval->symbol.length, sizeof(command)-1));
+ strmake(command, thd->lex->yylval->symbol.str,
+ min(thd->lex->yylval->symbol.length, sizeof(command)-1));
net_printf(thd, ER_CANT_USE_OPTION_HERE, command);
return 1;
}
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index 522879c863a..cb417a43535 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -71,6 +71,7 @@ Long data handling:
#include "sql_acl.h"
#include "sql_select.h" // for JOIN
#include <m_ctype.h> // for isspace()
+#include "sp_head.h"
#define IS_PARAM_NULL(pos, param_no) pos[param_no/8] & (1 << param_no & 7)
@@ -421,7 +422,7 @@ void setup_param_functions(Item_param *param, uchar param_type)
static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("insert_params_withlog");
@@ -467,7 +468,7 @@ static bool insert_params_withlog(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("insert_params");
@@ -493,7 +494,7 @@ static bool insert_params(PREP_STMT *stmt, uchar *pos, uchar *read_pos)
static bool setup_params_data(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
List_iterator<Item> param_iterator(params);
Item_param *param;
DBUG_ENTER("setup_params_data");
@@ -538,8 +539,8 @@ static bool mysql_test_insert_fields(PREP_STMT *stmt,
DBUG_ENTER("mysql_test_insert_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
- my_bool update=(thd->lex.value_list.elements ? UPDATE_ACL : 0);
- ulong privilege= (thd->lex.duplicates == DUP_REPLACE ?
+ my_bool update=(thd->lex->value_list.elements ? UPDATE_ACL : 0);
+ ulong privilege= (thd->lex->duplicates == DUP_REPLACE ?
INSERT_ACL | DELETE_ACL : INSERT_ACL | update);
if (check_access(thd,privilege,table_list->db,
@@ -640,8 +641,8 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
SELECT_LEX *select_lex)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->lex;
- select_result *result= thd->lex.result;
+ LEX *lex= thd->lex;
+ select_result *result= thd->lex->result;
DBUG_ENTER("mysql_test_select_fields");
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -668,7 +669,7 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
}
else
{
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
if (!result && !(result= new select_send()))
{
delete select_lex->having;
@@ -704,8 +705,8 @@ static bool mysql_test_select_fields(PREP_STMT *stmt, TABLE_LIST *tables,
static bool send_prepare_results(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- LEX *lex= &thd->lex;
- enum enum_sql_command sql_command= thd->lex.sql_command;
+ LEX *lex= thd->lex;
+ enum enum_sql_command sql_command= thd->lex->sql_command;
DBUG_ENTER("send_prepare_results");
DBUG_PRINT("enter",("command: %d, param_count: %ld",
sql_command, lex->param_count));
@@ -766,7 +767,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);
}
@@ -786,9 +787,19 @@ static bool parse_prepare_query(PREP_STMT *stmt,
LEX *lex=lex_start(thd, (uchar*) packet, length);
lex->safe_to_cache_query= 0;
thd->prepare_command= TRUE;
- thd->lex.param_count= 0;
+ thd->lex->param_count= 0;
if (!yyparse((void *)thd) && !thd->is_fatal_error)
error= send_prepare_results(stmt);
+ else
+ {
+ if (thd->lex->sphead)
+ {
+ if (lex != thd->lex)
+ thd->lex->sphead->restore_lex(thd);
+ delete thd->lex->sphead;
+ thd->lex->sphead= NULL;
+ }
+ }
lex_end(lex);
DBUG_RETURN(error);
}
@@ -800,13 +811,13 @@ static bool parse_prepare_query(PREP_STMT *stmt,
static bool init_param_items(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- List<Item> &params= thd->lex.param_list;
+ List<Item> &params= thd->lex->param_list;
Item_param **to;
uint32 length= thd->query_length;
- stmt->lex= thd->lex;
+ stmt->lex= *thd->lex;
- if (mysql_bin_log.is_open() || mysql_update_log.is_open())
+ if (mysql_bin_log.is_open())
{
stmt->log_full_query= 1;
#ifndef EMBEDDED_LIBRARY
@@ -853,7 +864,7 @@ static bool init_param_items(PREP_STMT *stmt)
static void init_stmt_execute(PREP_STMT *stmt)
{
THD *thd= stmt->thd;
- TABLE_LIST *tables= (TABLE_LIST*) thd->lex.select_lex.table_list.first;
+ TABLE_LIST *tables= (TABLE_LIST*) thd->lex->select_lex.table_list.first;
/*
TODO: When the new table structure is ready, then have a status bit
@@ -911,7 +922,7 @@ bool mysql_stmt_prepare(THD *thd, char *packet, uint packet_length)
my_pthread_setprio(pthread_self(),WAIT_PRIOR);
// save WHERE clause pointers to avoid damaging they by optimisation
- for (sl= thd->lex.all_selects_list;
+ for (sl= thd->lex->all_selects_list;
sl;
sl= sl->next_select_in_list())
{
@@ -964,8 +975,8 @@ void mysql_stmt_execute(THD *thd, char *packet)
DBUG_VOID_RETURN;
}
- LEX thd_lex= thd->lex;
- thd->lex= stmt->lex;
+ LEX *thd_lex= thd->lex;
+ thd->lex= &stmt->lex;
for (sl= stmt->lex.all_selects_list;
sl;
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 5db3be7599b..8913910230a 100644
--- a/sql/sql_repl.cc
+++ b/sql/sql_repl.cc
@@ -678,8 +678,8 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
was running (as we don't wan't to touch the other thread), so set the
bit to 0 for the other thread
*/
- if (thd->lex.slave_thd_opt)
- thread_mask &= thd->lex.slave_thd_opt;
+ if (thd->lex->slave_thd_opt)
+ thread_mask &= thd->lex->slave_thd_opt;
if (thread_mask) //some threads are stopped, start them
{
if (init_master_info(mi,master_info_file,relay_log_info_file, 0))
@@ -695,22 +695,22 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
{
pthread_mutex_lock(&mi->rli.data_lock);
- if (thd->lex.mi.pos)
+ if (thd->lex->mi.pos)
{
mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_MASTER_POS;
- mi->rli.until_log_pos= thd->lex.mi.pos;
+ mi->rli.until_log_pos= thd->lex->mi.pos;
/*
We don't check thd->lex.mi.log_file_name for NULL here
since it is checked in sql_yacc.yy
*/
- strmake(mi->rli.until_log_name, thd->lex.mi.log_file_name,
+ strmake(mi->rli.until_log_name, thd->lex->mi.log_file_name,
sizeof(mi->rli.until_log_name)-1);
}
- else if (thd->lex.mi.relay_log_pos)
+ else if (thd->lex->mi.relay_log_pos)
{
mi->rli.until_condition= RELAY_LOG_INFO::UNTIL_RELAY_POS;
- mi->rli.until_log_pos= thd->lex.mi.relay_log_pos;
- strmake(mi->rli.until_log_name, thd->lex.mi.relay_log_name,
+ mi->rli.until_log_pos= thd->lex->mi.relay_log_pos;
+ strmake(mi->rli.until_log_name, thd->lex->mi.relay_log_name,
sizeof(mi->rli.until_log_name)-1);
}
else
@@ -748,7 +748,7 @@ int start_slave(THD* thd , MASTER_INFO* mi, bool net_report)
pthread_mutex_unlock(&mi->rli.data_lock);
}
- else if (thd->lex.mi.pos || thd->lex.mi.relay_log_pos)
+ else if (thd->lex->mi.pos || thd->lex->mi.relay_log_pos)
push_warning(thd, MYSQL_ERROR::WARN_LEVEL_NOTE, ER_UNTIL_COND_IGNORED,
ER(ER_UNTIL_COND_IGNORED));
@@ -802,8 +802,8 @@ int stop_slave(THD* thd, MASTER_INFO* mi, bool net_report )
was stopped (as we don't wan't to touch the other thread), so set the
bit to 0 for the other thread
*/
- if (thd->lex.slave_thd_opt)
- thread_mask &= thd->lex.slave_thd_opt;
+ if (thd->lex->slave_thd_opt)
+ thread_mask &= thd->lex->slave_thd_opt;
if (thread_mask)
{
@@ -952,7 +952,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);
}
}
@@ -975,7 +975,7 @@ int change_master(THD* thd, MASTER_INFO* mi)
}
thd->proc_info = "Changing master";
- LEX_MASTER_INFO* lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO* lex_mi = &thd->lex->mi;
// TODO: see if needs re-write
if (init_master_info(mi, master_info_file, relay_log_info_file, 0))
{
@@ -1159,7 +1159,7 @@ int show_binlog_events(THD* thd)
if (mysql_bin_log.is_open())
{
- LEX_MASTER_INFO *lex_mi = &thd->lex.mi;
+ LEX_MASTER_INFO *lex_mi = &thd->lex->mi;
ha_rows event_count, limit_start, limit_end;
my_off_t pos = max(BIN_LOG_HEADER_SIZE, lex_mi->pos); // user-friendly
char search_file_name[FN_REFLEN], *name;
@@ -1168,8 +1168,8 @@ int show_binlog_events(THD* thd)
LOG_INFO linfo;
Log_event* ev;
- limit_start = thd->lex.current_select->offset_limit;
- limit_end = thd->lex.current_select->select_limit + limit_start;
+ limit_start = thd->lex->current_select->offset_limit;
+ limit_end = thd->lex->current_select->select_limit + limit_start;
name= search_file_name;
if (log_file_name)
diff --git a/sql/sql_repl.h b/sql/sql_repl.h
index fe1b7167d4a..2ff38029b7b 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 9bdb989b6a1..718ba141e3d 100644
--- a/sql/sql_select.cc
+++ b/sql/sql_select.cc
@@ -206,7 +206,8 @@ int handle_select(THD *thd, LEX *lex, select_result *result)
send_error(thd, 0, NullS);
res= 1; // Error sent to client
}
- delete result;
+ if (result != lex->result)
+ delete result;
DBUG_RETURN(res);
}
@@ -502,8 +503,8 @@ JOIN::optimize()
optimized= 1;
// Ignore errors of execution if option IGNORE present
- if (thd->lex.duplicates == DUP_IGNORE)
- thd->lex.current_select->no_error= 1;
+ if (thd->lex->duplicates == DUP_IGNORE)
+ thd->lex->current_select->no_error= 1;
#ifdef HAVE_REF_TO_FIELDS // Not done yet
/* Add HAVING to WHERE if possible */
if (having && !group_list && !sum_func_count)
@@ -980,7 +981,7 @@ JOIN::optimize()
}
}
- if (select_lex != &thd->lex.select_lex &&
+ if (select_lex != &thd->lex->select_lex &&
select_lex->linkage != DERIVED_TABLE_TYPE)
{
if (!(tmp_join= (JOIN*)thd->alloc(sizeof(JOIN))))
@@ -2393,7 +2394,7 @@ update_ref_and_keys(THD *thd, DYNAMIC_ARRAY *keyuse,JOIN_TAB *join_tab,
if (!(key_fields=(KEY_FIELD*)
thd->alloc(sizeof(key_fields[0])*
- (thd->lex.current_select->cond_count+1)*2)))
+ (thd->lex->current_select->cond_count+1)*2)))
return TRUE; /* purecov: inspected */
and_level=0; end=key_fields;
if (cond)
@@ -3447,7 +3448,7 @@ static void
make_join_readinfo(JOIN *join, uint options)
{
uint i;
- SELECT_LEX *select_lex = &(join->thd->lex.select_lex);
+ SELECT_LEX *select_lex = &(join->thd->lex->select_lex);
DBUG_ENTER("make_join_readinfo");
for (i=join->const_tables ; i < join->tables ; i++)
@@ -5210,7 +5211,7 @@ bool create_myisam_from_heap(THD *thd, TABLE *table, TMP_TABLE_PARAM *param,
thd->proc_info="converting HEAP to MyISAM";
if (create_myisam_tmp_table(&new_table,param,
- thd->lex.select_lex.options | thd->options))
+ thd->lex->select_lex.options | thd->options))
goto err2;
if (open_tmp_table(&new_table))
goto err1;
@@ -5408,7 +5409,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)
@@ -5448,7 +5449,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++;
@@ -5529,7 +5530,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;
@@ -6154,7 +6155,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)
@@ -6222,7 +6223,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 */
}
@@ -6292,7 +6293,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 */
}
@@ -6339,7 +6340,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 ||
@@ -7094,7 +7095,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;
}
@@ -7206,7 +7207,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;
}
@@ -8685,7 +8686,7 @@ static void select_describe(JOIN *join, bool need_tmp_table, bool need_order,
List<Item> field_list;
List<Item> item_list;
THD *thd=join->thd;
- SELECT_LEX *select_lex = &(join->thd->lex.select_lex);
+ SELECT_LEX *select_lex = &(join->thd->lex->select_lex);
select_result *result=join->result;
Item *item_null= new Item_null();
CHARSET_INFO *cs= &my_charset_latin1;
@@ -8870,8 +8871,8 @@ int mysql_explain_union(THD *thd, SELECT_LEX_UNIT *unit, select_result *result)
sl= sl->next_select())
{
res= mysql_explain_select(thd, sl,
- (((&thd->lex.select_lex)==sl)?
- ((thd->lex.all_selects_list != sl)?"PRIMARY":
+ (((&thd->lex->select_lex)==sl)?
+ ((thd->lex->all_selects_list != sl)?"PRIMARY":
"SIMPLE"):
((sl == first)?
((sl->linkage == DERIVED_TABLE_TYPE) ?
@@ -8899,7 +8900,7 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
DBUG_ENTER("mysql_explain_select");
DBUG_PRINT("info", ("Select 0x%lx, type %s", (ulong)select_lex, type))
select_lex->type= type;
- thd->lex.current_select= select_lex;
+ thd->lex->current_select= select_lex;
SELECT_LEX_UNIT *unit= select_lex->master_unit();
int res= mysql_select(thd, &select_lex->ref_pointer_array,
(TABLE_LIST*) select_lex->table_list.first,
@@ -8910,7 +8911,7 @@ int mysql_explain_select(THD *thd, SELECT_LEX *select_lex, char const *type,
(ORDER*) select_lex->order_list.first,
(ORDER*) select_lex->group_list.first,
select_lex->having,
- (ORDER*) thd->lex.proc_list.first,
+ (ORDER*) thd->lex->proc_list.first,
select_lex->options | thd->options | SELECT_DESCRIBE,
result, unit, select_lex, 0);
DBUG_RETURN(res);
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index fce25da2cd0..cba84f93c49 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -1400,7 +1400,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 3d07c7e04de..458dd15babb 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);
}
}
@@ -954,17 +950,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:
@@ -1211,7 +1203,7 @@ static int prepare_for_restore(THD* thd, TABLE_LIST* table,
}
else
{
- char* backup_dir = thd->lex.backup_dir;
+ char* backup_dir = thd->lex->backup_dir;
char src_path[FN_REFLEN], dst_path[FN_REFLEN];
char* table_name = table->real_name;
char* db = thd->db ? thd->db : table->db;
@@ -1973,7 +1965,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);
@@ -2356,7 +2347,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);
@@ -2487,7 +2477,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);
@@ -2585,8 +2574,8 @@ copy_data_between_tables(TABLE *from,TABLE *to,
tables.db = from->table_cache_key;
error=1;
- if (thd->lex.select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+ if (thd->lex->select_lex.setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, fields, all_fields, order) ||
!(sortorder=make_unireg_sortorder(order, &length)) ||
(from->sort.found_records = filesort(thd, from, sortorder, length,
@@ -2616,7 +2605,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_union.cc b/sql/sql_union.cc
index 3a903d2e896..934cda3b1a8 100644
--- a/sql/sql_union.cc
+++ b/sql/sql_union.cc
@@ -115,7 +115,7 @@ bool select_union::flush()
int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
bool tables_and_fields_initied)
{
- SELECT_LEX *lex_select_save= thd->lex.current_select;
+ SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *select_cursor,*sl;
DBUG_ENTER("st_select_lex_unit::prepare");
@@ -129,7 +129,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
t_and_f= tables_and_fields_initied;
bzero((char *)&tmp_table_param,sizeof(TMP_TABLE_PARAM));
- thd->lex.current_select= sl= select_cursor= first_select_in_union();
+ thd->lex->current_select= sl= select_cursor= first_select_in_union();
/* Global option */
if (t_and_f)
{
@@ -192,7 +192,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
JOIN *join= new JOIN(thd, sl->item_list,
sl->options | thd->options | SELECT_NO_UNLOCK,
union_result);
- thd->lex.current_select= sl;
+ thd->lex->current_select= sl;
offset_limit_cnt= sl->offset_limit;
select_limit_cnt= sl->select_limit+sl->offset_limit;
if (select_limit_cnt < sl->select_limit)
@@ -217,7 +217,7 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
}
item_list.empty();
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
{
List_iterator<Item> it(select_cursor->item_list);
Field **field;
@@ -232,14 +232,14 @@ int st_select_lex_unit::prepare(THD *thd, select_result *sel_result,
DBUG_RETURN(res || thd->is_fatal_error ? 1 : 0);
err:
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(-1);
}
int st_select_lex_unit::exec()
{
- SELECT_LEX *lex_select_save= thd->lex.current_select;
+ SELECT_LEX *lex_select_save= thd->lex->current_select;
SELECT_LEX *select_cursor=first_select_in_union();
ulonglong add_rows=0;
DBUG_ENTER("st_select_lex_unit::exec");
@@ -259,7 +259,7 @@ int st_select_lex_unit::exec()
for (SELECT_LEX *sl= select_cursor; sl; sl= sl->next_select())
{
ha_rows records_at_start= 0;
- thd->lex.current_select= sl;
+ thd->lex->current_select= sl;
if (optimized)
res= sl->join->reinit();
@@ -331,13 +331,13 @@ int st_select_lex_unit::exec()
offset_limit_cnt= sl->offset_limit;
if (!res && union_result->flush())
{
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(1);
}
}
if (res)
{
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(res);
}
/* Needed for the following test and for records_at_start in next loop */
@@ -368,7 +368,7 @@ int st_select_lex_unit::exec()
if (!thd->is_fatal_error) // Check if EOM
{
ulong options= thd->options;
- thd->lex.current_select= fake_select_lex;
+ thd->lex->current_select= fake_select_lex;
offset_limit_cnt= global_parameters->offset_limit;
select_limit_cnt= global_parameters->select_limit +
global_parameters->offset_limit;
@@ -422,7 +422,7 @@ int st_select_lex_unit::exec()
*/
}
}
- thd->lex.current_select= lex_select_save;
+ thd->lex->current_select= lex_select_save;
DBUG_RETURN(res);
}
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index e14e20b62d6..8c400b2e98e 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -68,7 +68,7 @@ int mysql_update(THD *thd,
SQL_SELECT *select;
READ_RECORD info;
TABLE_LIST *update_table_list= ((TABLE_LIST*)
- thd->lex.select_lex.table_list.first);
+ thd->lex->select_lex.table_list.first);
TABLE_LIST tables;
List<Item> all_fields;
DBUG_ENTER("mysql_update");
@@ -79,7 +79,7 @@ int mysql_update(THD *thd,
if ((open_and_lock_tables(thd, table_list)))
DBUG_RETURN(-1);
thd->proc_info="init";
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
table= table_list->table;
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -97,10 +97,10 @@ int mysql_update(THD *thd,
if (setup_tables(update_table_list) ||
setup_conds(thd,update_table_list,&conds) ||
- thd->lex.select_lex.setup_ref_array(thd, order_num) ||
- setup_order(thd, thd->lex.select_lex.ref_pointer_array,
+ thd->lex->select_lex.setup_ref_array(thd, order_num) ||
+ setup_order(thd, thd->lex->select_lex.ref_pointer_array,
&tables, all_fields, all_fields, order) ||
- setup_ftfuncs(&thd->lex.select_lex))
+ setup_ftfuncs(&thd->lex->select_lex))
DBUG_RETURN(-1); /* purecov: inspected */
/* Check that we are not using table that we are updating in a sub select */
@@ -144,7 +144,7 @@ int mysql_update(THD *thd,
#endif
if (setup_fields(thd, 0, update_table_list, values, 0, 0, 0))
{
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
DBUG_RETURN(-1); /* purecov: inspected */
}
@@ -155,7 +155,7 @@ int mysql_update(THD *thd,
(select && select->check_quick(thd, safe_update, limit)) || !limit)
{
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (error)
{
DBUG_RETURN(-1); // Error in where
@@ -166,7 +166,7 @@ int mysql_update(THD *thd,
/* If running in safe sql mode, don't allow updates without keys */
if (!table->quick_keys)
{
- thd->lex.select_lex.options|=QUERY_NO_INDEX_USED;
+ thd->lex->select_lex.options|=QUERY_NO_INDEX_USED;
if (safe_update && !using_limit)
{
my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE,
@@ -174,7 +174,7 @@ int mysql_update(THD *thd,
goto err;
}
}
- init_ftfuncs(thd, &thd->lex.select_lex, 1);
+ init_ftfuncs(thd, &thd->lex->select_lex, 1);
/* Check if we are modifying a key that we are used to search with */
if (select && select->quick)
used_key_is_modified= (!select->quick->unique_key_range() &&
@@ -349,7 +349,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,
@@ -373,9 +372,9 @@ int mysql_update(THD *thd,
}
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ 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];
@@ -392,7 +391,7 @@ int mysql_update(THD *thd,
err:
delete select;
- free_underlaid_joins(thd, &thd->lex.select_lex);
+ free_underlaid_joins(thd, &thd->lex->select_lex);
if (table->key_read)
{
table->key_read=0;
@@ -429,7 +428,7 @@ int mysql_multi_update(THD *thd,
#endif
if ((res=open_and_lock_tables(thd,table_list)))
DBUG_RETURN(res);
- fix_tables_pointers(thd->lex.all_selects_list);
+ fix_tables_pointers(thd->lex->all_selects_list);
select_lex->select_limit= HA_POS_ERROR;
if (setup_fields(thd, 0, table_list, *fields, 1, 0, 0))
@@ -978,7 +977,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 8491ba7675d..599a55693ed 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -28,13 +28,17 @@
#define MYSQL_YACC
#define YYINITDEPTH 100
#define YYMAXDEPTH 3200 /* Because of 64K stack */
-#define Lex (&(YYTHD->lex))
+#define Lex ((YYTHD->lex))
#define Select Lex->current_select
#include "mysql_priv.h"
#include "slave.h"
#include "sql_acl.h"
#include "lex_symbol.h"
#include "item_create.h"
+#include "sp_head.h"
+#include "sp_pcontext.h"
+#include "sp_rcontext.h"
+#include "sp.h"
#include <myisam.h>
#include <myisammrg.h>
@@ -81,6 +85,9 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B)
datetime_format_types datetime_format_type;
st_select_lex *select_lex;
chooser_compare_func_creator boolfunc2creator;
+ struct sp_cond_type *spcondtype;
+ struct { int vars, conds, hndlrs, curs; } spblock;
+ struct st_lex *lex;
}
%{
@@ -121,6 +128,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
@@ -201,10 +209,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token COLUMNS
%token COLUMN_SYM
%token CONCURRENT
+%token CONDITION_SYM
+%token CONNECTION_SYM
%token CONSTRAINT
+%token CONTINUE_SYM
%token CONVERT_SYM
%token DATABASES
%token DATA_SYM
+%token DECLARE_SYM
%token DEFAULT
%token DELAYED_SYM
%token DELAY_KEY_WRITE_SYM
@@ -222,14 +234,17 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token DIRECTORY_SYM
%token ESCAPE_SYM
%token EXISTS
+%token EXIT_SYM
%token EXTENDED_SYM
%token FALSE_SYM
+%token FETCH_SYM
%token FILE_SYM
%token FIRST_SYM
%token FIXED_SYM
%token FLOAT_NUM
%token FORCE_SYM
%token FOREIGN
+%token FOUND_SYM
%token FROM
%token FULL
%token FULLTEXT_SYM
@@ -252,6 +267,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
@@ -267,6 +283,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
@@ -317,6 +334,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
@@ -356,6 +374,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SIMPLE_SYM
%token SHUTDOWN
%token SPATIAL_SYM
+%token SPECIFIC_SYM
+%token SQLEXCEPTION_SYM
+%token SQLWARNING_SYM
%token SSL_SYM
%token STARTING
%token STATUS_SYM
@@ -377,11 +398,13 @@ 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 UNDO_SYM
%token UNICODE_SYM
%token UNION_SYM
%token UNIQUE_SYM
@@ -528,6 +551,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%token SECOND_SYM
%token SECOND_MICROSECOND_SYM
%token SHARE_SYM
+%token SP_FUNC
%token SUBDATE_SYM
%token SUBSTRING
%token SUBSTRING_INDEX
@@ -561,6 +585,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
@@ -587,6 +623,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 opt_component
+ SP_FUNC ident_or_spfunc
%type <lex_str_ptr>
opt_table_alias
@@ -619,13 +656,14 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
%type <item>
literal text_literal insert_ident order_ident
simple_ident select_item2 expr opt_expr opt_else sum_expr in_sum_expr
- table_wild no_in_expr expr_expr simple_expr no_and_expr
+ table_wild no_in_expr expr_expr simple_expr no_and_expr udf_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
+ expr_list sp_expr_list udf_expr_list udf_expr_list2 when_list
+ ident_list ident_list_arg
%type <key_type>
key_type opt_unique_or_fulltext
@@ -692,7 +730,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize);
select_item_list select_item values_list no_braces
opt_limit_clause delete_limit_clause fields opt_values values
procedure_list procedure_list2 procedure_item
- when_list2 expr_list2 handler
+ when_list2 expr_list2 udf_expr_list3 handler
opt_precision opt_ignore opt_column opt_restrict
grant revoke set lock unlock string_list field_options field_option
field_opt_list opt_binary table_lock_list table_lock
@@ -711,8 +749,15 @@ 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_decl_idents sp_opt_inout sp_handler_type sp_hcond_list
+%type <spcondtype> sp_cond sp_hcond
+%type <spblock> sp_decls sp_decl
+%type <lex> sp_cursor_stmt
+
%type <NONE>
'-' '+' '*' '/' '%' '(' ')'
',' '!' '{' '}' '&' '|' AND OR OR_OR_CONCAT BETWEEN_SYM CASE_SYM
@@ -725,23 +770,29 @@ query:
{
THD *thd= YYTHD;
if (!thd->bootstrap &&
- (!(thd->lex.select_lex.options & OPTION_FOUND_COMMENT)))
+ (!(thd->lex->select_lex.options & OPTION_FOUND_COMMENT)))
{
send_error(thd,ER_EMPTY_QUERY);
YYABORT;
}
else
{
- thd->lex.sql_command = SQLCOM_EMPTY_QUERY;
+ thd->lex->sql_command = SQLCOM_EMPTY_QUERY;
}
}
| verb_clause END_OF_INPUT {};
verb_clause:
+ statement
+ | begin
+ ;
+
+/* Verb clauses, except begin */
+statement:
alter
| analyze
| backup
- | begin
+ | call
| change
| check
| checksum
@@ -953,20 +1004,827 @@ 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;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "PROCEDURE");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(&$3, lex, 0, 0);
+
+ sp->m_type= TYPE_ENUM_PROCEDURE;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->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;
+ lex->sphead->restore_thd_mem_root(YYTHD);
+ }
+ ;
+
+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;
+ sp_head *sp;
+
+ if (lex->sphead)
+ {
+ net_printf(YYTHD, ER_SP_NO_RECURSIVE_CREATE, "FUNCTION");
+ YYABORT;
+ }
+ /* Order is important here: new - reset - init */
+ sp= new sp_head();
+ sp->reset_thd_mem_root(YYTHD);
+ sp->init(&lex->udf.name, lex, 0, 0);
+
+ sp->m_type= TYPE_ENUM_FUNCTION;
+ lex->sphead= sp;
+ /*
+ * We have to turn of CLIENT_MULTI_QUERIES while parsing a
+ * stored procedure, otherwise yylex will chop it into pieces
+ * at each ';'.
+ */
+ sp->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;
+ lex->sphead->restore_thd_mem_root(YYTHD);
+ }
+ ;
+
+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_pvar(&$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_pvar(&$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 */
+ {
+ $$.vars= $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | sp_decls sp_decl ';'
+ {
+ $$.vars= $1.vars + $2.vars;
+ $$.conds= $1.conds + $2.conds;
+ $$.hndlrs= $1.hndlrs + $2.hndlrs;
+ $$.curs= $1.curs + $2.curs;
+ }
+ ;
+
+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);
+ }
+ }
+ $$.vars= $2;
+ $$.conds= $$.hndlrs= $$.curs= 0;
+ }
+ | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond
+ {
+ YYTHD->lex->spcont->push_cond(&$2, $5);
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
+ | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+ sp_instr_hpush_jump *i=
+ new sp_instr_hpush_jump(sp->instructions(), $2,
+ ctx->current_framesize());
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ ctx->add_handler();
+ }
+ sp_hcond_list sp_proc_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_label_t *hlab= lex->spcont->pop_label(); /* After this hdlr */
+
+ if ($2 == SP_HANDLER_CONTINUE)
+ sp->add_instr(new sp_instr_hreturn(sp->instructions(),
+ lex->spcont->current_framesize()));
+ else
+ { /* EXIT or UNDO handler, just jump to the end of the block */
+ sp_instr_jump *i= new sp_instr_jump(sp->instructions());
+
+ sp->add_instr(i);
+ sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */
+ }
+ lex->sphead->backpatch(hlab);
+ $$.vars= $$.conds= $$.curs= 0;
+ $$.hndlrs= $6;
+ }
+ | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_cpush *i= new sp_instr_cpush(sp->instructions(), $5);
+
+ sp->add_instr(i);
+ lex->spcont->push_cursor(&$2);
+ $$.vars= $$.conds= $$.hndlrs= 0;
+ $$.curs= 1;
+ }
+ ;
+
+sp_cursor_stmt:
+ {
+ Lex->sphead->reset_lex(YYTHD);
+
+ /* We use statement here just be able to get a better
+ error message. Using 'select' works too, but will then
+ result in a generic "syntax error" if a non-select
+ statement is given. */
+ }
+ statement
+ {
+ LEX *lex= Lex;
+
+ if (lex->sql_command != SQLCOM_SELECT)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_QUERY);
+ YYABORT;
+ }
+ if (lex->result)
+ {
+ send_error(YYTHD, ER_SP_BAD_CURSOR_SELECT);
+ YYABORT;
+ }
+ lex->sp_lex_in_use= TRUE;
+ $$= lex;
+ lex->sphead->restore_lex(YYTHD);
+ }
+ ;
+
+sp_handler_type:
+ EXIT_SYM { $$= SP_HANDLER_EXIT; }
+ | CONTINUE_SYM { $$= SP_HANDLER_CONTINUE; }
+/* | UNDO_SYM { QQ No yet } */
+ ;
+
+sp_hcond_list:
+ sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($1);
+ $$= 1;
+ }
+ | sp_hcond_list ',' sp_hcond
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_instr_hpush_jump *i= (sp_instr_hpush_jump *)sp->last_instruction();
+
+ i->add_condition($3);
+ $$= $1 + 1;
+ }
+ ;
+
+sp_cond:
+ ULONG_NUM
+ { /* mysql errno */
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::number;
+ $$->mysqlerr= $1;
+ }
+ | TEXT_STRING_literal
+ { /* SQLSTATE */
+ uint len= ($1.length < sizeof($$->sqlstate)-1 ?
+ $1.length : sizeof($$->sqlstate)-1);
+
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::state;
+ memcpy($$->sqlstate, $1.str, len);
+ $$->sqlstate[len]= '\0';
+ }
+ ;
+
+sp_hcond:
+ sp_cond
+ {
+ $$= $1;
+ }
+ | ident /* CONDITION name */
+ {
+ $$= Lex->spcont->find_cond(&$1);
+ if ($$ == NULL)
+ {
+ net_printf(YYTHD, ER_SP_COND_MISMATCH, $1.str);
+ YYABORT;
+ }
+ }
+ | SQLWARNING_SYM /* SQLSTATEs 01??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::warning;
+ }
+ | NOT FOUND_SYM /* SQLSTATEs 02??? */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::notfound;
+ }
+ | SQLEXCEPTION_SYM /* All other SQLSTATEs */
+ {
+ $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t));
+ $$->type= sp_cond_type_t::exception;
+ }
+ ;
+
+sp_decl_idents:
+ ident
+ {
+ Lex->spcont->push_pvar(&$1, (enum_field_types)0, sp_param_in);
+ $$= 1;
+ }
+ | sp_decl_idents ',' ident
+ {
+ Lex->spcont->push_pvar(&$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_results= 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->sp_lex_in_use= TRUE;
+ }
+ }
+ 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_freturn *i=
+ new sp_instr_freturn(lex->sphead->instructions(),
+ $2, lex->sphead->m_returns);
+
+ lex->sphead->add_instr(i);
+ lex->sphead->m_has_return= TRUE;
+ }
+ }
+ | 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_pvar(&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_pvar();
+ }
+ | 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);
+ }
+ }
+ | OPEN_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_copen *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_copen(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ | FETCH_SYM ident INTO
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cfetch *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cfetch(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ sp_fetch_list
+ { }
+ | CLOSE_SYM ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ uint offset;
+ sp_instr_cclose *i;
+
+ if (! lex->spcont->find_cursor(&$2, &offset))
+ {
+ net_printf(YYTHD, ER_SP_CURSOR_MISMATCH, $2.str);
+ YYABORT;
+ }
+ i= new sp_instr_cclose(sp->instructions(), offset);
+ sp->add_instr(i);
+ }
+ ;
+
+sp_fetch_list:
+ ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$1)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $1.str);
+ YYABORT;
+ }
+ else
+ { /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ |
+ sp_fetch_list ',' ident
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ if (!spc || !(spv = spc->find_pvar(&$3)))
+ {
+ net_printf(YYTHD, ER_SP_UNDECLARED_VAR, $3.str);
+ YYABORT;
+ }
+ else
+ { /* An SP local variable */
+ sp_instr_cfetch *i= (sp_instr_cfetch *)sp->last_instruction();
+
+ i->add_to_varlist(spv);
+ spv->isset= TRUE;
+ }
+ }
+ ;
+
+sp_if:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i = new sp_instr_jump_if_not(ip, $1);
+
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_elseifs
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_elseifs:
+ /* Empty */
+ | ELSEIF_SYM sp_if
+ | ELSE sp_proc_stmts
+ ;
+
+sp_case:
+ expr THEN_SYM
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump_if_not *i;
+
+ if (! sp->m_simple_case)
+ i= new sp_instr_jump_if_not(ip, $1);
+ else
+ { /* Simple case: <caseval> = <whenval> */
+ Item *var= (Item*) new Item_splocal(ctx->current_framesize()-1);
+ Item *expr= Item_bool_func2::eq_creator(var, $1);
+
+ i= new sp_instr_jump_if_not(ip, expr);
+ }
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ sp->add_instr(i);
+ }
+ sp_proc_stmts
+ {
+ sp_head *sp= Lex->sphead;
+ sp_pcontext *ctx= Lex->spcont;
+ uint ip= sp->instructions();
+ sp_instr_jump *i = new sp_instr_jump(ip);
+
+ sp->add_instr(i);
+ sp->backpatch(ctx->pop_label());
+ sp->push_backpatch(i, ctx->push_label((char *)"", 0));
+ }
+ sp_whens
+ {
+ LEX *lex= Lex;
+
+ lex->sphead->backpatch(lex->spcont->pop_label());
+ }
+ ;
+
+sp_whens:
+ /* Empty */ {}
+ | WHEN_SYM sp_case {}
+ | ELSE sp_proc_stmts {}
+ ;
+
+sp_labeled_control:
+ IDENT ':'
+ {
+ LEX *lex= Lex;
+ sp_label_t *lab= lex->spcont->find_label($1.str);
+
+ if (lab)
+ {
+ 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
+ { /* 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->push_label((char *)"", 0); /* For end of block */
+ }
+ sp_decls
+ sp_proc_stmts
+ END
+ {
+ LEX *lex= Lex;
+ sp_head *sp= lex->sphead;
+ sp_pcontext *ctx= lex->spcont;
+
+ sp->backpatch(ctx->pop_label());
+ ctx->pop_pvar($3.vars);
+ ctx->pop_cond($3.conds);
+ ctx->pop_cursor($3.curs);
+ if ($3.hndlrs)
+ sp->add_instr(new sp_instr_hpop(sp->instructions(),$3.hndlrs));
+ if ($3.curs)
+ sp->add_instr(new sp_instr_cpop(sp->instructions(), $3.curs));
+ }
+ | 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:
'(' create2a {}
@@ -1623,7 +2481,7 @@ alter:
ALTER opt_ignore TABLE_SYM table_ident
{
THD *thd= YYTHD;
- LEX *lex=&thd->lex;
+ LEX *lex= thd->lex;
lex->sql_command = SQLCOM_ALTER_TABLE;
lex->name=0;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
@@ -1652,8 +2510,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:
| DISCARD TABLESPACE { Lex->tablespace_op=DISCARD_TABLESPACE; }
@@ -2186,7 +3066,7 @@ select_item_list:
THD *thd= YYTHD;
if (add_item_to_list(thd, new Item_field(NULL, NULL, "*")))
YYABORT;
- (thd->lex.current_select->with_wild)++;
+ (thd->lex->current_select->with_wild)++;
};
@@ -2494,6 +3374,8 @@ simple_expr:
{ $$= new Item_date_add_interval($3, $5, INTERVAL_DAY, 0);}
| ADDDATE_SYM '(' expr ',' INTERVAL_SYM expr interval ')'
{ $$= new Item_date_add_interval($3, $6, $7, 0); }
+ | REPEAT_SYM '(' expr ',' expr ')'
+ { $$= new Item_func_repeat($3,$5); }
| ATAN '(' expr ')'
{ $$= new Item_func_atan($3); }
| ATAN '(' expr ',' expr ')'
@@ -2759,6 +3641,14 @@ simple_expr:
{ $$= new Item_func_round($3,$5,1); }
| TRUE_SYM
{ $$= new Item_int((char*) "TRUE",1,1); }
+ | SP_FUNC '(' sp_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)
@@ -2841,10 +3731,43 @@ simple_expr:
| EXTRACT_SYM '(' interval FROM expr ')'
{ $$=new Item_extract( $3, $5); };
-udf_expr_list:
+sp_expr_list:
/* empty */ { $$= NULL; }
| expr_list { $$= $1;};
+udf_expr_list:
+ /* empty */ { $$= NULL; }
+ | udf_expr_list2 { $$= $1;}
+ ;
+
+udf_expr_list2:
+ { Select->expr_list.push_front(new List<Item>); }
+ udf_expr_list3
+ { $$= Select->expr_list.pop(); }
+ ;
+
+udf_expr_list3:
+ udf_expr
+ {
+ Select->expr_list.head()->push_back($1);
+ }
+ | udf_expr_list3 ',' udf_expr
+ {
+ Select->expr_list.head()->push_back($3);
+ }
+ ;
+
+udf_expr:
+ remember_name expr remember_end select_alias
+ {
+ if ($4.str)
+ $2->set_name($4.str,$4.length,system_charset_info);
+ else
+ $2->set_name($1,(uint) ($3 - $1), YYTHD->charset());
+ $$= $2;
+ }
+ ;
+
sum_expr:
AVG_SYM '(' in_sum_expr ')'
{ $$=new Item_sum_avg($3); }
@@ -3448,11 +4371,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,(enum_field_types)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_SP_UNDECLARED_VAR);
YYABORT;
+ }
+ if (! lex->result)
+ YYABORT;
+ else
+ {
+ ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($1,1,t->offset,t->type));
+ t->isset= TRUE;
+ }
}
;
@@ -3533,11 +4478,19 @@ 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;
}
| DROP USER
{
@@ -3549,7 +4502,6 @@ drop:
{}
;
-
table_list:
table_name
| table_list ',' table_name;
@@ -3608,7 +4560,6 @@ replace:
}
insert_field_spec
{}
- {}
;
insert_lock_option:
@@ -3962,8 +4913,8 @@ show_param:
| opt_var_type VARIABLES wild
{
THD *thd= YYTHD;
- thd->lex.sql_command= SQLCOM_SHOW_VARIABLES;
- thd->lex.option_type= (enum_var_type) $1;
+ thd->lex->sql_command= SQLCOM_SHOW_VARIABLES;
+ thd->lex->option_type= (enum_var_type) $1;
}
| charset wild
{ Lex->sql_command= SQLCOM_SHOW_CHARSETS; }
@@ -4147,18 +5098,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
@@ -4344,16 +5300,33 @@ order_ident:
simple_ident:
ident
{
- SELECT_LEX *sel=Select;
- $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
- 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 *sel=Select;
+ $$= (sel->parsing_place != SELECT_LEX_NODE::IN_HAVING ||
+ sel->get_in_sum_expr() > 0) ?
+ (Item*) new Item_field(NullS,NullS,$1.str) :
+ (Item*) new Item_ref(NullS,NullS,$1.str);
+ }
}
| ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4369,7 +5342,7 @@ simple_ident:
| '.' ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4385,7 +5358,7 @@ simple_ident:
| ident '.' ident '.' ident
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
@@ -4678,7 +5651,7 @@ keyword:
| TIMESTAMP {}
| TIME_SYM {}
| TYPE_SYM {}
- | UDF_SYM {}
+ | FUNCTION_SYM {}
| UNCOMMITTED_SYM {}
| UNICODE_SYM {}
| UNTIL_SYM {}
@@ -4736,15 +5709,28 @@ 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)));
- }
+ '@' 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
{
LEX *lex=Lex;
- lex->var_list.push_back(new set_var(lex->option_type, $1.var,
- &$1.base_name, $3));
+
+ if ($1.var)
+ { /* System variable */
+ lex->var_list.push_back(new set_var(lex->option_type, $1.var,
+ &$1.base_name, $3));
+ }
+ else
+ { /* An SP local variable */
+ sp_pvar_t *spv= lex->spcont->find_pvar(&$1.base_name);
+ sp_instr_set *i= new sp_instr_set(lex->sphead->instructions(),
+ spv->offset, $3, spv->type);
+
+ lex->sphead->add_instr(i);
+ spv->isset= TRUE;
+ }
}
| '@' '@' opt_var_ident_type internal_variable_name equal set_expr_or_default
{
@@ -4791,7 +5777,7 @@ option_value:
YYABORT;
user->host.str=0;
user->user.str=thd->priv_user;
- thd->lex.var_list.push_back(new set_var_password(user, $3));
+ thd->lex->var_list.push_back(new set_var_password(user, $3));
}
| PASSWORD FOR_SYM user equal text_or_password
{
@@ -4802,12 +5788,25 @@ option_value:
internal_variable_name:
ident
{
- sys_var *tmp=find_sys_var($1.str, $1.length);
- if (!tmp)
- YYABORT;
- $$.var= tmp;
- $$.base_name.str=0;
- $$.base_name.length=0;
+ LEX *lex= Lex;
+ sp_pcontext *spc= lex->spcont;
+ sp_pvar_t *spv;
+
+ /* We have to lookup here since local vars can shadow sysvars */
+ 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;
+ $$.var= tmp;
+ $$.base_name.str=0;
+ $$.base_name.length=0;
+ }
+ else
+ { /* An SP local variable */
+ $$.var= NULL;
+ $$.base_name= $1;
+ }
}
| ident '.' ident
{
@@ -5339,7 +6338,7 @@ optional_order_or_limit:
|
{
THD *thd= YYTHD;
- LEX *lex= &thd->lex;
+ LEX *lex= thd->lex;
DBUG_ASSERT(lex->current_select->linkage != GLOBAL_OPTIONS_TYPE);
SELECT_LEX *sel= lex->current_select;
SELECT_LEX_UNIT *unit= sel->master_unit();
@@ -5355,7 +6354,7 @@ optional_order_or_limit:
order_or_limit
{
THD *thd= YYTHD;
- thd->lex.current_select->no_table_names_allowed= 0;
+ thd->lex->current_select->no_table_names_allowed= 0;
thd->where= "";
}
;
diff --git a/sql/table.h b/sql/table.h
index 7b4e5745732..afa6665c8cd 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -26,6 +26,7 @@ class st_select_lex_unit;
typedef struct st_order {
struct st_order *next;
Item **item; /* Point at item in select fields */
+ Item **item_copy; /* For SPs; the original item ptr */
bool asc; /* true if ascending */
bool free_me; /* true if item isn't shared */
bool in_field_list; /* true if in select field list */
diff --git a/sql/udf_example.cc b/sql/udf_example.cc
index ba056a9d2fd..6748be5a06f 100644
--- a/sql/udf_example.cc
+++ b/sql/udf_example.cc
@@ -56,7 +56,9 @@
**
** Function 'myfunc_int' returns summary length of all its arguments.
**
-** Function 'sequence' returns an sequence starting from a certain number
+** Function 'sequence' returns an sequence starting from a certain number.
+**
+** Function 'myfunc_argument_name' returns name of argument.
**
** On the end is a couple of functions that converts hostnames to ip and
** vice versa.
@@ -82,6 +84,7 @@
** CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
** CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
** CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+** CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
**
** After this the functions will work exactly like native MySQL functions.
** Functions should be created only once.
@@ -94,6 +97,7 @@
** DROP FUNCTION lookup;
** DROP FUNCTION reverse_lookup;
** DROP FUNCTION avgcost;
+** DROP FUNCTION myfunc_argument_name;
**
** The CREATE FUNCTION and DROP FUNCTION update the func@mysql table. All
** Active function will be reloaded on every restart of server
@@ -984,4 +988,43 @@ avgcost( UDF_INIT* initid, UDF_ARGS* args, char* is_null, char* error )
return data->totalprice/double(data->totalquantity);
}
+extern "C" {
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message);
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error);
+}
+
+my_bool myfunc_argument_name_init(UDF_INIT *initid, UDF_ARGS *args,
+ char *message)
+{
+ if (args->arg_count != 1)
+ {
+ strmov(message,"myfunc_argument_name_init accepts only one argument");
+ return 1;
+ }
+ initid->max_length= args->attribute_lengths[0];
+ initid->maybe_null= 1;
+ initid->const_item= 1;
+ return 0;
+}
+
+char *myfunc_argument_name(UDF_INIT *initid, UDF_ARGS *args, char *result,
+ unsigned long *length, char *null_value,
+ char *error)
+{
+ if (!args->attributes[0])
+ {
+ null_value= 0;
+ return 0;
+ }
+ (*length)--; // space for ending \0 (for debugging purposes)
+ if (*length > args->attribute_lengths[0])
+ *length= args->attribute_lengths[0];
+ memcpy(result, args->attributes[0], *length);
+ result[*length]= 0;
+ return result;
+}
+
#endif /* HAVE_DLOPEN */
diff --git a/tests/udf_test b/tests/udf_test
index 4621a7b34a5..15ad640f984 100644
--- a/tests/udf_test
+++ b/tests/udf_test
@@ -9,6 +9,7 @@ CREATE FUNCTION myfunc_int RETURNS INTEGER SONAME "udf_example.so";
CREATE FUNCTION lookup RETURNS STRING SONAME "udf_example.so";
CREATE FUNCTION reverse_lookup RETURNS STRING SONAME "udf_example.so";
CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so";
+CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so";
select metaphon("hello");
select myfunc_double("hello","world");
@@ -20,6 +21,7 @@ create temporary table t1 (a int,b double);
insert into t1 values (1,5),(1,4),(2,8),(3,9),(4,11);
select avgcost(a,b) from t1;
select avgcost(a,b) from t1 group by a;
+select a, myfunc_argument_name(a), myfunc_argument_name(a as b) from t1;
drop table t1;
DROP FUNCTION metaphon;
@@ -28,3 +30,4 @@ DROP FUNCTION myfunc_int;
DROP FUNCTION lookup;
DROP FUNCTION reverse_lookup;
DROP FUNCTION avgcost;
+DROP FUNCTION myfunc_argument_name;
diff --git a/tests/udf_test.res b/tests/udf_test.res
index 66634e13616..de9e9969f3a 100644
--- a/tests/udf_test.res
+++ b/tests/udf_test.res
@@ -35,6 +35,12 @@ CREATE AGGREGATE FUNCTION avgcost RETURNS REAL SONAME "udf_example.so"
Query OK, 0 rows affected
--------------
+CREATE FUNCTION myfunc_argument_name RETURNS STRING SONAME "udf_example.so"
+--------------
+
+Query OK, 0 rows affected
+
+--------------
select metaphon("hello")
--------------
@@ -107,6 +113,18 @@ avgcost(a,b)
4 rows in set
--------------
+select a, myfunc_argument_name(a) from t1;
+--------------
+
+a myfunc_argument_name(a) myfunc_argument_name(a as b)
+1 a b
+1 a b
+2 a b
+3 a b
+4 a b
+5 rows in set
+
+--------------
drop table t1
--------------
@@ -148,4 +166,10 @@ DROP FUNCTION avgcost
Query OK, 0 rows affected
+--------------
+DROP FUNCTION myfunc_argument_name;
+--------------
+
+Query OK, 0 rows affected
+
Bye