diff options
author | unknown <pem@mysql.com> | 2003-11-19 15:19:46 +0100 |
---|---|---|
committer | unknown <pem@mysql.com> | 2003-11-19 15:19:46 +0100 |
commit | 214ad8280394bac10ddaf6a4a837617214b09d7e (patch) | |
tree | e3ba11d4938f6675c5c72ee69cc88980a81ad705 | |
parent | b52b44f8970a93b45ebbd3b148cfefb1ec83bff7 (diff) | |
parent | bd2263503d82958ced3fbf700ab4a7f9265e9dc5 (diff) | |
download | mariadb-git-214ad8280394bac10ddaf6a4a837617214b09d7e.tar.gz |
Merging 4.1->5.0.
BitKeeper/etc/ignore:
auto-union
BitKeeper/etc/logging_ok:
auto-union
BitKeeper/triggers/post-commit:
Auto merged
client/mysql.cc:
Auto merged
configure.in:
Auto merged
include/my_global.h:
Auto merged
include/my_pthread.h:
Auto merged
include/mysql_com.h:
Auto merged
libmysql/libmysql.c:
Auto merged
libmysqld/Makefile.am:
Auto merged
libmysqld/lib_sql.cc:
Auto merged
myisam/mi_check.c:
Auto merged
myisam/myisamchk.c:
Auto merged
myisam/myisamdef.h:
Auto merged
mysql-test/r/rpl_temporary.result:
Auto merged
mysql-test/r/show_check.result:
Auto merged
mysql-test/r/subselect.result:
Auto merged
mysql-test/r/variables.result:
Auto merged
mysql-test/t/subselect.test:
Auto merged
mysql-test/t/variables.test:
Auto merged
scripts/mysql_install_db.sh:
Auto merged
sql/Makefile.am:
Auto merged
sql/ha_berkeley.cc:
Auto merged
sql/ha_innodb.cc:
Auto merged
sql/ha_myisam.cc:
Auto merged
sql/handler.cc:
Auto merged
sql/handler.h:
Auto merged
sql/item_subselect.cc:
Auto merged
sql/item_sum.cc:
Auto merged
sql/item_sum.h:
Auto merged
sql/mysql_priv.h:
Auto merged
sql/mysqld.cc:
Auto merged
sql/slave.cc:
Auto merged
sql/sql_acl.cc:
Auto merged
sql/sql_base.cc:
Auto merged
sql/sql_cache.cc:
Auto merged
sql/sql_db.cc:
Auto merged
sql/sql_delete.cc:
Auto merged
sql/sql_lex.cc:
Auto merged
sql/sql_prepare.cc:
Auto merged
sql/sql_repl.cc:
Auto merged
sql/sql_show.cc:
Auto merged
sql/sql_table.cc:
Auto merged
sql/sql_union.cc:
Auto merged
sql/sql_update.cc:
Auto merged
sql/table.h:
Auto merged
136 files changed, 12038 insertions, 1189 deletions
diff --git a/.bzrignore b/.bzrignore index e9de6662cb2..367f05c6465 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 86fcc8687d7..6ed0abe98fb 100644 --- a/BitKeeper/etc/logging_ok +++ b/BitKeeper/etc/logging_ok @@ -5,6 +5,7 @@ Administrator@fred. Miguel@light.local Sinisa@sinisa.nasamreza.org WAX@sergbook.mysql.com +acurtis@pcgem.rdg.cyberkinetica.com administrador@light.hegel.local ahlentz@co3064164-a.rochd1.qld.optusnet.com.au akishkin@work.mysql.com @@ -96,10 +97,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@deer.(none) ram@gw.mysql.r18.ru ram@gw.udmsearch.izhnet.ru diff --git a/BitKeeper/triggers/post-commit b/BitKeeper/triggers/post-commit index b0d70c85661..873c9665faf 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':P:::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 af807d9fb60..9129b7dfaa3 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 4b930b45f04..4d1d44a931d 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 43cacf8fa65..94263892ebd 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 3031b883e98..cc773160b3d 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; @@ -291,6 +293,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 a6e23fbff3a..372ddc0787b 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -300,4 +300,36 @@ #define ER_WARN_QC_RESIZE 1281 #define ER_BAD_FT_COLUMN 1282 #define ER_UNKNOWN_KEY_CACHE 1283 -#define ER_ERROR_MESSAGES 284 +#define ER_SP_NO_RECURSIVE_CREATE 1284 +#define ER_SP_ALREADY_EXISTS 1285 +#define ER_SP_DOES_NOT_EXIST 1286 +#define ER_SP_DROP_FAILED 1287 +#define ER_SP_STORE_FAILED 1288 +#define ER_SP_LILABEL_MISMATCH 1289 +#define ER_SP_LABEL_REDEFINE 1290 +#define ER_SP_LABEL_MISMATCH 1291 +#define ER_SP_UNINIT_VAR 1292 +#define ER_SP_BADSELECT 1293 +#define ER_SP_BADRETURN 1294 +#define ER_SP_BADQUERY 1295 +#define ER_UPDATE_LOG_DEPRECATED_IGNORED 1296 +#define ER_UPDATE_LOG_DEPRECATED_TRANSLATED 1297 +#define ER_QUERY_INTERRUPTED 1298 +#define ER_SP_WRONG_NO_OF_ARGS 1299 +#define ER_SP_COND_MISMATCH 1300 +#define ER_SP_NORETURN 1301 +#define ER_SP_NORETURNEND 1302 +#define ER_SP_BAD_CURSOR_QUERY 1303 +#define ER_SP_BAD_CURSOR_SELECT 1304 +#define ER_SP_CURSOR_MISMATCH 1305 +#define ER_SP_CURSOR_ALREADY_OPEN 1306 +#define ER_SP_CURSOR_NOT_OPEN 1307 +#define ER_SP_UNDECLARED_VAR 1308 +#define ER_SP_WRONG_NO_OF_FETCH_ARGS 1309 +#define ER_SP_FETCH_NO_DATA 1310 +#define ER_SP_DUP_PARAM 1311 +#define ER_SP_DUP_VAR 1312 +#define ER_SP_DUP_COND 1313 +#define ER_SP_DUP_CURS 1314 +#define ER_SP_CANT_ALTER 1315 +#define ER_ERROR_MESSAGES 316 diff --git a/libmysql/libmysql.c b/libmysql/libmysql.c index 9c388f71314..caa47ba7aad 100644 --- a/libmysql/libmysql.c +++ b/libmysql/libmysql.c @@ -3167,6 +3167,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 95cbd4ec826..83fab7e2cdb 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -57,7 +57,8 @@ sqlsources = derror.cc field.cc field_conv.cc strfunc.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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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 cfdb696b60b..4f8fc9b981a 100644 --- a/myisam/mi_check.c +++ b/myisam/mi_check.c @@ -2633,7 +2633,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 605baa14582..5193a817a3a 100644 --- a/myisam/myisamchk.c +++ b/myisam/myisamchk.c @@ -1667,9 +1667,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 4f1bed5d50a..20574e43ac3 100644 --- a/myisam/myisamdef.h +++ b/myisam/myisamdef.h @@ -703,7 +703,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 e668b9031cc..b04a41235e9 100644 --- a/mysql-test/r/query_cache.result +++ b/mysql-test/r/query_cache.result @@ -809,4 +809,29 @@ 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 48 +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 49 +show status like "Qcache_hits"; +Variable_name Value +Qcache_hits 13 +drop table t1; + SET GLOBAL query_cache_size=0; diff --git a/mysql-test/r/show_check.result b/mysql-test/r/show_check.result index 2f29dcfdb47..7cc242457db 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..adfecc151b7 --- /dev/null +++ b/mysql-test/r/sp-error.result @@ -0,0 +1,258 @@ +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 1282 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: begin +iterate foo; +end; +ERROR HY000: ITERATE with no matching label: foo +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; +alter procedure bar3 sql security invoker; +ERROR HY000: PROCEDURE bar3 does not exist +alter procedure bar3 name +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA; +ERROR 42000: Identifier name 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA' is too long +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; +create procedure p(in x int, x char(10)) +begin +end; +ERROR HY000: Duplicate parameter: x +create function p(x int, x char(10)) +begin +end; +ERROR HY000: Duplicate parameter: x +create procedure p() +begin +declare x float; +declare x int; +end; +ERROR HY000: Duplicate variable: x +create procedure p() +begin +declare c condition for 1064; +declare c condition for 1065; +end; +ERROR HY000: Duplicate condition: c +create procedure p() +begin +declare c cursor for select * from t1; +declare c cursor for select field from t1; +end; +ERROR HY000: Duplicate cursor: c +drop table t1; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result new file mode 100644 index 00000000000..56f6bc59087 --- /dev/null +++ b/mysql-test/r/sp.result @@ -0,0 +1,930 @@ +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 empty() +begin +end; +call empty(); +drop procedure empty; +create procedure scope(a int, b float) +begin +declare b int; +declare c float; +begin +declare c int; +end; +end; +drop procedure scope; +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; +drop table if exists t3; +create table t3 ( d date, i int, f double, s varchar(32) ); +create procedure nullset() +begin +declare ld date; +declare li int; +declare lf double; +declare ls varchar(32); +set ld = null, li = null, lf = null, ls = null; +insert into t3 values (ld, li, lf, ls); +end; +call nullset(); +select * from t3; +d i f s +NULL NULL NULL NULL +drop table t3; +drop procedure nullset; +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; +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 i(x int) +foo: +begin +if x = 0 then +leave foo; +end if; +insert into test.t1 values ("i", x); +end foo; +call i(0); +call i(3); +select * from t1; +id data +i 3 +delete from t1; +drop procedure i; +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_test3() +begin +declare x char(16); +declare y int; +select * into x,y from test.t1 limit 1; +insert into test.t2 values (x, y, 0.0); +end; +insert into t1 values ("into3", 19); +delete from t2; +call into_test3(); +call into_test3(); +select * from t2; +s i d +into3 19 0 +into3 19 0 +delete from t1; +delete from t2; +drop procedure into_test3; +create procedure into_test4() +begin +declare x int; +select data into x from test.t1 limit 1; +insert into test.t3 values ("into4", x); +end; +delete from t1; +drop table if exists t3; +create table t3 ( s char(16), d int); +call into_test4(); +Warnings: +select * from t3; +s d +into4 NULL +insert into t1 values ("i4", 77); +call into_test4(); +select * from t3; +s d +into4 NULL +into4 77 +delete from t1; +drop table t3; +drop procedure into_test4; +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 +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 bar condition for sqlstate '42S98'; # Just for testing syntax +declare zip condition for sqlstate value '42S99'; # Just for testing syntax +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 sqlstate '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 1306 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 1306 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 1306 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 t70; +create table t70 (s1 int,s2 int); +insert into t70 values (1,2); +create procedure bug1656(out p1 int, out p2 int) +select * into p1, p1 from t70; +call bug1656(@1, @2); +select @1, @2; +@1 @2 +2 NULL +drop table t70; +drop procedure bug1656; +drop table if exists t3; +create table t3(a int); +create procedure bug1862() +begin +insert into t3 values(2); +flush tables; +end; +call bug1862(); +call bug1862(); +select * from t3; +a +2 +2 +drop table t3; +drop procedure bug1862; +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; +show function status like '%f%'; +Name Type Creator Modified Created Suid Comment +fac function root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y +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; +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; +show create procedure opp; +Procedure Create Procedure +opp 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; +end +show procedure status like '%p%'; +Name Type Creator Modified Created Suid Comment +ip procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y +opp procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y +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; +create procedure bar(x char(16), y int) +comment "111111111111" sql security invoker +insert into test.t1 values (x, y); +show procedure status like 'bar'; +Name Type Creator Modified Created Suid Comment +bar procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 N 111111111111 +alter procedure bar name bar2 comment "2222222222" sql security definer; +alter procedure bar2 name bar comment "3333333333"; +alter procedure bar; +show create procedure bar; +Procedure Create Procedure +bar create procedure bar(x char(16), y int) +comment "111111111111" sql security invoker +insert into test.t1 values (x, y) +show procedure status like 'bar'; +Name Type Creator Modified Created Suid Comment +bar procedure root@localhost 0000-00-00 00:00:00 0000-00-00 00:00:00 Y 3333333333 +drop procedure bar; +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 95638cbee41..177baa84e0f 100644 --- a/mysql-test/r/subselect.result +++ b/mysql-test/r/subselect.result @@ -647,6 +647,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 90654bece2e..82701dc338c 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 @@ -337,6 +359,8 @@ set sql_buffer_result=1; set sql_log_bin=1; set sql_log_off=1; set sql_log_update=1; +Warnings: +Note 1292 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 14cbf4c906d..148433dcacb 100644 --- a/mysql-test/t/query_cache.test +++ b/mysql-test/t/query_cache.test @@ -581,6 +581,21 @@ set character_set_results=cp1251; SELECT a,'Â','â'='Â' FROM t1; show status like "Qcache_hits"; show status like "Qcache_queries_in_cache"; +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"; + # # Keep things tidy # diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test new file mode 100644 index 00000000000..87f765cb7c8 --- /dev/null +++ b/mysql-test/t/sp-error.test @@ -0,0 +1,344 @@ +# +# 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 1280 +create procedure foo() + create procedure bar() set @x=3| +--error 1280 +create procedure foo() + create function bar() returns double return 2.3| + +# Already exists +--error 1281 +create procedure proc1() + set @x = 42| +--error 1281 +create function func1() returns int + return 42| + +drop procedure proc1| +drop function func1| + +# Does not exist +--error 1282 +alter procedure foo| +--error 1282 +alter function foo| +--error 1282 +drop procedure foo| +--error 1282 +drop function foo| +--error 1282 +call foo()| +drop procedure if exists foo| + +# LEAVE/ITERATE with no match +--error 1285 +create procedure foo() +foo: loop + leave bar; +end loop| +--error 1285 +create procedure foo() +foo: loop + iterate bar; +end loop| +--error 1285 +create procedure foo() +foo: begin + iterate foo; +end| + +# Redefining label +--error 1286 +create procedure foo() +foo: loop + foo: loop + set @x=2; + end loop foo; +end loop foo| + +# End label mismatch +--error 1287 +create procedure foo() +foo: loop + set @x=2; +end loop bar| + +# Referring to undef variable +--error 1288 +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 1289 +call foo()| +drop procedure foo| + +# RETURN in FUNCTION only +--error 1290 +create procedure foo() + return 42| + +# Doesn't allow queries in FUNCTIONs (for now :-( ) +--error 1291 +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 1295 +call p()| +--error 1295 +call p(1, 2)| +--error 1295 +select f()| +--error 1295 +select f(1, 2)| + +drop procedure p| +drop function f| + +--error 1296 +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 1296 +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 1297 +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 1298 +select f(10)| + +drop function f| + +--error 1299 +create procedure p() +begin + declare c cursor for insert into test.t1 values ("foo", 42); + + open c; + close c; +end| + +--error 1300 +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 1301 +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 1302 +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 1303 +call p()| +drop procedure p| + +--error 1282 +alter procedure bar3 sql security invoker| +--error 1059 +alter procedure bar3 name +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA| + +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 1304 +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 1305 +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 1305 +call p()| +drop procedure p| + +--error 1307 +create procedure p(in x int, x char(10)) +begin +end| +--error 1307 +create function p(x int, x char(10)) +begin +end| + +--error 1308 +create procedure p() +begin + declare x float; + declare x int; +end| + +--error 1309 +create procedure p() +begin + declare c condition for 1064; + declare c condition for 1065; +end| + +--error 1310 +create procedure p() +begin + declare c cursor for select * from t1; + declare c cursor for select field from t1; +end| + +drop table t1| + +delimiter ;| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test new file mode 100644 index 00000000000..091a3e364ce --- /dev/null +++ b/mysql-test/t/sp.test @@ -0,0 +1,1054 @@ +# +# 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 |; + +# Empty statement +create procedure empty() +begin +end| + +call empty()| +drop procedure empty| + +# Scope test. This is legal (warnings might be possible in the future, +# but for the time being, we just accept it). +create procedure scope(a int, b float) +begin + declare b int; + declare c float; + + begin + declare c int; + end; +end| + +drop procedure scope| + +# 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| + + +# Set things to null +--disable_warnings +drop table if exists t3| +--enable_warnings +create table t3 ( d date, i int, f double, s varchar(32) )| + +create procedure nullset() +begin + declare ld date; + declare li int; + declare lf double; + declare ls varchar(32); + + set ld = null, li = null, lf = null, ls = null; + insert into t3 values (ld, li, lf, ls); +end| + +call nullset()| +select * from t3| +drop table t3| +drop procedure nullset| + + +# 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| + +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| + + +# It's actually possible to LEAVE a BEGIN-END block +create procedure i(x int) +foo: +begin + if x = 0 then + leave foo; + end if; + insert into test.t1 values ("i", x); +end foo| + +call i(0)| +call i(3)| +select * from t1| +delete from t1| +drop procedure i| + + +# 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| + + +# SELECT * INTO ... (bug test) +create procedure into_test3() +begin + declare x char(16); + declare y int; + + select * into x,y from test.t1 limit 1; + insert into test.t2 values (x, y, 0.0); +end| + +insert into t1 values ("into3", 19)| +delete from t2| +# Two call needed for bug test +call into_test3()| +call into_test3()| +select * from t2| +delete from t1| +delete from t2| +drop procedure into_test3| + + +# SELECT INTO with no data is a warning ("no data", which we will +# not see normally). When not caught, execution proceeds. +create procedure into_test4() +begin + declare x int; + + select data into x from test.t1 limit 1; + insert into test.t3 values ("into4", x); +end| + +delete from t1| +--disable_warnings +drop table if exists t3| +--enable_warnings +create table t3 ( s char(16), d int)| +call into_test4()| +select * from t3| +insert into t1 values ("i4", 77)| +call into_test4()| +select * from t3| +delete from t1| +drop table t3| +drop procedure into_test4| + + +# 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 bar condition for sqlstate '42S98'; # Just for testing syntax + declare zip condition for sqlstate value '42S99'; # Just for testing syntax + 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 sqlstate '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 1306 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 1306 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 1306 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| + +# +# BUG#1656 +# +--disable_warnings +drop table if exists t70| +--enable_warnings +create table t70 (s1 int,s2 int)| +insert into t70 values (1,2)| + +create procedure bug1656(out p1 int, out p2 int) + select * into p1, p1 from t70| + +call bug1656(@1, @2)| +select @1, @2| +drop table t70| +drop procedure bug1656| + + +# +# BUG#1862 +# +--disable_warnings +drop table if exists t3| +--enable_warnings +create table t3(a int)| + +create procedure bug1862() +begin + insert into t3 values(2); + flush tables; +end| + +call bug1862()| +# the second call caused a segmentation +call bug1862()| +select * from t3| +drop table t3| +drop procedure bug1862| + + +# +# 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| +--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +show function status like '%f%'| +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; +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| +show create procedure opp| +--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +show procedure status like '%p%'| + +# 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| + +# Comment & suid +create procedure bar(x char(16), y int) + comment "111111111111" sql security invoker + insert into test.t1 values (x, y)| +--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +show procedure status like 'bar'| +alter procedure bar name bar2 comment "2222222222" sql security definer| +alter procedure bar2 name bar comment "3333333333"| +alter procedure bar| +show create procedure bar| +--replace_column 4 '0000-00-00 00:00:00' 5 '0000-00-00 00:00:00' +show procedure status like 'bar'| +drop procedure bar| + +delimiter ;| +drop table t1; +drop table t2; diff --git a/mysql-test/t/subselect.test b/mysql-test/t/subselect.test index 75ca521d24e..3126c5d55af 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 6365ad77c57..289f04153c8 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(¶m, 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 e2ab55da6a2..170cd170b96 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= ¤t_thd->killed; + volatile THD::killed_state *killed= ¤t_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= ¤t_thd->killed; - bool not_killable; + volatile THD::killed_state *killed= ¤t_thd->killed; + THD::killed_state not_killable; DBUG_ENTER("merge_buffers"); statistic_increment(filesort_merge_passes, &LOCK_status); if (param->not_killable) { killed= ¬_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 e87e31708f3..c4735403267 100644 --- a/sql/ha_berkeley.cc +++ b/sql/ha_berkeley.cc @@ -2113,7 +2113,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 0f22a83a2b4..09f034b27bf 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,...) @@ -391,7 +391,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; @@ -431,7 +431,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/handler.cc b/sql/handler.cc index 56db355f45e..96444f97da8 100644 --- a/sql/handler.cc +++ b/sql/handler.cc @@ -70,6 +70,37 @@ const char *tx_isolation_names[] = TYPELIB tx_isolation_typelib= {array_elements(tx_isolation_names)-1,"", tx_isolation_names}; +enum db_type ha_resolve_by_name(char *name, uint namelen) +{ + enum db_type result = DB_TYPE_UNKNOWN; + if (!my_strcasecmp(&my_charset_latin1, name, "HEAP") || + !my_strcasecmp(&my_charset_latin1, name, "MEMORY")) { + result = DB_TYPE_HEAP; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "MRG_MYISAM") || + !my_strcasecmp(&my_charset_latin1, name, "MERGE")) { + result = DB_TYPE_MRG_MYISAM; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "MYISAM")) { + result = DB_TYPE_MYISAM; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "BDB") || + !my_strcasecmp(&my_charset_latin1, name, "BERKELEYDB")) { + result = DB_TYPE_BERKELEY_DB; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "INNODB") || + !my_strcasecmp(&my_charset_latin1, name, "INNOBASE")) { + result = DB_TYPE_INNODB; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "ISAM")) { + result = DB_TYPE_ISAM; + } else + if (!my_strcasecmp(&my_charset_latin1, name, "DEFAULT")) { + result = (enum db_type) current_thd->variables.table_type; + } + return result; +} + /* Use other database handler if databasehandler is not incompiled */ enum db_type ha_checktype(enum db_type database_type) @@ -77,18 +108,21 @@ enum db_type ha_checktype(enum db_type database_type) switch (database_type) { #ifdef HAVE_BERKELEY_DB case DB_TYPE_BERKELEY_DB: - return(berkeley_skip ? DB_TYPE_MYISAM : database_type); + if (berkeley_skip) break; + return (database_type); #endif #ifdef HAVE_INNOBASE_DB case DB_TYPE_INNODB: - return(innodb_skip ? DB_TYPE_MYISAM : database_type); + if (innodb_skip) break; + return (database_type); #endif #ifndef NO_HASH case DB_TYPE_HASH: #endif #ifdef HAVE_ISAM case DB_TYPE_ISAM: - return (isam_skip ? DB_TYPE_MYISAM : database_type); + if (isam_skip) break; + return (database_type); case DB_TYPE_MRG_ISAM: return (isam_skip ? DB_TYPE_MRG_MYISAM : database_type); #else @@ -102,7 +136,12 @@ enum db_type ha_checktype(enum db_type database_type) default: break; } - return(DB_TYPE_MYISAM); /* Use this as default */ + /* Use this as default */ +#if 0 + return((enum db_type) current_thd->variables.table_type); +#else + return(DB_TYPE_MYISAM); +#endif } /* ha_checktype */ diff --git a/sql/handler.h b/sql/handler.h index 40773c67605..f4ae45fafe3 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -383,6 +383,7 @@ extern TYPELIB ha_table_typelib, tx_isolation_typelib; #define ha_supports_generate(T) (T != DB_TYPE_INNODB) +enum db_type ha_resolve_by_name(char *name, uint namelen); handler *get_new_handler(TABLE *table, enum db_type db_type); my_off_t ha_get_ptr(byte *ptr, uint pack_length); void ha_store_ptr(byte *buff, uint pack_length, my_off_t pos); diff --git a/sql/item.cc b/sql/item.cc index d76ab529db5..0c5b6450b4a 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; @@ -56,13 +57,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++; } } @@ -136,6 +137,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)) @@ -146,12 +148,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))); } @@ -221,6 +223,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)) @@ -790,7 +809,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 & DESCRIBE_EXTENDED) + if (thd->lex->describe & DESCRIBE_EXTENDED) { char warn_buff[MYSQL_ERRMSG_SIZE]; sprintf(warn_buff, ER(ER_WARN_FIELD_RESOLVED), @@ -831,7 +850,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) { SELECT_LEX_UNIT *prev_unit= cursel->master_unit(); @@ -1407,7 +1426,7 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) { TABLE_LIST *where= 0, *table_list; bool upward_lookup= 0; - SELECT_LEX_UNIT *prev_unit= thd->lex.current_select->master_unit(); + SELECT_LEX_UNIT *prev_unit= thd->lex->current_select->master_unit(); SELECT_LEX *sl= prev_unit->outer_select(); /* Finding only in current select will be performed for selects that have @@ -1415,10 +1434,10 @@ bool Item_ref::fix_fields(THD *thd,TABLE_LIST *tables, Item **reference) 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 : @@ -1494,7 +1513,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); } @@ -1507,7 +1526,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 @@ -1518,7 +1537,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; } @@ -1533,7 +1552,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; } } @@ -1546,8 +1565,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 8b35705f191..0a0db619e1a 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 */ @@ -203,6 +204,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; } @@ -220,6 +224,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 8731e8fbe21..1629ed6aa80 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -1696,7 +1696,7 @@ Item_cond::fix_fields(THD *thd, TABLE_LIST *tables, Item **ref) if (item->maybe_null) maybe_null=1; } - 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 1c0588b06a8..1ddb1fcb080 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(UNCACHEABLE_SIDEEFFECT); + current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); 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(UNCACHEABLE_SIDEEFFECT); + current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); 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(UNCACHEABLE_SIDEEFFECT); + current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); return new Item_load_file(a); } @@ -472,13 +472,13 @@ Item *create_func_cast(Item *a, Cast_target cast_type, int len, Item *create_func_is_free_lock(Item* a) { - current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT); + current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); return new Item_func_is_free_lock(a); } Item *create_func_is_used_lock(Item* a) { - current_thd->lex.uncacheable(UNCACHEABLE_SIDEEFFECT); + current_thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); return new Item_func_is_used_lock(a); } diff --git a/sql/item_func.cc b/sql/item_func.cc index c42019a9fbb..7e03f8c9c45 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) @@ -1492,11 +1495,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); @@ -1515,8 +1523,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 ! @@ -2486,8 +2496,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) @@ -2950,7 +2959,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(UNCACHEABLE_SIDEEFFECT); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); buff[0]='@'; buff[1]='@'; pos=buff+2; @@ -2990,7 +2999,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(UNCACHEABLE_SIDEEFFECT); + thd->lex->uncacheable(UNCACHEABLE_SIDEEFFECT); item->set_name(item_name, 0, system_charset_info); // Will use original name return item; } @@ -3051,3 +3060,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 6b43ebaccbe..d3cfaf63b5a 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -47,7 +47,8 @@ public: SP_CONTAINS_FUNC,SP_OVERLAPS_FUNC, SP_STARTPOINT,SP_ENDPOINT,SP_EXTERIORRING, SP_POINTN,SP_GEOMETRYN,SP_INTERIORRINGN, - NOT_FUNC, NOT_ALL_FUNC}; + NOT_FUNC, NOT_ALL_FUNC, + 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; } @@ -946,6 +947,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); @@ -1058,3 +1061,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 a60a35aac6b..3ed137d0731 100644 --- a/sql/item_subselect.cc +++ b/sql/item_subselect.cc @@ -253,7 +253,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); @@ -609,14 +609,14 @@ Item_in_subselect::single_value_transformer(JOIN *join, subs= new Item_maxmin_subselect(this, select_lex, func->l_op()); } // left expression belong to outer select - 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(); if (left_expr->fix_fields(thd, up->get_table_list(), &left_expr)) { - thd->lex.current_select= current; + thd->lex->current_select= current; DBUG_RETURN(RES_ERROR); } - thd->lex.current_select= current; + thd->lex->current_select= current; substitution= func->create(left_expr, subs); DBUG_RETURN(RES_OK); } @@ -627,16 +627,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 @@ -725,7 +725,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); @@ -761,15 +761,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->uncacheable|= UNCACHEABLE_DEPENDENT; } @@ -912,8 +912,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, @@ -926,7 +926,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; } @@ -1032,8 +1032,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; @@ -1041,7 +1041,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) @@ -1054,7 +1054,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(); @@ -1065,11 +1065,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 74dd95bf0ab..f179e2c40d1 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; @@ -1643,7 +1643,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; @@ -1825,7 +1825,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 fd13af348d1..a3cf1ab79be 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,8 +127,10 @@ 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}, + { "DEFINER", SYM(DEFINER_SYM),0,0}, { "DELAYED", SYM(DELAYED_SYM),0,0}, { "DELAY_KEY_WRITE", SYM(DELAY_KEY_WRITE_SYM),0,0}, { "DELETE", SYM(DELETE_SYM),0,0}, @@ -144,6 +152,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,11 +160,14 @@ 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}, { "EXPANSION", SYM(EXPANSION_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}, @@ -164,9 +176,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}, @@ -175,7 +187,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}, @@ -186,7 +198,6 @@ static SYMBOL symbols[] = { { "HAVING", SYM(HAVING),0,0}, { "HANDLER", SYM(HANDLER_SYM),0,0}, { "HASH", SYM(HASH_SYM),0,0}, - { "HEAP", SYM(HEAP_SYM),0,0}, { "HELP", SYM(HELP_SYM),0,0}, { "HIGH_PRIORITY", SYM(HIGH_PRIORITY),0,0}, { "HOUR", SYM(HOUR_SYM),0,0}, @@ -204,6 +215,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}, @@ -219,14 +232,16 @@ static SYMBOL symbols[] = { { "IF", SYM(IF),0,0}, { "IS", SYM(IS),0,0}, { "ISOLATION", SYM(ISOLATION),0,0}, - { "ISAM", SYM(ISAM_SYM),0,0}, { "ISSUER", SYM(ISSUER_SYM),0,0}, + { "ITERATE", SYM(ITERATE_SYM),0,0}, + { "INVOKER", SYM(INVOKER_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}, @@ -243,6 +258,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}, @@ -268,9 +284,7 @@ static SYMBOL symbols[] = { { "MEDIUMBLOB", SYM(MEDIUMBLOB),0,0}, { "MEDIUMTEXT", SYM(MEDIUMTEXT),0,0}, { "MEDIUMINT", SYM(MEDIUMINT),0,0}, - { "MERGE", SYM(MERGE_SYM),0,0}, { "MEDIUM", SYM(MEDIUM_SYM),0,0}, - { "MEMORY", SYM(MEMORY_SYM),0,0}, { "MICROSECOND", SYM(MICROSECOND_SYM),0,0}, { "MIDDLEINT", SYM(MEDIUMINT),0,0}, /* For powerbuilder */ { "MIN_ROWS", SYM(MIN_ROWS),0,0}, @@ -284,8 +298,7 @@ static SYMBOL symbols[] = { { "MULTILINESTRING", SYM(MULTILINESTRING),0,0}, { "MULTIPOINT", SYM(MULTIPOINT),0,0}, { "MULTIPOLYGON", SYM(MULTIPOLYGON),0,0}, - { "MRG_MYISAM", SYM(MERGE_SYM),0,0}, - { "MYISAM", SYM(MYISAM_SYM),0,0}, + { "NAME", SYM(NAME_SYM),0,0}, { "NAMES", SYM(NAMES_SYM),0,0}, { "NATURAL", SYM(NATURAL),0,0}, { "NATIONAL", SYM(NATIONAL_SYM),0,0}, @@ -307,6 +320,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}, @@ -337,13 +351,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 */ @@ -355,8 +371,10 @@ static SYMBOL symbols[] = { { "SAVEPOINT", SYM(SAVEPOINT_SYM),0,0}, { "SECOND", SYM(SECOND_SYM),0,0}, { "SECOND_MICROSECOND", SYM(SECOND_MICROSECOND_SYM),0,0}, + { "SECURITY", SYM(SECURITY_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}, @@ -371,6 +389,11 @@ static SYMBOL symbols[] = { { "SOME", SYM(ANY_SYM),0,0}, { "SONAME", SYM(UDF_SONAME_SYM),0,0}, { "SPATIAL", SYM(SPATIAL_SYM),0,0}, + { "SPECIFIC", SYM(SPECIFIC_SYM),0,0}, + { "SQL", SYM(SQL_SYM),0,0}, + { "SQLEXCEPTION", SYM(SQLEXCEPTION_SYM),0,0}, + { "SQLSTATE", SYM(SQLSTATE_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}, @@ -409,15 +432,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}, @@ -437,6 +461,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}, @@ -624,7 +649,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 d0dc0a83b11..8c93a22cf6e 100644 --- a/sql/log_event.cc +++ b/sql/log_event.cc @@ -832,7 +832,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), /* save the original thread id; we already know the server id */ slave_proxy_id(thd_arg->variables.pseudo_thread_id) diff --git a/sql/mysql_priv.h b/sql/mysql_priv.h index 9bc32581f32..7cf276cb31e 100644 --- a/sql/mysql_priv.h +++ b/sql/mysql_priv.h @@ -54,7 +54,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); @@ -408,7 +408,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); @@ -419,7 +419,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); @@ -1045,22 +1045,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 f26f18418a8..0f2968b464e 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 @@ -613,7 +616,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; @@ -796,6 +799,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__ */ @@ -887,7 +891,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 @@ -1268,12 +1271,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); } @@ -1403,7 +1406,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 @@ -1887,12 +1890,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 == 0 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); @@ -2093,7 +2100,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(); @@ -2211,6 +2217,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, @@ -2268,9 +2275,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", @@ -2968,7 +3021,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); @@ -3863,7 +3916,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, @@ -4141,9 +4195,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 884569d334a..0a767765d5d 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 @@ -145,6 +150,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); @@ -175,6 +184,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 @@ -1140,12 +1153,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 9a4543ada00..dfbe7d60741 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); @@ -340,7 +341,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, @@ -2119,13 +2120,38 @@ 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); + set_option_bit(thd, 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 c40765ebf94..7a13dfca46e 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -296,3 +296,35 @@ character-set=latin2 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index 98540e1bd0a..c5a4092f14b 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -290,3 +290,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 44c9399b821..68d0ee50f06 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -298,3 +298,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index e4f7c27610b..655428e97f2 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -287,3 +287,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index dec488567ff..aeb398eb6b0 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -292,3 +292,35 @@ character-set=latin7 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index c41c927d539..bad1f3ccafb 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -287,3 +287,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 0425a709950..10fcdda16e3 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -299,3 +299,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index 3cf5bbf592d..1a22f72c8c4 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -287,3 +287,35 @@ character-set=greek "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index f1b719ba716..a1c84108cc5 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -289,3 +289,35 @@ character-set=latin2 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index ed39950e9f1..47af6ec3639 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -287,3 +287,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index 9760cd3f9e8..ce798eeceff 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -289,3 +289,35 @@ character-set=ujis "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 10eed3bb2de..d9a7c1d90cc 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -287,3 +287,35 @@ character-set=euckr "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 7149eea8b10..cf885ff266d 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -289,3 +289,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index dc96d39f8dc..26f9c31e274 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -289,3 +289,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index b2b2e52ad75..4487f5f7cbd 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -291,3 +291,35 @@ character-set=latin2 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index c4a150d79bf..ac7064ff337 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -288,3 +288,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index dce141da20a..47d9cfc4f05 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -291,3 +291,35 @@ character-set=latin2 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index 27c1b49f4f0..ef1b4f1923e 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -289,3 +289,35 @@ character-set=koi8r "ëÅÛ ÚÁÐÒÏÓÏ× ÎÅ ÍÏÖÅÔ ÕÓÔÁÎÏ×ÉÔØ ÒÁÚÍÅÒ %lu, ÎÏ×ÙÊ ÒÁÚÍÅÒ ËÅÛÁ ÚÐÒÏÓÏ× - %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 5311fa016dc..12728e8985e 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -282,3 +282,35 @@ character-set=cp1250 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index 9355e8fc0c4..bc700c4e972 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -295,3 +295,35 @@ character-set=latin2 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 3cdcc3967d7..c7cd21baa4e 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -289,3 +289,35 @@ character-set=latin1 "Query cache failed to set size %lu, new query cache size is %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index 17dcdb89ae6..785809f482d 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -287,3 +287,35 @@ character-set=latin1 "Storleken av "Query cache" kunde inte sättas till %lu, ny storlek är %lu", "Kolumn '%-.64s' kan inte vara del av ett FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index 99a09afde6c..884b084c0a3 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -292,3 +292,35 @@ character-set=koi8u "ëÅÛ ÚÁÐÉÔ¦× ÎÅÓÐÒÏÍÏÖÅÎ ×ÓÔÁÎÏ×ÉÔÉ ÒÏÚÍ¦Ò %lu, ÎÏ×ÉÊ ÒÏÚÍ¦Ò ËÅÛÁ ÚÁÐÉÔ¦× - %lu", "Column '%-.64s' cannot be part of FULLTEXT index", "Unknown key cache '%-.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" +"Duplicate parameter: %s" +"Duplicate variable: %s" +"Duplicate condition: %s" +"Duplicate cursor: %s" +"Failed to ALTER %s %s" diff --git a/sql/slave.cc b/sql/slave.cc index 561b32b668c..c531d3cc4f7 100644 --- a/sql/slave.cc +++ b/sql/slave.cc @@ -583,7 +583,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()); } } } @@ -1103,12 +1103,11 @@ static int get_master_version_and_clock(MYSQL* mysql, MASTER_INFO* mi) BINLOG_FORMAT_323_GEQ_57 ; break; case '4': + case '5': mi->old_format = BINLOG_FORMAT_CURRENT; break; default: - /* 5.0 is not supported */ - errmsg = "Master reported an unrecognized MySQL version. Note that 4.0 \ -slaves can't replicate a 5.0 or newer master."; + errmsg = "Master reported unrecognized MySQL version"; break; } @@ -2330,7 +2329,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), @@ -2735,7 +2734,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..bf8cf37f293 --- /dev/null +++ b/sql/sp.cc @@ -0,0 +1,733 @@ +/* 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 + * + */ + +#define MYSQL_PROC_FIELD_NAME 0 +#define MYSQL_PROC_FIELD_TYPE 1 +#define MYSQL_PROC_FIELD_DEFINITION 2 +#define MYSQL_PROC_FIELD_CREATOR 3 +#define MYSQL_PROC_FIELD_MODIFIED 4 +#define MYSQL_PROC_FIELD_CREATED 5 +#define MYSQL_PROC_FIELD_SUID 6 +#define MYSQL_PROC_FIELD_COMMENT 7 +#define MYSQL_PROC_FIELD_COUNT 8 + +/* *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 (table->fields != MYSQL_PROC_FIELD_COUNT) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + + if ((defstr= get_field(&thd->mem_root, + table->field[MYSQL_PROC_FIELD_DEFINITION])) == NULL) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + + // Get additional information + if ((creator= get_field(&thd->mem_root, + table->field[MYSQL_PROC_FIELD_CREATOR])) == NULL) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + + modified= table->field[MYSQL_PROC_FIELD_MODIFIED]->val_int(); + created= table->field[MYSQL_PROC_FIELD_CREATED]->val_int(); + + if ((ptr= get_field(&thd->mem_root, + table->field[MYSQL_PROC_FIELD_SUID])) == NULL) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + if (ptr[0] == 'N') + suid= 0; + + table->field[MYSQL_PROC_FIELD_COMMENT]->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); + + if (table->fields != MYSQL_PROC_FIELD_COUNT) + { + ret= SP_GET_FIELD_FAILED; + goto done; + } + table->field[MYSQL_PROC_FIELD_NAME]->store(name, namelen, + system_charset_info); + table->field[MYSQL_PROC_FIELD_TYPE]->store((longlong)type); + table->field[MYSQL_PROC_FIELD_DEFINITION]->store(def, deflen, + system_charset_info); + table->field[MYSQL_PROC_FIELD_CREATOR]->store(creator, + (uint)strlen(creator), + system_charset_info); + ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_CREATED])->set_time(); + if (!suid) + table->field[MYSQL_PROC_FIELD_SUID]->store((longlong) 1); + if (comment) + table->field[MYSQL_PROC_FIELD_COMMENT]->store(comment, commentlen, + system_charset_info); + + if (table->file->write_row(table->record[0])) + ret= SP_WRITE_ROW_FAILED; + else + ret= SP_OK; + } + +done: + 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); +} + +static int +db_update_routine(THD *thd, int type, char *name, uint namelen, + char *newname, uint newnamelen, + char *comment, uint commentlen, enum suid_behaviour suid) +{ + DBUG_ENTER("db_update_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) + { + store_record(table,record[1]); + ((Field_timestamp *)table->field[MYSQL_PROC_FIELD_MODIFIED])->set_time(); + if (suid) + table->field[MYSQL_PROC_FIELD_SUID]->store((longlong) suid); + if (newname) + table->field[MYSQL_PROC_FIELD_NAME]->store(newname, + newnamelen, + system_charset_info); + if (comment) + table->field[MYSQL_PROC_FIELD_COMMENT]->store(comment, + commentlen, + system_charset_info); + if ((table->file->update_row(table->record[1],table->record[0]))) + ret= SP_WRITE_ROW_FAILED; + } + if (opened) + close_thread_tables(thd); + DBUG_RETURN(ret); +} + +struct st_used_field +{ + const char *field_name; + uint field_length; + enum enum_field_types field_type; + Field *field; +}; + +static struct st_used_field init_fields[]= +{ + { "Name", NAME_LEN, MYSQL_TYPE_STRING, 0}, + { "Type", 9, MYSQL_TYPE_STRING, 0}, + { "Creator", 77, MYSQL_TYPE_STRING, 0}, + { "Modified", 0, MYSQL_TYPE_TIMESTAMP, 0}, + { "Created", 0, MYSQL_TYPE_TIMESTAMP, 0}, + { "Suid", 1, MYSQL_TYPE_STRING, 0}, + { "Comment", NAME_LEN, MYSQL_TYPE_STRING, 0}, + { 0, 0, MYSQL_TYPE_STRING, 0} +}; + +int print_field_values(THD *thd, TABLE *table, + struct st_used_field *used_fields, + int type, const char *wild) +{ + Protocol *protocol= thd->protocol; + + if (table->field[MYSQL_PROC_FIELD_TYPE]->val_int() == type) + { + String *tmp_string= new String(); + struct st_used_field *used_field= used_fields; + get_field(&thd->mem_root, + used_field->field, + tmp_string); + if (!wild || !wild[0] || !wild_compare(tmp_string->ptr(), wild, 0)) + { + protocol->prepare_for_resend(); + protocol->store(tmp_string); + for (used_field++; + used_field->field_name; + used_field++) + { + switch (used_field->field_type) { + case MYSQL_TYPE_TIMESTAMP: + { + TIME tmp_time; + ((Field_timestamp *) used_field->field)->get_time(&tmp_time); + protocol->store(&tmp_time); + } + break; + default: + { + String *tmp_string1= new String(); + get_field(&thd->mem_root, used_field->field, tmp_string1); + protocol->store(tmp_string1); + } + break; + } + } + if (protocol->write()) + return 1; + } + } + return 0; +} + +int +db_show_routine_status(THD *thd, int type, const char *wild) +{ + DBUG_ENTER("db_show_routine_status"); + + TABLE *table; + TABLE_LIST tables; + + memset(&tables, 0, sizeof(tables)); + tables.db= (char*)"mysql"; + tables.real_name= tables.alias= (char*)"proc"; + + if (! (table= open_ltable(thd, &tables, TL_READ))) + { + DBUG_RETURN(1); + } + else + { + Item *item; + List<Item> field_list; + struct st_used_field *used_field; + st_used_field used_fields[array_elements(init_fields)]; + memcpy((char*) used_fields, (char*) init_fields, sizeof(used_fields)); + /* Init header */ + for (used_field= &used_fields[0]; + used_field->field_name; + used_field++) + { + switch (used_field->field_type) { + case MYSQL_TYPE_TIMESTAMP: + field_list.push_back(item=new Item_datetime(used_field->field_name)); + break; + default: + field_list.push_back(item=new Item_empty_string(used_field->field_name, + used_field-> + field_length)); + break; + } + } + /* Print header */ + if (thd->protocol->send_fields(&field_list,1)) + goto err_case; + + /* Init fields */ + setup_tables(&tables); + for (used_field= &used_fields[0]; + used_field->field_name; + used_field++) + { + TABLE_LIST *not_used; + Item_field *field= new Item_field("mysql", "proc", + used_field->field_name); + if (!(used_field->field= find_field_in_tables(thd, field, &tables, + ¬_used, TRUE))) + goto err_case1; + } + + table->file->index_init(0); + table->file->index_first(table->record[0]); + if (print_field_values(thd, table, used_fields, type, wild)) + goto err_case1; + while (!table->file->index_next(table->record[0])) + { + if (print_field_values(thd, table, used_fields, type, wild)) + goto err_case1; + } + send_eof(thd); + close_thread_tables(thd); + DBUG_RETURN(0); + } +err_case1: + send_eof(thd); +err_case: + close_thread_tables(thd); + DBUG_RETURN(1); +} + + +/* + * + * 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); +} + +int +sp_update_procedure(THD *thd, char *name, uint namelen, + char *newname, uint newnamelen, + char *comment, uint commentlen, enum suid_behaviour suid) +{ + DBUG_ENTER("sp_update_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_update_routine(thd, TYPE_ENUM_PROCEDURE, name, namelen, + newname, newnamelen, + comment, commentlen, suid); + + DBUG_RETURN(ret); +} + +int +sp_show_create_procedure(THD *thd, LEX_STRING *name) +{ + DBUG_ENTER("sp_show_create_procedure"); + DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + sp_head *sp; + + sp= sp_find_procedure(thd, name); + if (sp) + DBUG_RETURN(sp->show_create_procedure(thd)); + + DBUG_RETURN(1); +} + +int +db_show_status_procedure(THD *thd, const char *wild) +{ + DBUG_ENTER("db_show_status_procedure"); + DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_PROCEDURE, wild)); +} + +/* + * + * 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); +} + +int +sp_update_function(THD *thd, char *name, uint namelen, + char *newname, uint newnamelen, + char *comment, uint commentlen, enum suid_behaviour suid) +{ + DBUG_ENTER("sp_update_procedure"); + 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_update_routine(thd, TYPE_ENUM_FUNCTION, name, namelen, + newname, newnamelen, + comment, commentlen, suid); + + DBUG_RETURN(ret); +} + +int +sp_show_create_function(THD *thd, LEX_STRING *name) +{ + DBUG_ENTER("sp_show_create_function"); + DBUG_PRINT("enter", ("name: %*s", name->length, name->str)); + sp_head *sp; + + sp= sp_find_function(thd, name); + if (sp) + DBUG_RETURN(sp->show_create_function(thd)); + + DBUG_RETURN(1); +} + +int +db_show_status_function(THD *thd, const char *wild) +{ + DBUG_ENTER("db_show_status_function"); + DBUG_RETURN(db_show_routine_status(thd, TYPE_ENUM_FUNCTION, wild)); +} + +// 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..e7c2fba3bba --- /dev/null +++ b/sql/sp.h @@ -0,0 +1,87 @@ +/* -*- 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); + + +int +sp_update_procedure(THD *thd, char *name, uint namelen, + char *newname, uint newnamelen, + char *comment, uint commentlen, enum suid_behaviour suid); + +int +sp_show_create_procedure(THD *thd, LEX_STRING *name); + +int +db_show_status_procedure(THD *thd, const char *wild); + +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); + +int +sp_update_function(THD *thd, char *name, uint namelen, + char *newname, uint newnamelen, + char *comment, uint commentlen, enum suid_behaviour suid); + +int +sp_show_create_function(THD *thd, LEX_STRING *name); + +int +db_show_status_function(THD *thd, const char *wild); + +// 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..106f4c58fa6 --- /dev/null +++ b/sql/sp_head.cc @@ -0,0 +1,983 @@ +/* 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->is_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) +{ + 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); + 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 + // Note: We havo to check even if ret==0, since warnings (and some + // errors don't return a non-zero value. + if (!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) + { + Item_null *nit= NULL; // Re-use this, and only create if needed + 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) + { + if (! nit) + nit= new Item_null(); + nctx->push_item(nit); // 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). + 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; + for (SELECT_LEX *sl= sublex->all_selects_list ; + sl ; + sl= sl->next_select_in_list()) + { + // Save WHERE clause pointers to avoid damaging by optimisation + sl->prep_where= sl->where; + if (sl->with_wild) + { + // Copy item_list. We will restore it before calling the + // sub-statement, so it's ok to pop them. + sl->item_list_copy.empty(); + while (Item *it= sl->item_list.pop()) + sl->item_list_copy.push_back(it); + } + } + + // 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); + } +} + +int +sp_head::show_create_procedure(THD *thd) +{ + Protocol *protocol= thd->protocol; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + int res; + List<Item> field_list; + + DBUG_ENTER("sp_head::show_create_procedure"); + DBUG_PRINT("info", ("procedure %s", m_name.str)); + + field_list.push_back(new Item_empty_string("Procedure",NAME_LEN)); + // 1024 is for not to confuse old clients + field_list.push_back(new Item_empty_string("Create Procedure", + max(buffer.length(),1024))); + if (protocol->send_fields(&field_list, 1)) + DBUG_RETURN(1); + protocol->prepare_for_resend(); + protocol->store(m_name.str, m_name.length, system_charset_info); + protocol->store(m_defstr.str, m_defstr.length, system_charset_info); + res= protocol->write(); + send_eof(thd); + DBUG_RETURN(res); +} + +int +sp_head::show_create_function(THD *thd) +{ + Protocol *protocol= thd->protocol; + char buff[2048]; + String buffer(buff, sizeof(buff), system_charset_info); + int res; + List<Item> field_list; + + DBUG_ENTER("sp_head::show_create_function"); + DBUG_PRINT("info", ("procedure %s", m_name.str)); + + field_list.push_back(new Item_empty_string("Function",NAME_LEN)); + field_list.push_back(new Item_empty_string("Create Function", + max(buffer.length(),1024))); + if (protocol->send_fields(&field_list, 1)) + DBUG_RETURN(1); + protocol->prepare_for_resend(); + protocol->store(m_name.str, m_name.length, system_charset_info); + protocol->store(m_defstr.str, m_defstr.length, system_charset_info); + res= protocol->write(); + send_eof(thd); + DBUG_RETURN(res); +} +// ------------------------------------------------------------------ + +// +// 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 (lex->sql_command == SQLCOM_CREATE_TABLE || + lex->sql_command == SQLCOM_INSERT_SELECT) + { // Destroys sl->table_list.first + sl->table_list_first_copy= sl->table_list.first; + } + if (sl->with_wild) + { + // Restore item_list + // Note: We have to do this before executing the sub-statement, + // to make sure that the list nodes are in the right + // memroot. + List_iterator_fast<Item> li(sl->item_list_copy); + + sl->item_list.empty(); + while (Item *it= li++) + sl->item_list.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()) + { + TABLE_LIST *tabs; + + // We have closed all tables, get rid of pointers to them + for (tabs=(TABLE_LIST *)sl->table_list.first ; + tabs ; + tabs= tabs->next) + { + tabs->table= NULL; + } + if (lex->sql_command == SQLCOM_CREATE_TABLE || + lex->sql_command == SQLCOM_INSERT_SELECT) + { // Restore sl->table_list.first + sl->table_list.first= sl->table_list_first_copy; + } + 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..2a8f8a1e017 --- /dev/null +++ b/sql/sp_head.h @@ -0,0 +1,651 @@ +/* -*- 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); + + void + init_options(LEX_STRING *comment, enum suid_behaviour suid) + { + m_comment.length= 0; + m_comment.str= 0; + if (comment) + { + m_comment.length= comment->length; + m_comment.str= comment->str; + } + m_suid= suid ? suid - 1 : 1; + } + + + 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); + + int + show_create_procedure(THD *thd); + + int + show_create_function(THD *thd); + + 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..b7e23c9f5ad --- /dev/null +++ b/sql/sp_pcontext.cc @@ -0,0 +1,246 @@ +/* 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)); + VOID(my_init_dynamic_array(&m_scopes, sizeof(sp_scope_t), 16, 8)); + m_label.empty(); +} + +void +sp_pcontext::destroy() +{ + delete_dynamic(&m_pvar); + delete_dynamic(&m_cond); + delete_dynamic(&m_cursor); + delete_dynamic(&m_scopes); + m_label.empty(); +} + +void +sp_pcontext::push_scope() +{ + sp_scope_t s; + + s.vars= m_pvar.elements; + s.conds= m_cond.elements; + s.curs= m_cursor.elements; + insert_dynamic(&m_scopes, (gptr)&s); +} + +void +sp_pcontext::pop_scope() +{ + (void)pop_dynamic(&m_scopes); +} + + +/* 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, my_bool scoped) +{ + uint i = m_pvar.elements; + uint limit; + + if (! scoped || m_scopes.elements == 0) + limit= 0; + else + { + sp_scope_t s; + + get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1); + limit= s.vars; + } + + while (i-- > limit) + { + 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; + lab->isbegin= FALSE; + 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, my_bool scoped) +{ + uint i = m_cond.elements; + uint limit; + + if (! scoped || m_scopes.elements == 0) + limit= 0; + else + { + sp_scope_t s; + + get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1); + limit= s.conds; + } + + while (i-- > limit) + { + 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, my_bool scoped) +{ + uint i = m_cursor.elements; + uint limit; + + if (! scoped || m_scopes.elements == 0) + limit= 0; + else + { + sp_scope_t s; + + get_dynamic(&m_scopes, (gptr)&s, m_scopes.elements-1); + limit= s.curs; + } + + while (i-- > limit) + { + 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..02134e3604f --- /dev/null +++ b/sql/sp_pcontext.h @@ -0,0 +1,256 @@ +/* -*- 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 + my_bool isbegin; // For ITERATE error checking +} 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; + +typedef struct sp_scope +{ + uint vars, conds, curs; +} sp_scope_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(); + + // For error checking of duplicate things + void + push_scope(); + + void + pop_scope(); + + // + // 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, my_bool scoped=0); + + // 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, my_bool scoped=0); + + // + // 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, my_bool scoped=0); + + 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 + DYNAMIC_ARRAY m_scopes; // For error checking + + 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 b54e6a95bc5..78a92193b5d 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 9fe61ae19e1..abaa77a4b4d 100644 --- a/sql/sql_base.cc +++ b/sql/sql_base.cc @@ -2199,7 +2199,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"; @@ -2218,7 +2218,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 @@ -2270,7 +2270,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))) @@ -2478,7 +2478,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 396764cd532..edc61acb117 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; @@ -913,7 +913,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; @@ -921,11 +921,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; @@ -1018,7 +1023,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 } @@ -1027,7 +1032,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*/ @@ -1038,7 +1043,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 @@ -2593,7 +2598,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); } } @@ -3033,7 +3038,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 4fcd6504a2f..045fe36e3c8 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; @@ -152,9 +157,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) @@ -184,7 +192,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 @@ -263,9 +271,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); } @@ -292,6 +302,8 @@ void THD::cleanup(void) my_free((char*) variables.datetime_format, MYF(MY_ALLOW_ZERO_PTR)); 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) @@ -301,6 +313,7 @@ void THD::cleanup(void) pthread_mutex_unlock(&LOCK_user_locks); ull= 0; } + cleanup_done=1; DBUG_VOID_RETURN; } @@ -332,6 +345,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); @@ -352,14 +368,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 @@ -534,7 +550,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; } @@ -1173,30 +1189,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 @@ -1208,24 +1246,31 @@ bool select_dumpvar::send_data(List<Item> &items) my_error(ER_TOO_MANY_ROWS, MYF(0)); DBUG_RETURN(1); } - while ((xx=li++)) + while ((zz=my_li++) && (item=it++)) { - xx->check(); - xx->update(); + if (zz->local) + { + if ((yy=var_li++)) + { + thd->spcont->set_item_eval(yy->get_offset(), item, zz->type); + } + } + else + { + if ((xx=li++)) + { + xx->check(); + xx->update(); + } + } } DBUG_RETURN(0); } bool select_dumpvar::send_eof() { - if (row_count) - { - ::send_ok(thd,row_count); - return 0; - } - else - { - my_error(ER_EMPTY_QUERY,MYF(0)); - return 1; - } + if (! row_count) + send_warning(thd, ER_SP_FETCH_NO_DATA); + ::send_ok(thd,row_count); + return 0; } diff --git a/sql/sql_class.h b/sql/sql_class.h index b19caf057e6..9a7b45c816b 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 }; @@ -447,7 +449,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 @@ -605,12 +608,27 @@ 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; bool slow_command; + 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 @@ -648,7 +666,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) { @@ -1144,13 +1162,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 63bf5f061e9..22c6cc1f6f7 100644 --- a/sql/sql_db.cc +++ b/sql/sql_db.cc @@ -241,7 +241,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); @@ -295,7 +294,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); @@ -381,7 +379,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 4bab7be255c..972f88c9723 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)) @@ -96,7 +96,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 } @@ -104,11 +104,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.is_clear_all()) { - 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); } @@ -130,8 +130,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, @@ -140,14 +140,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) @@ -202,7 +202,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, @@ -225,9 +224,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); @@ -315,7 +314,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); } @@ -507,7 +506,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, @@ -618,7 +616,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_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 bc87f6aaabc..6ed8d23ab95 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}; @@ -115,9 +117,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; @@ -132,6 +135,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; } @@ -155,6 +166,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))) @@ -433,7 +455,7 @@ int yylex(void *arg, void *yythd) int tokval, result_state; 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 d2345165eb9..8f879f12b10 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 @@ -75,6 +78,10 @@ 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, + SQLCOM_SHOW_CREATE_PROC, SQLCOM_SHOW_CREATE_FUNC, + SQLCOM_SHOW_STATUS_PROC, SQLCOM_SHOW_STATUS_FUNC, /* This should be the last !!! */ SQLCOM_END @@ -84,6 +91,10 @@ enum enum_sql_command { #define DESCRIBE_NORMAL 1 #define DESCRIBE_EXTENDED 2 +enum suid_behaviour +{ + IS_DEFAULT_SUID= 0, IS_NOT_SUID, IS_SUID +}; typedef List<Item> List_item; @@ -348,7 +359,7 @@ public: void print(String *str); - 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, @@ -369,6 +380,8 @@ 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 */ + byte *table_list_first_copy; /* For SPs */ List<String> interval_list, use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; /* @@ -480,7 +493,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() { @@ -507,6 +520,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; @@ -560,6 +574,7 @@ typedef struct st_lex enum enum_enable_or_disable alter_keys_onoff; enum enum_var_type option_type; enum tablespace_op_type tablespace_op; + enum suid_behaviour suid; uint uint_geom_type; uint grant, grant_tot_col, which_columns; uint fk_delete_opt, fk_update_opt, fk_match_option; @@ -570,7 +585,22 @@ typedef struct st_lex bool in_comment, ignore_space, verbose, simple_alter, no_write_to_binlog; bool derived_tables; 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(uint8 cause) { safe_to_cache_query= 0; @@ -602,4 +632,4 @@ extern pthread_key(LEX*,THR_LEX); extern LEX_STRING tmp_table_alias; -#define current_lex (¤t_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 017ef065012..eb687ab0082 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") + #ifdef SOLARIS extern "C" int gethostname(char *name, int namelen); #endif @@ -938,7 +950,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; @@ -1143,7 +1155,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; @@ -1169,6 +1181,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) @@ -1204,7 +1219,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: { @@ -1349,16 +1364,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) @@ -1580,7 +1595,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_SET_OPTION: @@ -1636,7 +1651,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))) { @@ -1652,6 +1667,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); } @@ -1707,16 +1723,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 @@ -1737,7 +1760,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 /* @@ -1773,14 +1796,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. @@ -1795,7 +1818,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); @@ -1840,12 +1863,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; if (lex->describe & DESCRIBE_EXTENDED) @@ -2155,7 +2178,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) @@ -2244,7 +2267,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; @@ -2257,7 +2280,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; @@ -2356,7 +2379,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)) @@ -2369,7 +2392,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) || @@ -2398,7 +2421,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); @@ -2426,7 +2448,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); @@ -2466,7 +2487,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); @@ -2484,7 +2504,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, @@ -2507,7 +2527,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; @@ -2546,7 +2566,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, @@ -2646,7 +2666,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; @@ -2772,7 +2792,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)) @@ -2815,7 +2835,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 @@ -2830,7 +2850,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; @@ -2879,7 +2899,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; @@ -2906,7 +2926,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; @@ -3098,26 +3118,24 @@ mysql_execute_command(THD *thd) res=mysqld_show_create_db(thd,lex->name,&lex->create_info); break; } - case SQLCOM_CREATE_FUNCTION: - if (check_access(thd,INSERT_ACL,"mysql",0,1,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; + res= -1; #endif break; + } #ifndef NO_EMBEDDED_ACCESS_CHECKS case SQLCOM_DROP_USER: { @@ -3125,7 +3143,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); @@ -3141,7 +3158,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); @@ -3192,14 +3208,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 @@ -3214,7 +3227,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); @@ -3257,7 +3269,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); @@ -3269,7 +3280,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: @@ -3381,16 +3392,257 @@ 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, 0)) || + (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: + { + res= -1; + uint newname_len= 0; + if (lex->name) + newname_len= strlen(lex->name); + if (newname_len > NAME_LEN) + { + net_printf(thd, ER_TOO_LONG_IDENT, lex->name); + goto error; + } + if (lex->sql_command == SQLCOM_ALTER_PROCEDURE) + res= sp_update_procedure(thd, lex->udf.name.str, lex->udf.name.length, + lex->name, newname_len, lex->comment->str, + lex->comment->length, lex->suid); + else + res= sp_update_function(thd, lex->udf.name.str, lex->udf.name.length, + lex->name, newname_len, lex->comment->str, + lex->comment->length, lex->suid); + switch (res) + { + case SP_OK: + send_ok(thd); + break; + case SP_KEY_NOT_FOUND: + net_printf(thd, ER_SP_DOES_NOT_EXIST, SP_COM_STRING(lex),lex->udf.name); + goto error; + default: + net_printf(thd, ER_SP_CANT_ALTER, SP_COM_STRING(lex),lex->udf.name); + goto error; + } + 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, 0)) + 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; + } + case SQLCOM_SHOW_CREATE_PROC: + { + res= -1; + if (lex->udf.name.length > NAME_LEN) + { + net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + goto error; + } + res= sp_show_create_procedure(thd, &lex->udf.name); + if (res == SP_KEY_NOT_FOUND) + { + net_printf(thd, ER_SP_DOES_NOT_EXIST, + SP_COM_STRING(lex), lex->udf.name.str); + goto error; + } + break; + } + case SQLCOM_SHOW_CREATE_FUNC: + { + if (lex->udf.name.length > NAME_LEN) + { + net_printf(thd, ER_TOO_LONG_IDENT, lex->udf.name.str); + goto error; + } + res= sp_show_create_function(thd, &lex->udf.name); + if (res == SP_KEY_NOT_FOUND) + { + net_printf(thd, ER_SP_DOES_NOT_EXIST, + SP_COM_STRING(lex), lex->udf.name.str); + goto error; + } + break; + } + case SQLCOM_SHOW_STATUS_PROC: + { + res= db_show_status_procedure(thd, (lex->wild ? + lex->wild->ptr() : NullS)); + break; + } + case SQLCOM_SHOW_STATUS_FUNC: + { + res= db_show_status_function(thd, (lex->wild ? + lex->wild->ptr() : NullS)); + 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); } @@ -3699,10 +3951,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; @@ -3718,22 +3970,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; } @@ -3827,7 +4084,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; @@ -3870,7 +4127,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); @@ -3886,6 +4152,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"; @@ -3909,7 +4182,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"); @@ -4238,7 +4511,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; } @@ -4523,7 +4796,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 @@ -4623,7 +4895,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; @@ -4645,7 +4917,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 @@ -4726,11 +4998,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 2fa08e2d649..9721b77e38a 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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> ¶ms= thd->lex.param_list; + List<Item> ¶ms= 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 ecad84ba0cb..cc64ca1aa2b 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 745f19d0bcf..bef69281666 100644 --- a/sql/sql_select.cc +++ b/sql/sql_select.cc @@ -208,7 +208,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); } @@ -504,8 +505,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) @@ -2449,7 +2450,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) @@ -3527,7 +3528,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++) @@ -5365,7 +5366,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; @@ -5563,7 +5564,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) @@ -5604,7 +5605,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++; @@ -5685,7 +5686,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; @@ -6310,7 +6311,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) @@ -6378,7 +6379,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 */ } @@ -6448,7 +6449,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 */ } @@ -6495,7 +6496,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 || @@ -7261,7 +7262,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; } @@ -7373,7 +7374,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; } @@ -8848,7 +8849,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; @@ -9035,8 +9036,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) ? @@ -9066,7 +9067,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, @@ -9077,7 +9078,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 c2ebdeab5c2..8a11c1de9d4 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1399,7 +1399,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 b1d23de06b0..4cc2842e2e7 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); } } @@ -987,17 +983,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: @@ -1244,7 +1236,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; @@ -1916,7 +1908,6 @@ int mysql_discard_or_import_tablespace(THD *thd, error=1; if (error) 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); @@ -2081,7 +2072,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); @@ -2464,7 +2454,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); @@ -2595,7 +2584,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); @@ -2693,8 +2681,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, @@ -2724,7 +2712,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 5292299f928..09269604f1c 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"); @@ -134,7 +134,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) { @@ -197,7 +197,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) @@ -222,7 +222,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; @@ -237,14 +237,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"); @@ -264,7 +264,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(); @@ -336,13 +336,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 */ @@ -373,7 +373,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; @@ -428,7 +428,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 9214894e214..b87504ddb71 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.is_clear_all()) { - 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)) @@ -977,7 +976,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 0dbe14fd2ab..e30b9226a38 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) timestamp_type date_time_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 @@ -129,6 +137,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token CREATE %token CROSS %token CUBE_SYM +%token DEFINER_SYM %token DELETE_SYM %token DUAL_SYM %token DO_SYM @@ -159,6 +168,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SELECT_SYM %token SHOW %token SLAVE +%token SQL_SYM %token SQL_THREAD %token START_SYM %token STD_SYM @@ -202,10 +212,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 @@ -223,14 +237,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 @@ -241,7 +258,6 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token GROUP %token HAVING %token HASH_SYM -%token HEAP_SYM %token HEX_NUM %token HIGH_PRIORITY %token HOSTS_SYM @@ -254,10 +270,11 @@ 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 INVOKER_SYM %token ISOLATION -%token ISAM_SYM %token JOIN_SYM %token KEYS %token KEY_SYM @@ -269,6 +286,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 @@ -296,11 +314,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token MAX_QUERIES_PER_HOUR %token MAX_UPDATES_PER_HOUR %token MEDIUM_SYM -%token MERGE_SYM -%token MEMORY_SYM %token MIN_ROWS -%token MYISAM_SYM %token NAMES_SYM +%token NAME_SYM %token NATIONAL_SYM %token NATURAL %token NEW_SYM @@ -319,6 +335,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 @@ -350,6 +367,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token ROW_FORMAT_SYM %token ROW_SYM %token RTREE_SYM +%token SECURITY_SYM %token SET %token SEPARATOR_SYM %token SERIAL_SYM @@ -358,6 +376,10 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); %token SIMPLE_SYM %token SHUTDOWN %token SPATIAL_SYM +%token SPECIFIC_SYM +%token SQLEXCEPTION_SYM +%token SQLSTATE_SYM +%token SQLWARNING_SYM %token SSL_SYM %token STARTING %token STATUS_SYM @@ -379,11 +401,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 @@ -530,6 +554,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 @@ -563,6 +588,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 @@ -589,6 +626,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text UNDERSCORE_CHARSET IDENT_sys TEXT_STRING_sys TEXT_STRING_literal NCHAR_STRING opt_component key_cache_name + SP_FUNC ident_or_spfunc sp_opt_label sp_comment sp_newname %type <lex_str_ptr> opt_table_alias @@ -622,13 +660,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 @@ -695,7 +734,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 @@ -715,8 +754,15 @@ bool my_yyoverflow(short **a, YYSTYPE **b,int *yystacksize); precision subselect_start opt_and charset subselect_end select_var_list select_var_list_init help opt_len opt_extended_describe + statement sp_suid 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 @@ -729,23 +775,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 @@ -958,20 +1010,917 @@ 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); + + 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_comment sp_suid + { + Lex->sphead->init_options(&$9, Lex->suid); + } + 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); + + 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_comment sp_suid + { + Lex->sphead->init_options(&$9, Lex->suid); + } + 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); + } + ; + +sp_comment: + /* Empty */ { $$.str= 0; $$.length= 0; } + | COMMENT_SYM TEXT_STRING_sys { $$= $2; } + ; + +sp_newname: + /* Empty */ { $$.str= 0; $$.length= 0; } + | NAME_SYM ident { $$= $2; } + ; + + +sp_suid: + /* Empty */ { Lex->suid= IS_DEFAULT_SUID; } + | SQL_SYM SECURITY_SYM DEFINER_SYM { Lex->suid= IS_SUID; } + | SQL_SYM SECURITY_SYM INVOKER_SYM { Lex->suid= IS_NOT_SUID; } + ; + +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 *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_pvar(&$1, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_PARAM, $1.str); + YYABORT; + } + spc->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 *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_pvar(&$2, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_PARAM, $2.str); + YYABORT; + } + spc->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: + /* Empty */ {} + | 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 + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_cond(&$2, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_COND, $2.str); + YYABORT; + } + 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_pcontext *spc= lex->spcont; + uint offp; + sp_instr_cpush *i; + + if (spc->find_cursor(&$2, &offp, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_CURS, $2.str); + delete $5; + YYABORT; + } + 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; + } + | SQLSTATE_SYM opt_value TEXT_STRING_literal + { /* SQLSTATE */ + uint len= ($3.length < sizeof($$->sqlstate)-1 ? + $3.length : sizeof($$->sqlstate)-1); + + $$= (sp_cond_type_t *)YYTHD->alloc(sizeof(sp_cond_type_t)); + $$->type= sp_cond_type_t::state; + memcpy($$->sqlstate, $3.str, len); + $$->sqlstate[len]= '\0'; + } + ; + +opt_value: + /* Empty */ {} + | VALUE_SYM {} + ; + +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 *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_pvar(&$1, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_VAR, $1.str); + YYABORT; + } + spc->push_pvar(&$1, (enum_field_types)0, sp_param_in); + $$= 1; + } + | sp_decl_idents ',' ident + { + LEX *lex= Lex; + sp_pcontext *spc= lex->spcont; + + if (spc->find_pvar(&$3, TRUE)) + { + net_printf(YYTHD, ER_SP_DUP_VAR, $3.str); + YYABORT; + } + spc->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 || lab->isbegin) + { + 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 sp_opt_label + { + LEX *lex= Lex; + + if ($5.str) + { + 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; + } + } + lex->sphead->backpatch(lex->spcont->pop_label()); + } + ; + +sp_opt_label: + /* Empty */ + { $$.str= NULL; $$.length= 0; } + | IDENT + { $$= $1; } + ; + +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 *lex= Lex; + sp_label_t *lab= lex->spcont->last_label(); + + lab->isbegin= TRUE; + /* Scope duplicate checking */ + lex->spcont->push_scope(); + } + sp_decls + sp_proc_stmts + END + { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_pcontext *ctx= lex->spcont; + + sp->backpatch(ctx->last_label()); /* We always has a 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)); + ctx->pop_scope(); + } + | 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 {} @@ -1126,13 +2075,14 @@ create_table_option: | INDEX DIRECTORY_SYM opt_equal TEXT_STRING_sys { Lex->create_info.index_file_name= $4.str; }; table_types: - ISAM_SYM { $$= DB_TYPE_ISAM; } - | MYISAM_SYM { $$= DB_TYPE_MYISAM; } - | MERGE_SYM { $$= DB_TYPE_MRG_MYISAM; } - | HEAP_SYM { $$= DB_TYPE_HEAP; } - | MEMORY_SYM { $$= DB_TYPE_HEAP; } - | BERKELEY_DB_SYM { $$= DB_TYPE_BERKELEY_DB; } - | INNOBASE_SYM { $$= DB_TYPE_INNODB; }; + ident_or_text + { + $$ = ha_resolve_by_name($1.str,$1.length); + if ($$ == DB_TYPE_UNKNOWN) { + net_printf(YYTHD, ER_UNKNOWN_TABLE, $1.str); + YYABORT; + } + }; row_types: DEFAULT { $$= ROW_TYPE_DEFAULT; } @@ -1643,7 +2593,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, @@ -1672,8 +2622,39 @@ alter: LEX *lex=Lex; lex->sql_command=SQLCOM_ALTER_DB; lex->name=$3.str; - }; + } + | ALTER PROCEDURE ident sp_newname sp_comment sp_suid + opt_restrict + { + THD *thd= YYTHD; + LEX *lex=Lex; + lex->sql_command= SQLCOM_ALTER_PROCEDURE; + lex->udf.name= $3; + lex->name= $4.str; + /* $5 is a yacc/bison internal struct, so we can't keep + the pointer to it for use outside the parser. */ + lex->comment= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING)); + lex->comment->str= $5.str; + lex->comment->length= $5.length; + } + | ALTER FUNCTION_SYM ident sp_newname sp_comment sp_suid + opt_restrict + { + THD *thd= YYTHD; + LEX *lex=Lex; + + lex->sql_command= SQLCOM_ALTER_FUNCTION; + lex->udf.name= $3; + lex->name= $4.str; + /* $5 is a yacc/bison internal struct, so we can't keep + the pointer to it for use outside the parser. */ + lex->comment= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING)); + lex->comment= (LEX_STRING *)thd->alloc(sizeof(LEX_STRING)); + lex->comment->str= $5.str; + lex->comment->length= $5.length; + } + ; alter_list: | DISCARD TABLESPACE { Lex->tablespace_op=DISCARD_TABLESPACE; } @@ -2240,7 +3221,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)++; }; @@ -2552,6 +3533,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 ')' @@ -2817,6 +3800,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) @@ -2905,10 +3896,43 @@ fulltext_options: | IN_SYM BOOLEAN_SYM MODE_SYM { $$= FT_BOOL; } ; -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); } @@ -3514,12 +4538,34 @@ select_var_list: | select_var_ident {} ; -select_var_ident: '@' ident_or_text +select_var_ident: + '@' ident_or_text { LEX *lex=Lex; - if (lex->result && ((select_dumpvar *)lex->result)->var_list.push_back((LEX_STRING*) sql_memdup(&$2,sizeof(LEX_STRING)))) + if (lex->result) + ((select_dumpvar *)lex->result)->var_list.push_back( new my_var($2,0,0,(enum_field_types)0)); + else YYABORT; } + | ident_or_text + { + LEX *lex=Lex; + if (!lex->spcont) + YYABORT; + sp_pvar_t *t; + if (!(t=lex->spcont->find_pvar(&$1))) + { + send_error(lex->thd, ER_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; + } + } ; into: @@ -3599,11 +4645,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 { @@ -3615,7 +4669,6 @@ drop: {} ; - table_list: table_name | table_list ',' table_name; @@ -3674,7 +4727,6 @@ replace: } insert_field_spec {} - {} ; insert_lock_option: @@ -4029,8 +5081,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; } @@ -4066,7 +5118,26 @@ show_param: | SLAVE STATUS_SYM { Lex->sql_command = SQLCOM_SHOW_SLAVE_STAT; - }; + } + | CREATE PROCEDURE ident + { + Lex->sql_command = SQLCOM_SHOW_CREATE_PROC; + Lex->udf.name= $3; + } + | CREATE FUNCTION_SYM ident + { + Lex->sql_command = SQLCOM_SHOW_CREATE_FUNC; + Lex->udf.name= $3; + } + | PROCEDURE STATUS_SYM wild + { + Lex->sql_command = SQLCOM_SHOW_STATUS_PROC; + } + | FUNCTION_SYM STATUS_SYM wild + { + Lex->sql_command = SQLCOM_SHOW_STATUS_FUNC; + }; + master_or_binary: MASTER_SYM @@ -4221,18 +5292,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 @@ -4419,16 +5495,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) { @@ -4444,7 +5537,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) { @@ -4460,7 +5553,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) { @@ -4601,6 +5694,7 @@ keyword: | DATETIME {} | DATE_SYM {} | DAY_SYM {} + | DEFINER_SYM {} | DELAY_KEY_WRITE_SYM {} | DES_KEY_FILE {} | DIRECTORY_SYM {} @@ -4633,15 +5727,14 @@ keyword: | GLOBAL_SYM {} | HANDLER_SYM {} | HASH_SYM {} - | HEAP_SYM {} | HELP_SYM {} | HOSTS_SYM {} | HOUR_SYM {} | IDENTIFIED_SYM {} + | INVOKER_SYM {} | IMPORT {} | INDEXES {} | ISOLATION {} - | ISAM_SYM {} | ISSUER_SYM {} | INNOBASE_SYM {} | INSERT_METHOD {} @@ -4672,8 +5765,6 @@ keyword: | MAX_QUERIES_PER_HOUR {} | MAX_UPDATES_PER_HOUR {} | MEDIUM_SYM {} - | MERGE_SYM {} - | MEMORY_SYM {} | MICROSECOND_SYM {} | MINUTE_SYM {} | MIN_ROWS {} @@ -4683,7 +5774,7 @@ keyword: | MULTILINESTRING {} | MULTIPOINT {} | MULTIPOLYGON {} - | MYISAM_SYM {} + | NAME_SYM {} | NAMES_SYM {} | NATIONAL_SYM {} | NCHAR_SYM {} @@ -4727,6 +5818,7 @@ keyword: | RTREE_SYM {} | SAVEPOINT_SYM {} | SECOND_SYM {} + | SECURITY_SYM {} | SERIAL_SYM {} | SERIALIZABLE_SYM {} | SESSION_SYM {} @@ -4755,7 +5847,7 @@ keyword: | TIMESTAMP {} | TIME_SYM {} | TYPE_SYM {} - | UDF_SYM {} + | FUNCTION_SYM {} | UNCOMMITTED_SYM {} | UNICODE_SYM {} | UNTIL_SYM {} @@ -4813,15 +5905,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 { @@ -4868,7 +5973,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 { @@ -4879,12 +5984,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 { @@ -5416,7 +6534,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(); @@ -5432,7 +6550,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 4dcd24b2aff..e492398ed17 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 |