diff options
author | unknown <pem@mysql.telia.com> | 2003-10-10 16:57:21 +0200 |
---|---|---|
committer | unknown <pem@mysql.telia.com> | 2003-10-10 16:57:21 +0200 |
commit | 04c6b9b8d8c10fd83ab80f1a3e2d6bd58b34b825 (patch) | |
tree | 502f5a108855c24da25af7d42aa635d0a3c5103b | |
parent | 4379cbcf3056a33f41ddf489828203a6c03e793f (diff) | |
download | mariadb-git-04c6b9b8d8c10fd83ab80f1a3e2d6bd58b34b825.tar.gz |
WL#962: Added simple, read-only, non-scrolling, asensitive cursors in SPs, using the
(updated) Protocol_cursor class.
Also did some bug fixes.
Docs/sp-imp-spec.txt:
Added CURSOR docs (and fixed typos)
Docs/sp-implemented.txt:
Updated for CURSORs
include/mysqld_error.h:
New error codes/messages for CURSORs
libmysqld/Makefile.am:
SP cursors now needs this.
mysql-test/r/sp-error.result:
New tests for cursors.
mysql-test/r/sp.result:
New tests for cursors.
mysql-test/t/sp-error.test:
New tests for cursors.
mysql-test/t/sp.test:
New tests for cursors.
sql/protocol.cc:
We now always have Protocol_cursor (SPs use it)
sql/protocol.h:
Fixed bugs in Protocol_cursor (for SPs)
sql/protocol_cursor.cc:
Fixed bugs in Protocol_cursor (for SPs)
sql/share/czech/errmsg.txt:
New error codes/messages for CURSORs
sql/share/danish/errmsg.txt:
New error codes/messages for CURSORs
sql/share/dutch/errmsg.txt:
New error codes/messages for CURSORs
sql/share/english/errmsg.txt:
New error codes/messages for CURSORs
sql/share/estonian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/french/errmsg.txt:
New error codes/messages for CURSORs
sql/share/german/errmsg.txt:
New error codes/messages for CURSORs
sql/share/greek/errmsg.txt:
New error codes/messages for CURSORs
sql/share/hungarian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/italian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/japanese/errmsg.txt:
New error codes/messages for CURSORs
sql/share/korean/errmsg.txt:
New error codes/messages for CURSORs
sql/share/norwegian-ny/errmsg.txt:
New error codes/messages for CURSORs
sql/share/norwegian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/polish/errmsg.txt:
New error codes/messages for CURSORs
sql/share/portuguese/errmsg.txt:
New error codes/messages for CURSORs
sql/share/romanian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/russian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/serbian/errmsg.txt:
New error codes/messages for CURSORs
sql/share/slovak/errmsg.txt:
New error codes/messages for CURSORs
sql/share/spanish/errmsg.txt:
New error codes/messages for CURSORs
sql/share/swedish/errmsg.txt:
New error codes/messages for CURSORs
sql/share/ukrainian/errmsg.txt:
New error codes/messages for CURSORs
sql/sp_head.cc:
Added cursor support.
Also fixed problems with item_lists, where pointers and ref_pointer_arrays.
sql/sp_head.h:
Added cursor support
sql/sp_pcontext.cc:
Added cursor support
sql/sp_pcontext.h:
Added cursor support
sql/sp_rcontext.cc:
Added cursor support, in particular the new sp_cursor class.
sql/sp_rcontext.h:
Added cursor support, in particular the new sp_cursor class.
sql/sql_lex.h:
We sometimes need to copy item_lists in LEX when executing substatements in SPs
sql/sql_yacc.yy:
Added minimal cursor support (not the full syntax yet).
42 files changed, 1290 insertions, 71 deletions
diff --git a/Docs/sp-imp-spec.txt b/Docs/sp-imp-spec.txt index 1b72026c427..dee26ab38c0 100644 --- a/Docs/sp-imp-spec.txt +++ b/Docs/sp-imp-spec.txt @@ -11,7 +11,7 @@ which dispatches on the command code (in Lex) to the corresponding code for executing that particular query. - There are thre structures involved in the execution of a query which are of + 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 @@ -186,7 +186,7 @@ stored in the table mysql.proc with the name and type as the key, the type being one of the enum ("procedure","function"). - A PROCEDURE is just stored int the mysql.proc table. A FUNCTION has an + 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 @@ -293,7 +293,7 @@ So, stored functions must be handled in a simpilar way, and as a consequence, UDFs and functions must not have the same name. - - Detecting and parsing a FUNCTION invokation + - 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 @@ -319,7 +319,7 @@ "on-the-fly" during the execution of *another* statement. This makes things a lot more complicated compared to CALL: - We can't read and parse the FUNCTION from the mysql.proc table at the - point of invokation; the server requires that all tables used are + 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 @@ -478,6 +478,67 @@ 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) + + - 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. @@ -569,6 +630,18 @@ // 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(); } @@ -589,8 +662,9 @@ class sp_rcontext { - // 'fsize' is the max size of the context, 'hmax' the number of handlers - sp_rcontext(uint fsize, uint hmax); + // '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); @@ -645,6 +719,18 @@ // 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); + } @@ -709,6 +795,7 @@ bool suid, char *comment, uint commentlen); } + - Instructions - The base class: @@ -816,6 +903,55 @@ 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 diff --git a/Docs/sp-implemented.txt b/Docs/sp-implemented.txt index 5ce09ae3af4..c9112d75e43 100644 --- a/Docs/sp-implemented.txt +++ b/Docs/sp-implemented.txt @@ -8,8 +8,7 @@ Summary of Not Yet Implemented: - Access control - Routine characteristics (mostly used for external languages) - SQL-99 COMMIT (related to BEGIN/END) - - DECLARE CURSOR ... - - FOR-loops (as it requires cursors) + - FOR-loops - CASCADE/RESTRICT for ALTER and DROP - ALTER/DROP METHOD (as it implies User Defined Types) - SIGNAL and RESIGNAL, and UNDO handlers @@ -25,6 +24,7 @@ Summary of what's implemented: - "Non-query" FUNCTIONs only - Prepared SP caching - CONDITIONs and HANDLERs + - Simple read-only CURSORs. List of what's implemented: @@ -86,6 +86,11 @@ List of what's 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: diff --git a/include/mysqld_error.h b/include/mysqld_error.h index 18283d576ad..34caaee4e6a 100644 --- a/include/mysqld_error.h +++ b/include/mysqld_error.h @@ -314,4 +314,12 @@ #define ER_SP_COND_MISMATCH 1295 #define ER_SP_NORETURN 1296 #define ER_SP_NORETURNEND 1297 -#define ER_ERROR_MESSAGES 298 +#define ER_SP_BAD_CURSOR_QUERY 1298 +#define ER_SP_BAD_CURSOR_SELECT 1299 +#define ER_SP_CURSOR_MISMATCH 1300 +#define ER_SP_CURSOR_ALREADY_OPEN 1301 +#define ER_SP_CURSOR_NOT_OPEN 1302 +#define ER_SP_UNDECLARED_VAR 1303 +#define ER_SP_WRONG_NO_OF_FETCH_ARGS 1304 +#define ER_SP_FETCH_NO_DATA 1305 +#define ER_ERROR_MESSAGES 306 diff --git a/libmysqld/Makefile.am b/libmysqld/Makefile.am index 080da0c7725..a75efd47914 100644 --- a/libmysqld/Makefile.am +++ b/libmysqld/Makefile.am @@ -56,7 +56,7 @@ sqlsources = derror.cc field.cc field_conv.cc filesort.cc \ sql_string.cc sql_table.cc sql_test.cc sql_udf.cc \ sql_update.cc sql_yacc.cc table.cc thr_malloc.cc time.cc \ unireg.cc uniques.cc stacktrace.c sql_union.cc hash_filo.cc \ - spatial.cc gstream.cc sql_help.cc \ + spatial.cc gstream.cc sql_help.cc protocol_cursor.cc \ sp_head.cc sp_pcontext.cc sp.cc sp_cache.cc sp_rcontext.cc libmysqld_int_a_SOURCES= $(libmysqld_sources) $(libmysqlsources) $(sqlsources) diff --git a/mysql-test/r/sp-error.result b/mysql-test/r/sp-error.result index d90aef7f609..ef8b2ac1552 100644 --- a/mysql-test/r/sp-error.result +++ b/mysql-test/r/sp-error.result @@ -138,3 +138,85 @@ end; select f(10); ERROR HY000: FUNCTION f ended without RETURN drop function f; +create procedure p() +begin +declare c cursor for insert into test.t1 values ("foo", 42); +open c; +close c; +end; +ERROR HY000: Cursor statement must be a SELECT +create procedure p() +begin +declare x int; +declare c cursor for select * into x from test.t limit 1; +open c; +close c; +end; +ERROR HY000: Cursor SELECT must not have INTO +create procedure p() +begin +declare c cursor for select * from test.t; +open cc; +close c; +end; +ERROR HY000: Undefined CURSOR: cc +drop table if exists t1; +create table t1 (val int); +create procedure p() +begin +declare c cursor for select * from test.t1; +open c; +open c; +close c; +end; +call p(); +ERROR HY000: Cursor is already open +drop procedure p; +create procedure p() +begin +declare c cursor for select * from test.t1; +open c; +close c; +close c; +end; +call p(); +ERROR HY000: Cursor is not open +drop procedure p; +drop table t1; +drop table if exists t1; +create table t1 (val int, x float); +insert into t1 values (42, 3.1), (19, 1.2); +create procedure p() +begin +declare c cursor for select * from t1; +declare x int; +open c; +fetch c into x, y; +close c; +end; +ERROR HY000: Undeclared variable: y +create procedure p() +begin +declare c cursor for select * from t1; +declare x int; +open c; +fetch c into x; +close c; +end; +call p(); +ERROR HY000: Wrong number of FETCH variables +drop procedure p; +create procedure p() +begin +declare c cursor for select * from t1; +declare x int; +declare y float; +declare z int; +open c; +fetch c into x, y, z; +close c; +end; +call p(); +ERROR HY000: Wrong number of FETCH variables +drop procedure p; +drop table t1; diff --git a/mysql-test/r/sp.result b/mysql-test/r/sp.result index c8652b25b2e..3339c93a0be 100644 --- a/mysql-test/r/sp.result +++ b/mysql-test/r/sp.result @@ -500,6 +500,33 @@ id data hndlr3 13 delete from t1; drop procedure hndlr3; +create procedure cur1() +begin +declare done int default 0; +declare continue handler for 1305 set done = 1; +declare c cursor for select * from test.t2; +declare a char(16); +declare b int; +declare c double; +open c; +repeat +fetch c into a, b, c; +if not done then +insert into test.t1 values (a, b+c); +end if; +until done end repeat; +close c; +end; +insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14); +call cur1(); +select * from t1; +id data +foo 40 +bar 15 +zap 663 +delete from t1; +delete from t2; +drop procedure cur1; create procedure bug822(a_id char(16), a_data int) begin declare n int; diff --git a/mysql-test/t/sp-error.test b/mysql-test/t/sp-error.test index ba2805bfb0c..c075e96cc78 100644 --- a/mysql-test/t/sp-error.test +++ b/mysql-test/t/sp-error.test @@ -194,4 +194,110 @@ select f(10)| drop function f| +--error 1298 +create procedure p() +begin + declare c cursor for insert into test.t1 values ("foo", 42); + + open c; + close c; +end| + +--error 1299 +create procedure p() +begin + declare x int; + declare c cursor for select * into x from test.t limit 1; + + open c; + close c; +end| + +--error 1300 +create procedure p() +begin + declare c cursor for select * from test.t; + + open cc; + close c; +end| + +--disable_warnings +drop table if exists t1| +--enable_warnings +create table t1 (val int)| + +create procedure p() +begin + declare c cursor for select * from test.t1; + + open c; + open c; + close c; +end| +--error 1301 +call p()| +drop procedure p| + +create procedure p() +begin + declare c cursor for select * from test.t1; + + open c; + close c; + close c; +end| +--error 1302 +call p()| +drop procedure p| + +drop table t1| + +--disable_warnings +drop table if exists t1| +--enable_warnings +create table t1 (val int, x float)| +insert into t1 values (42, 3.1), (19, 1.2)| + +--error 1303 +create procedure p() +begin + declare c cursor for select * from t1; + declare x int; + + open c; + fetch c into x, y; + close c; +end| + +create procedure p() +begin + declare c cursor for select * from t1; + declare x int; + + open c; + fetch c into x; + close c; +end| +--error 1304 +call p()| +drop procedure p| + +create procedure p() +begin + declare c cursor for select * from t1; + declare x int; + declare y float; + declare z int; + + open c; + fetch c into x, y, z; + close c; +end| +--error 1304 +call p()| +drop procedure p| + +drop table t1| + delimiter ;| diff --git a/mysql-test/t/sp.test b/mysql-test/t/sp.test index 43deb12b379..724180c65bf 100644 --- a/mysql-test/t/sp.test +++ b/mysql-test/t/sp.test @@ -588,7 +588,38 @@ select * from t1| delete from t1| drop procedure hndlr3| +# +# Cursors +# +create procedure cur1() +begin + declare done int default 0; + declare continue handler for 1305 set done = 1; + declare c cursor for select * from test.t2; + declare a char(16); + declare b int; + declare c double; + + open c; + repeat + fetch c into a, b, c; + if not done then + insert into test.t1 values (a, b+c); + end if; + until done end repeat; + close c; +end| +insert into t2 values ("foo", 42, -1.9), ("bar", 3, 12.1), ("zap", 666, -3.14)| +call cur1()| +select * from t1| +delete from t1| +delete from t2| +drop procedure cur1| + +# +# BUG#822 +# create procedure bug822(a_id char(16), a_data int) begin declare n int; diff --git a/sql/protocol.cc b/sql/protocol.cc index 35a299e37bf..2ad12e56ff7 100644 --- a/sql/protocol.cc +++ b/sql/protocol.cc @@ -1145,12 +1145,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/share/czech/errmsg.txt b/sql/share/czech/errmsg.txt index d572b3e4b29..b21a65342aa 100644 --- a/sql/share/czech/errmsg.txt +++ b/sql/share/czech/errmsg.txt @@ -310,3 +310,11 @@ character-set=latin2 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/danish/errmsg.txt b/sql/share/danish/errmsg.txt index c83c6ce000e..7b2c114d456 100644 --- a/sql/share/danish/errmsg.txt +++ b/sql/share/danish/errmsg.txt @@ -304,3 +304,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/dutch/errmsg.txt b/sql/share/dutch/errmsg.txt index 7001dc859b9..13b2802b8ba 100644 --- a/sql/share/dutch/errmsg.txt +++ b/sql/share/dutch/errmsg.txt @@ -312,3 +312,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/english/errmsg.txt b/sql/share/english/errmsg.txt index 3936957fad8..3e58da13cf2 100644 --- a/sql/share/english/errmsg.txt +++ b/sql/share/english/errmsg.txt @@ -301,3 +301,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/estonian/errmsg.txt b/sql/share/estonian/errmsg.txt index 7d85d53b9ce..3a30c65cd58 100644 --- a/sql/share/estonian/errmsg.txt +++ b/sql/share/estonian/errmsg.txt @@ -306,3 +306,11 @@ character-set=latin7 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/french/errmsg.txt b/sql/share/french/errmsg.txt index a3dcdea06c0..40edf256b57 100644 --- a/sql/share/french/errmsg.txt +++ b/sql/share/french/errmsg.txt @@ -301,3 +301,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/german/errmsg.txt b/sql/share/german/errmsg.txt index 59735b8bb5d..66df9eb519d 100644 --- a/sql/share/german/errmsg.txt +++ b/sql/share/german/errmsg.txt @@ -310,3 +310,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/greek/errmsg.txt b/sql/share/greek/errmsg.txt index bcfd619a0a1..4777d09df46 100644 --- a/sql/share/greek/errmsg.txt +++ b/sql/share/greek/errmsg.txt @@ -301,3 +301,11 @@ character-set=greek "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/hungarian/errmsg.txt b/sql/share/hungarian/errmsg.txt index c4a4cdb1fb6..b3aeb507016 100644 --- a/sql/share/hungarian/errmsg.txt +++ b/sql/share/hungarian/errmsg.txt @@ -303,3 +303,11 @@ character-set=latin2 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/italian/errmsg.txt b/sql/share/italian/errmsg.txt index 3e1034c8c4a..5b83f699791 100644 --- a/sql/share/italian/errmsg.txt +++ b/sql/share/italian/errmsg.txt @@ -301,3 +301,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/japanese/errmsg.txt b/sql/share/japanese/errmsg.txt index eb2e456fa66..5916c5f8a32 100644 --- a/sql/share/japanese/errmsg.txt +++ b/sql/share/japanese/errmsg.txt @@ -303,3 +303,11 @@ character-set=ujis "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/korean/errmsg.txt b/sql/share/korean/errmsg.txt index 82f55bb7b02..154ed0d9b6f 100644 --- a/sql/share/korean/errmsg.txt +++ b/sql/share/korean/errmsg.txt @@ -301,3 +301,11 @@ character-set=euckr "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/norwegian-ny/errmsg.txt b/sql/share/norwegian-ny/errmsg.txt index 74b5fbfbb65..4ff587bcee1 100644 --- a/sql/share/norwegian-ny/errmsg.txt +++ b/sql/share/norwegian-ny/errmsg.txt @@ -303,3 +303,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/norwegian/errmsg.txt b/sql/share/norwegian/errmsg.txt index ac08124cf26..b8a47078bcc 100644 --- a/sql/share/norwegian/errmsg.txt +++ b/sql/share/norwegian/errmsg.txt @@ -303,3 +303,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/polish/errmsg.txt b/sql/share/polish/errmsg.txt index 86dd51da336..546d24adc9f 100644 --- a/sql/share/polish/errmsg.txt +++ b/sql/share/polish/errmsg.txt @@ -305,3 +305,11 @@ character-set=latin2 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/portuguese/errmsg.txt b/sql/share/portuguese/errmsg.txt index 6e55a8c071e..69d573b53af 100644 --- a/sql/share/portuguese/errmsg.txt +++ b/sql/share/portuguese/errmsg.txt @@ -302,3 +302,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/romanian/errmsg.txt b/sql/share/romanian/errmsg.txt index 50521fbef94..af7b26c9ef1 100644 --- a/sql/share/romanian/errmsg.txt +++ b/sql/share/romanian/errmsg.txt @@ -305,3 +305,11 @@ character-set=latin2 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/russian/errmsg.txt b/sql/share/russian/errmsg.txt index f4ef5415136..435c9c5193a 100644 --- a/sql/share/russian/errmsg.txt +++ b/sql/share/russian/errmsg.txt @@ -303,3 +303,11 @@ character-set=koi8r "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/serbian/errmsg.txt b/sql/share/serbian/errmsg.txt index 83adc33c73b..2c7dc25e0ac 100644 --- a/sql/share/serbian/errmsg.txt +++ b/sql/share/serbian/errmsg.txt @@ -296,3 +296,11 @@ character-set=cp1250 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/slovak/errmsg.txt b/sql/share/slovak/errmsg.txt index fc4cb769bc8..3596b73682b 100644 --- a/sql/share/slovak/errmsg.txt +++ b/sql/share/slovak/errmsg.txt @@ -309,3 +309,11 @@ character-set=latin2 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/spanish/errmsg.txt b/sql/share/spanish/errmsg.txt index 129df358d2e..d24153d29d2 100644 --- a/sql/share/spanish/errmsg.txt +++ b/sql/share/spanish/errmsg.txt @@ -303,3 +303,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/swedish/errmsg.txt b/sql/share/swedish/errmsg.txt index e17e20aa5f5..3931ce9718e 100644 --- a/sql/share/swedish/errmsg.txt +++ b/sql/share/swedish/errmsg.txt @@ -301,3 +301,11 @@ character-set=latin1 "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/share/ukrainian/errmsg.txt b/sql/share/ukrainian/errmsg.txt index cb29db0330e..0f69481aefe 100644 --- a/sql/share/ukrainian/errmsg.txt +++ b/sql/share/ukrainian/errmsg.txt @@ -306,3 +306,11 @@ character-set=koi8u "Undefined CONDITION: %s" "No RETURN found in FUNCTION %s" "FUNCTION %s ended without RETURN" +"Cursor statement must be a SELECT" +"Cursor SELECT must not have INTO" +"Undefined CURSOR: %s" +"Cursor is already open" +"Cursor is not open" +"Undeclared variable: %s" +"Wrong number of FETCH variables" +"No data to FETCH" diff --git a/sql/sp_head.cc b/sql/sp_head.cc index 82afb9305f0..bea4d7a34be 100644 --- a/sql/sp_head.cc +++ b/sql/sp_head.cc @@ -289,6 +289,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) 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; @@ -304,7 +305,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) } // QQ Should have some error checking here? (types, etc...) - nctx= new sp_rcontext(csize, hmax); + nctx= new sp_rcontext(csize, hmax, cmax); for (i= 0 ; i < params && i < argcount ; i++) { sp_pvar_t *pvar = m_pcont->find_pvar(i); @@ -335,6 +336,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount, Item **resp) } } + nctx->pop_all_cursors(); // To avoid memory leaks after an error thd->spcont= octx; DBUG_RETURN(ret); } @@ -349,6 +351,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) 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 @@ -360,17 +363,17 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) DBUG_RETURN(-1); } - if (csize > 0 || hmax > 0) + if (csize > 0 || hmax > 0 || cmax > 0) { uint i; List_iterator_fast<Item> li(*args); Item *it; - nctx = new sp_rcontext(csize, hmax); + nctx= new sp_rcontext(csize, hmax, cmax); if (! octx) { // Create a temporary old context - octx = new sp_rcontext(csize, hmax); - tmp_octx = TRUE; + octx= new sp_rcontext(csize, hmax, cmax); + tmp_octx= TRUE; } // QQ: Should do type checking? for (i = 0 ; (it= li++) && i < params ; i++) @@ -443,13 +446,14 @@ sp_head::execute_procedure(THD *thd, List<Item> *args) } } } - - if (tmp_octx) - thd->spcont= NULL; - else - thd->spcont= octx; } + if (tmp_octx) + octx= NULL; + if (nctx) + nctx->pop_all_cursors(); // To avoid memory leaks after an error + thd->spcont= octx; + DBUG_RETURN(ret); } @@ -596,12 +600,20 @@ 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= m_lex; // Use my own 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; @@ -610,10 +622,19 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) // Copy WHERE clause pointers to avoid damaging by optimisation // Also clear ref_pointer_arrays. - for (SELECT_LEX *sl= m_lex->all_selects_list ; + for (SELECT_LEX *sl= lex->all_selects_list ; sl ; sl= sl->next_select_in_list()) { + List_iterator_fast<Item> li(sl->item_list); + + if (sl->with_wild) + { + // Copy item_list + sl->item_list_copy.empty(); + while (Item *it= li++) + sl->item_list_copy.push_back(it); + } sl->ref_pointer_array= 0; if (sl->prep_where) sl->where= sl->prep_where->copy_andor_structure(thd); @@ -628,11 +649,22 @@ sp_instr_stmt::execute(THD *thd, uint *nextp) close_thread_tables(thd); /* Free tables */ } + for (SELECT_LEX *sl= lex->all_selects_list ; + sl ; + sl= sl->next_select_in_list()) + { + if (sl->with_wild) + { + // Restore item_list + sl->item_list.empty(); + while (Item *it= sl->item_list_copy.pop()) + sl->item_list.push_back(it); + } + } thd->lex= olex; // Restore the other lex thd->free_list= freelist; - *nextp = m_ip+1; - DBUG_RETURN(res); + return res; } // @@ -747,3 +779,96 @@ sp_instr_hreturn::execute(THD *thd, uint *nextp) *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 index a68a1bf83ef..b582d37a185 100644 --- a/sql/sp_head.h +++ b/sql/sp_head.h @@ -33,10 +33,9 @@ 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 { @@ -278,6 +277,10 @@ public: return m_lex; } +protected: + + int exec_stmt(THD *thd, LEX *lex); // Execute a statement + private: LEX *m_lex; // My own lex @@ -503,4 +506,126 @@ private: }; // 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 index a192d78b9a3..3730230d47d 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -27,10 +27,11 @@ #include "sp_head.h" sp_pcontext::sp_pcontext() - : Sql_alloc(), m_params(0), m_framesize(0), m_handlers(0), m_genlab(0) + : Sql_alloc(), m_params(0), m_framesize(0), m_handlers(0), m_cursmax(0) { VOID(my_init_dynamic_array(&m_pvar, sizeof(sp_pvar_t *), 16, 8)); VOID(my_init_dynamic_array(&m_cond, sizeof(sp_cond_type_t *), 16, 8)); + VOID(my_init_dynamic_array(&m_cursor, sizeof(LEX_STRING), 16, 8)); m_label.empty(); } @@ -39,6 +40,7 @@ sp_pcontext::destroy() { delete_dynamic(&m_pvar); delete_dynamic(&m_cond); + delete_dynamic(&m_cursor); m_label.empty(); } @@ -124,8 +126,6 @@ sp_pcontext::push_cond(LEX_STRING *name, sp_cond_type_t *val) if (p) { - if (m_cond.elements == m_framesize) - m_framesize += 1; p->name.str= name->str; p->name.length= name->length; p->val= val; @@ -155,3 +155,39 @@ sp_pcontext::find_cond(LEX_STRING *name) } return NULL; } + +void +sp_pcontext::push_cursor(LEX_STRING *name) +{ + LEX_STRING n; + + n.str= name->str; + n.length= name->length; + insert_dynamic(&m_cursor, (gptr)&n); + if (m_cursor.elements > m_cursmax) + m_cursmax= m_cursor.elements; +} + +/* + * See comment for find_pvar() above + */ +my_bool +sp_pcontext::find_cursor(LEX_STRING *name, uint *poff) +{ + uint i = m_cursor.elements; + + while (i-- > 0) + { + LEX_STRING n; + + get_dynamic(&m_cursor, (gptr)&n, i); + if (my_strnncoll(system_charset_info, + (const uchar *)name->str, name->length, + (const uchar *)n.str, n.length) == 0) + { + *poff= i; + return TRUE; + } + } + return FALSE; +} diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h index 6fb56faccf6..23be38edcbf 100644 --- a/sql/sp_pcontext.h +++ b/sql/sp_pcontext.h @@ -29,7 +29,7 @@ typedef enum sp_param_inout } sp_param_mode_t; -typedef struct +typedef struct sp_pvar { LEX_STRING name; enum enum_field_types type; @@ -200,17 +200,41 @@ class sp_pcontext : public Sql_alloc return m_handlers; } + // + // Cursors + // + + void + push_cursor(LEX_STRING *name); + + my_bool + find_cursor(LEX_STRING *name, uint *poff); + + inline void + pop_cursor(uint num) + { + while (num--) + pop_dynamic(&m_cursor); + } + + inline uint + cursors() + { + return m_cursmax; + } + private: uint m_params; // The number of parameters uint m_framesize; // The maximum framesize uint m_handlers; // The total number of handlers + uint m_cursmax; // The maximum number of cursors DYNAMIC_ARRAY m_pvar; // Parameters/variables DYNAMIC_ARRAY m_cond; // Conditions + DYNAMIC_ARRAY m_cursor; // Cursors List<sp_label_t> m_label; // The label list - uint m_genlab; // Gen. label counter }; // class sp_pcontext : public Sql_alloc diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 79d02d843ea..d73f3ed6dd7 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -23,16 +23,20 @@ #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) - : m_count(0), m_fsize(fsize), m_result(NULL), m_hcount(0), m_hsp(0) +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(); } @@ -93,3 +97,141 @@ sp_rcontext::restore_variables(uint fp) 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 index fe954ed0d94..027f2f74789 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -23,6 +23,8 @@ #endif struct sp_cond_type; +struct sp_cursor; +struct sp_pvar; #define SP_HANDLER_NONE 0 #define SP_HANDLER_EXIT 1 @@ -44,7 +46,7 @@ class sp_rcontext : public Sql_alloc public: - sp_rcontext(uint fsize, uint hmax); + sp_rcontext(uint fsize, uint hmax, uint cmax); ~sp_rcontext() { @@ -155,22 +157,93 @@ class sp_rcontext : public Sql_alloc 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_lex.h b/sql/sql_lex.h index 99a58f5fbf9..c836ff1ba38 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -348,6 +348,7 @@ public: enum olap_type olap; SQL_LIST table_list, group_list; /* FROM & GROUP BY clauses */ List<Item> item_list; /* list of fields & expressions */ + List<Item> item_list_copy; /* For SPs */ List<String> interval_list, use_index, *use_index_ptr, ignore_index, *ignore_index_ptr; /* diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index c9fb5e0db41..58c1680a5f3 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -86,7 +86,8 @@ inline Item *or_or_concat(THD *thd, Item* A, Item* B) st_select_lex *select_lex; chooser_compare_func_creator boolfunc2creator; struct sp_cond_type *spcondtype; - struct { int vars, conds, hndlrs; } spblock; + struct { int vars, conds, hndlrs, curs; } spblock; + struct st_lex *lex; } %{ @@ -748,6 +749,7 @@ END_OF_INPUT %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> '-' '+' '*' '/' '%' '(' ')' @@ -1189,13 +1191,14 @@ sp_proc_stmts: sp_decls: /* Empty */ { - $$.vars= $$.conds= $$.hndlrs= 0; + $$.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; } ; @@ -1222,12 +1225,12 @@ sp_decl: } } $$.vars= $2; - $$.conds= $$.hndlrs= 0; + $$.conds= $$.hndlrs= $$.curs= 0; } | DECLARE_SYM ident CONDITION_SYM FOR_SYM sp_cond { YYTHD->lex->spcont->push_cond(&$2, $5); - $$.vars= $$.hndlrs= 0; + $$.vars= $$.hndlrs= $$.curs= 0; $$.conds= 1; } | DECLARE_SYM sp_handler_type HANDLER_SYM FOR_SYM @@ -1260,14 +1263,49 @@ sp_decl: sp->push_backpatch(i, lex->spcont->last_label()); /* Block end */ } lex->sphead->backpatch(hlab); - $$.vars= $$.conds= 0; + $$.vars= $$.conds= $$.curs= 0; $$.hndlrs= $6; } -/* QQ Not yet | DECLARE_SYM ident CURSOR_SYM FOR_SYM sp_cursor_stmt { + LEX *lex= Lex; + sp_head *sp= lex->sphead; + sp_instr_cpush *i= new sp_instr_cpush(sp->instructions(), $5); + + sp->add_instr(i); + lex->spcont->push_cursor(&$2); $$.vars= $$.conds= $$.hndlrs= 0; - }*/ + $$.curs= 1; + } + ; + +sp_cursor_stmt: + { + Lex->sphead->reset_lex(YYTHD); + + /* We use statement here just be able to get a better + error message. Using 'select' works too, but will then + result in a generic "syntax error" if a non-select + statement is given. */ + } + statement + { + LEX *lex= Lex; + + if (lex->sql_command != SQLCOM_SELECT) + { + send_error(YYTHD, ER_SP_BAD_CURSOR_QUERY); + YYABORT; + } + if (lex->result) + { + send_error(YYTHD, ER_SP_BAD_CURSOR_SELECT); + YYABORT; + } + lex->sp_lex_in_use= TRUE; + $$= lex; + lex->sphead->restore_lex(YYTHD); + } ; sp_handler_type: @@ -1506,11 +1544,96 @@ sp_proc_stmt: } } | OPEN_SYM ident - {} - | FETCH_SYM ident INTO select_var_list_init - {} + { + 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: @@ -1649,11 +1772,11 @@ sp_unlabeled_control: sp->backpatch(ctx->pop_label()); ctx->pop_pvar($3.vars); ctx->pop_cond($3.conds); + ctx->pop_cursor($3.curs); if ($3.hndlrs) - { - sp_instr_hpop *i= new sp_instr_hpop(sp->instructions(),$3.hndlrs); - sp->add_instr(i); - } + sp->add_instr(new sp_instr_hpop(sp->instructions(),$3.hndlrs)); + if ($3.curs) + sp->add_instr(new sp_instr_cpop(sp->instructions(), $3.curs)); } | LOOP_SYM sp_proc_stmts END LOOP_SYM @@ -4206,7 +4329,7 @@ select_var_ident: sp_pvar_t *t; if (!(t=lex->spcont->find_pvar(&$1))) { - send_error(lex->thd, ER_SYNTAX_ERROR); + send_error(lex->thd, ER_SP_UNDECLARED_VAR); YYABORT; } if (! lex->result) |