diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-09-27 10:13:08 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:51 +0400 |
commit | 76714a5c9a4c05fa7084f2c562a9eb50a0b7bd17 (patch) | |
tree | fc0e93ef9195d89d4079c8608e102012deb3b5e7 | |
parent | 4bb87996b915a9383c7bf33c8683f128d3791014 (diff) | |
download | mariadb-git-76714a5c9a4c05fa7084f2c562a9eb50a0b7bd17.tar.gz |
MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND
-rw-r--r-- | mysql-test/r/keywords.result | 4 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/sp-cursor.result | 291 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/sp-cursor.test | 296 | ||||
-rw-r--r-- | mysql-test/t/keywords.test | 6 | ||||
-rw-r--r-- | sql/item_func.cc | 48 | ||||
-rw-r--r-- | sql/item_func.h | 70 | ||||
-rw-r--r-- | sql/lex.h | 2 | ||||
-rw-r--r-- | sql/sp_pcontext.cc | 4 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 13 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 14 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 4 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 58 |
12 files changed, 800 insertions, 10 deletions
diff --git a/mysql-test/r/keywords.result b/mysql-test/r/keywords.result index 231da5a86b6..07d7ae233e5 100644 --- a/mysql-test/r/keywords.result +++ b/mysql-test/r/keywords.result @@ -350,3 +350,7 @@ CREATE TABLE decode (decode int); DROP TABLE decode; CREATE TABLE rowcount (rowcount int); DROP TABLE rowcount; +CREATE TABLE isopen (isopen int); +DROP TABLE isopen; +CREATE TABLE notfound (notfound int); +DROP TABLE notfound; diff --git a/mysql-test/suite/compat/oracle/r/sp-cursor.result b/mysql-test/suite/compat/oracle/r/sp-cursor.result new file mode 100644 index 00000000000..1458d820f78 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/sp-cursor.result @@ -0,0 +1,291 @@ +SET sql_mode=ORACLE; +# +# MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND +# +# +# Cursor attributes outside of an SP context +# +SELECT c%ISOPEN; +ERROR 42000: Undefined CURSOR: c +SELECT c%FOUND; +ERROR 42000: Undefined CURSOR: c +SELECT c%NOTFOUND; +ERROR 42000: Undefined CURSOR: c +SELECT c%ROWCOUNT; +ERROR 42000: Undefined CURSOR: c +# +# Undefinite cursor attributes +# +CREATE PROCEDURE p1 +AS +BEGIN +SELECT c%ISOPEN; +END; +$$ +ERROR 42000: Undefined CURSOR: c +CREATE PROCEDURE p1 +AS +BEGIN +SELECT c%ROWCOUNT; +END; +$$ +ERROR 42000: Undefined CURSOR: c +CREATE PROCEDURE p1 +AS +BEGIN +SELECT c%FOUND; +END; +$$ +ERROR 42000: Undefined CURSOR: c +CREATE PROCEDURE p1 +AS +BEGIN +SELECT c%NOTFOUND; +END; +$$ +ERROR 42000: Undefined CURSOR: c +# +# Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT +# +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%ROWCOUNT; +END; +$$ +CALL p1; +ERROR 24000: Cursor is not open +DROP PROCEDURE p1; +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%FOUND; +END; +$$ +CALL p1; +ERROR 24000: Cursor is not open +DROP PROCEDURE p1; +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%NOTFOUND; +END; +$$ +CALL p1; +ERROR 24000: Cursor is not open +DROP PROCEDURE p1; +# +# Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT with INVALID_CURSOR exception +# +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%ROWCOUNT; +EXCEPTION +WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +CALL p1; +c%ROWCOUNT +msg +INVALID_CURSOR caught +DROP PROCEDURE p1; +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%FOUND; +EXCEPTION +WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +CALL p1; +c%FOUND +msg +INVALID_CURSOR caught +DROP PROCEDURE p1; +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN +SELECT c%NOTFOUND; +EXCEPTION +WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +CALL p1; +c%NOTFOUND +msg +INVALID_CURSOR caught +DROP PROCEDURE p1; +# +# print() +# +CREATE TABLE t1 (a INT); +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN +EXPLAIN EXTENDED SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +END; +$$ +CALL p1(); +id select_type table type possible_keys key key_len ref rows filtered Extra +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used +Warnings: +Note 1003 select "c"%ISOPEN AS "c%ISOPEN","c"%ROWCOUNT AS "c%ROWCOUNT","c"%FOUND AS "c%FOUND","c"%NOTFOUND AS "c%NOTFOUND" +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Declared data type of the attributes +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +CREATE PROCEDURE p1 +AS +CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN +OPEN c; +CREATE TABLE t2 AS SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +SHOW CREATE TABLE t2; +DROP TABLE t2; +CLOSE c; +END; +$$ +CALL p1(); +Table Create Table +t2 CREATE TABLE "t2" ( + "c%ISOPEN" int(1) NOT NULL, + "c%ROWCOUNT" bigint(21) NOT NULL, + "c%FOUND" int(1) DEFAULT NULL, + "c%NOTFOUND" int(1) DEFAULT NULL +) +DROP PROCEDURE p1; +DROP TABLE t1; +# +# Core functionality +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +CREATE PROCEDURE p1 +AS +a INT:=0; +CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN +SELECT a, c%ISOPEN; +OPEN c; +/* +After OPEN and before FETCH: +- %ROWCOUNT returns 0 +- %FOUND and %NOTFOUND return NULL +*/ +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +FETCH c INTO a; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +FETCH c INTO a; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +FETCH c INTO a; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +FETCH c INTO a; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +CLOSE c; +SELECT a, c%ISOPEN; +/* +After reopen and before FETCH: +- %ROWCOUNT returns 0 +- %FOUND and %NOTFOUND return NULL +*/ +OPEN c; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +FETCH c INTO a; +SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +CLOSE c; +END; +$$ +CALL p1(); +a c%ISOPEN +0 0 +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +0 1 0 NULL NULL +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +10 1 1 1 0 +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +20 1 2 1 0 +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +30 1 3 1 0 +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +30 1 3 0 1 +a c%ISOPEN +30 0 +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +30 1 0 NULL NULL +a c%ISOPEN c%ROWCOUNT c%FOUND c%NOTFOUND +10 1 1 1 0 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# %NOTFOUND as a loop exit condition +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +CREATE PROCEDURE p1 +AS +a INT:=0; +CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN +OPEN c; +LOOP +FETCH c INTO a; +EXIT WHEN c%NOTFOUND; +SELECT a; +END LOOP; +CLOSE c; +END; +$$ +CALL p1(); +a +10 +a +20 +a +30 +DROP PROCEDURE p1; +DROP TABLE t1; +# +# %FOUND as a loop exit condition +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +CREATE PROCEDURE p1 +AS +a INT:=0; +CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN +OPEN c; +LOOP +FETCH c INTO a; +EXIT WHEN NOT c%FOUND; +SELECT a; +END LOOP; +CLOSE c; +END; +$$ +CALL p1(); +a +10 +a +20 +a +30 +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/t/sp-cursor.test b/mysql-test/suite/compat/oracle/t/sp-cursor.test new file mode 100644 index 00000000000..e69ae5d85a8 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/sp-cursor.test @@ -0,0 +1,296 @@ +SET sql_mode=ORACLE; + +--echo # +--echo # MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND +--echo # + + +--echo # +--echo # Cursor attributes outside of an SP context +--echo # + +--error ER_SP_CURSOR_MISMATCH +SELECT c%ISOPEN; +--error ER_SP_CURSOR_MISMATCH +SELECT c%FOUND; +--error ER_SP_CURSOR_MISMATCH +SELECT c%NOTFOUND; +--error ER_SP_CURSOR_MISMATCH +SELECT c%ROWCOUNT; + + +--echo # +--echo # Undefinite cursor attributes +--echo # + +DELIMITER $$; +--error ER_SP_CURSOR_MISMATCH +CREATE PROCEDURE p1 +AS +BEGIN + SELECT c%ISOPEN; +END; +$$ +--error ER_SP_CURSOR_MISMATCH +CREATE PROCEDURE p1 +AS +BEGIN + SELECT c%ROWCOUNT; +END; +$$ +--error ER_SP_CURSOR_MISMATCH +CREATE PROCEDURE p1 +AS +BEGIN + SELECT c%FOUND; +END; +$$ +--error ER_SP_CURSOR_MISMATCH +CREATE PROCEDURE p1 +AS +BEGIN + SELECT c%NOTFOUND; +END; +$$ +DELIMITER ;$$ + + +--echo # +--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%ROWCOUNT; +END; +$$ +DELIMITER ;$$ +--error ER_SP_CURSOR_NOT_OPEN +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%FOUND; +END; +$$ +DELIMITER ;$$ +--error ER_SP_CURSOR_NOT_OPEN +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%NOTFOUND; +END; +$$ +DELIMITER ;$$ +--error ER_SP_CURSOR_NOT_OPEN +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # Not opened cursor attributes %FOUND, %NOTFOUND, %ROWCOUNT with INVALID_CURSOR exception +--echo # + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%ROWCOUNT; +EXCEPTION + WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%FOUND; +EXCEPTION + WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT 1 AS c FROM DUAL; +BEGIN + SELECT c%NOTFOUND; +EXCEPTION + WHEN INVALID_CURSOR THEN SELECT 'INVALID_CURSOR caught' AS msg; +END; +$$ +DELIMITER ;$$ +CALL p1; +DROP PROCEDURE p1; + + +--echo # +--echo # print() +--echo # + +CREATE TABLE t1 (a INT); +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN + EXPLAIN EXTENDED SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Declared data type of the attributes +--echo # +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +DELIMITER $$; +CREATE PROCEDURE p1 +AS + CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN + OPEN c; + CREATE TABLE t2 AS SELECT c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + SHOW CREATE TABLE t2; + DROP TABLE t2; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # Core functionality +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +DELIMITER $$; +CREATE PROCEDURE p1 +AS + a INT:=0; + CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN + SELECT a, c%ISOPEN; + OPEN c; + /* + After OPEN and before FETCH: + - %ROWCOUNT returns 0 + - %FOUND and %NOTFOUND return NULL + */ + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + FETCH c INTO a; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + FETCH c INTO a; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + FETCH c INTO a; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + FETCH c INTO a; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + CLOSE c; + SELECT a, c%ISOPEN; + /* + After reopen and before FETCH: + - %ROWCOUNT returns 0 + - %FOUND and %NOTFOUND return NULL + */ + OPEN c; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + FETCH c INTO a; + SELECT a, c%ISOPEN, c%ROWCOUNT, c%FOUND, c%NOTFOUND; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # %NOTFOUND as a loop exit condition +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +DELIMITER $$; +CREATE PROCEDURE p1 +AS + a INT:=0; + CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN + OPEN c; + LOOP + FETCH c INTO a; + EXIT WHEN c%NOTFOUND; + SELECT a; + END LOOP; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; + + +--echo # +--echo # %FOUND as a loop exit condition +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (10); +INSERT INTO t1 VALUES (20); +INSERT INTO t1 VALUES (30); +DELIMITER $$; +CREATE PROCEDURE p1 +AS + a INT:=0; + CURSOR c IS SELECT * FROM t1 ORDER BY a; +BEGIN + OPEN c; + LOOP + FETCH c INTO a; + EXIT WHEN NOT c%FOUND; + SELECT a; + END LOOP; + CLOSE c; +END; +$$ +DELIMITER ;$$ +CALL p1(); +DROP PROCEDURE p1; +DROP TABLE t1; diff --git a/mysql-test/t/keywords.test b/mysql-test/t/keywords.test index de97aa58fd5..b61f09e4048 100644 --- a/mysql-test/t/keywords.test +++ b/mysql-test/t/keywords.test @@ -247,3 +247,9 @@ DROP TABLE decode; CREATE TABLE rowcount (rowcount int); DROP TABLE rowcount; + +CREATE TABLE isopen (isopen int); +DROP TABLE isopen; + +CREATE TABLE notfound (notfound int); +DROP TABLE notfound; diff --git a/sql/item_func.cc b/sql/item_func.cc index 4dd3a1e7da4..ad3a863fa57 100644 --- a/sql/item_func.cc +++ b/sql/item_func.cc @@ -6862,4 +6862,52 @@ void Item_func_last_value::fix_length_and_dec() } +void Item_func_cursor_int_attr::print(String *str, enum_query_type query_type) +{ + append_identifier(current_thd, str, m_cursor_name.str, m_cursor_name.length); + str->append(func_name()); +} + + +sp_cursor *Item_func_cursor_int_attr::get_open_cursor_or_error() +{ + THD *thd= current_thd; + sp_cursor *c= thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c); + if (!c/*safety*/ || !c->is_open()) + { + my_message(ER_SP_CURSOR_NOT_OPEN, ER_THD(thd, ER_SP_CURSOR_NOT_OPEN), + MYF(0)); + return NULL; + } + return c; +} + + +longlong Item_func_cursor_isopen::val_int() +{ + sp_cursor *c= current_thd->spcont->get_cursor(m_cursor_offset); + DBUG_ASSERT(c != NULL); + return c ? c->is_open() : 0; +} + + +longlong Item_func_cursor_found::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && c->found(); +} + +longlong Item_func_cursor_notfound::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= (!c || c->fetch_count() == 0)) && !c->found(); +} + + +longlong Item_func_cursor_rowcount::val_int() +{ + sp_cursor *c= get_open_cursor_or_error(); + return !(null_value= !c) ? c->row_count() : 0; +} diff --git a/sql/item_func.h b/sql/item_func.h index 0fbf76c510d..28cdb78f6dd 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -725,6 +725,76 @@ public: }; +class Item_func_cursor_int_attr: public Item_int_func +{ +protected: + LEX_STRING m_cursor_name; + uint m_cursor_offset; + class sp_cursor *get_open_cursor_or_error(); +public: + Item_func_cursor_int_attr(THD *thd, const LEX_STRING name, uint offset) + :Item_int_func(thd), m_cursor_name(name), m_cursor_offset(offset) + { } + bool check_vcol_func_processor(void *arg) + { + return mark_unsupported_function(func_name(), arg, VCOL_SESSION_FUNC); + } + void print(String *str, enum_query_type query_type); +}; + + +class Item_func_cursor_isopen: public Item_func_cursor_int_attr +{ +public: + Item_func_cursor_isopen(THD *thd, const LEX_STRING name, uint offset) + :Item_func_cursor_int_attr(thd, name, offset) { } + const char *func_name() const { return "%ISOPEN"; } + void fix_length_and_dec() { max_length= 1; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_isopen>(thd, mem_root, this); } +}; + + +class Item_func_cursor_found: public Item_func_cursor_int_attr +{ +public: + Item_func_cursor_found(THD *thd, const LEX_STRING name, uint offset) + :Item_func_cursor_int_attr(thd, name, offset) { } + const char *func_name() const { return "%FOUND"; } + void fix_length_and_dec() { max_length= 1; maybe_null= true; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_found>(thd, mem_root, this); } +}; + + +class Item_func_cursor_notfound: public Item_func_cursor_int_attr +{ +public: + Item_func_cursor_notfound(THD *thd, const LEX_STRING name, uint offset) + :Item_func_cursor_int_attr(thd, name, offset) { } + const char *func_name() const { return "%NOTFOUND"; } + void fix_length_and_dec() { max_length= 1; maybe_null= true; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_notfound>(thd, mem_root, this); } +}; + + +class Item_func_cursor_rowcount: public Item_func_cursor_int_attr +{ +public: + Item_func_cursor_rowcount(THD *thd, const LEX_STRING name, uint offset) + :Item_func_cursor_int_attr(thd, name, offset) { } + const char *func_name() const { return "%ROWCOUNT"; } + longlong val_int(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_cursor_rowcount>(thd, mem_root, this); } +}; + + + class Item_func_connection_id :public Item_int_func { longlong value; diff --git a/sql/lex.h b/sql/lex.h index 38b0e2ab68c..08bcba3beee 100644 --- a/sql/lex.h +++ b/sql/lex.h @@ -310,6 +310,7 @@ static SYMBOL symbols[] = { { "IPC", SYM(IPC_SYM)}, { "IS", SYM(IS)}, { "ISOLATION", SYM(ISOLATION)}, + { "ISOPEN", SYM(ISOPEN_SYM)}, { "ISSUER", SYM(ISSUER_SYM)}, { "ITERATE", SYM(ITERATE_SYM)}, { "INVOKER", SYM(INVOKER_SYM)}, @@ -415,6 +416,7 @@ static SYMBOL symbols[] = { { "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NONE", SYM(NONE_SYM)}, { "NOT", SYM(NOT_SYM)}, + { "NOTFOUND", SYM(NOTFOUND_SYM)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NULL", SYM(NULL_SYM)}, { "NUMBER", SYM(NUMBER_SYM)}, diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc index 6b796cb95e9..daf8a6febd0 100644 --- a/sql/sp_pcontext.cc +++ b/sql/sp_pcontext.cc @@ -289,13 +289,15 @@ sp_condition_value *sp_pcontext::find_condition(const LEX_STRING name, static sp_condition_value + cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN), cond_no_data_found(ER_SP_FETCH_NO_DATA), cond_dup_val_on_index(ER_DUP_ENTRY), cond_too_many_rows(ER_TOO_MANY_ROWS); -static sp_condition sp_predefined_conditions[3]= +static sp_condition sp_predefined_conditions[]= { + sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor), sp_condition(C_STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found), sp_condition(C_STRING_WITH_LEN("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index), sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc index 8873b87d989..ff5ecc5e6c6 100644 --- a/sql/sp_rcontext.cc +++ b/sql/sp_rcontext.cc @@ -425,7 +425,10 @@ sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i) result(thd_arg), m_lex_keeper(lex_keeper), server_side_cursor(NULL), - m_i(i) + m_i(i), + m_fetch_count(0), + m_row_count(0), + m_found(false) { /* currsor can't be stored in QC, so we should prevent opening QC for @@ -470,6 +473,8 @@ int sp_cursor::close(THD *thd) MYF(0)); return -1; } + m_row_count= m_fetch_count= 0; + m_found= false; destroy(); return 0; } @@ -497,6 +502,7 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) return -1; } + m_fetch_count++; DBUG_EXECUTE_IF("bug23032_emit_warning", push_warning(thd, Sql_condition::WARN_LEVEL_WARN, ER_UNKNOWN_ERROR, @@ -514,10 +520,15 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) */ if (! server_side_cursor->is_open()) { + m_found= false; + if (thd->variables.sql_mode & MODE_ORACLE) + return 0; my_message(ER_SP_FETCH_NO_DATA, ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0)); return -1; } + m_found= true; + m_row_count++; return 0; } diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h index 2640490fefa..b6b0d4de0c3 100644 --- a/sql/sp_rcontext.h +++ b/sql/sp_rcontext.h @@ -452,6 +452,15 @@ public: my_bool is_open() { return MY_TEST(server_side_cursor); } + bool found() const + { return m_found; } + + ulonglong row_count() const + { return m_row_count; } + + ulonglong fetch_count() const + { return m_fetch_count; } + int fetch(THD *, List<sp_variable> *vars); sp_instr_cpush *get_instr() @@ -461,7 +470,10 @@ private: Select_fetch_into_spvars result; sp_lex_keeper *m_lex_keeper; Server_side_cursor *server_side_cursor; - sp_instr_cpush *m_i; // My push instruction + sp_instr_cpush *m_i; // My push instruction + ulonglong m_fetch_count; // Number of FETCH commands since last OPEN + ulonglong m_row_count; // Number of successful FETCH since last OPEN + bool m_found; // If last FETCH fetched a row void destroy(); }; // class sp_cursor : public Sql_alloc diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index d93cbd7ed61..3a63b8467e9 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1181,6 +1181,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IPC_SYM %token IS /* SQL-2003-R */ %token ISOLATION /* SQL-2003-R */ +%token ISOPEN_SYM /* Oracle-N */ %token ISSUER_SYM %token ITERATE_SYM %token JOIN_SYM /* SQL-2003-R */ @@ -1290,6 +1291,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM %token NOT_SYM /* SQL-2003-R */ +%token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ %token NO_WAIT_SYM @@ -14346,6 +14348,7 @@ keyword_sp: | IO_SYM {} | IPC_SYM {} | ISOLATION {} + | ISOPEN_SYM {} | ISSUER_SYM {} | JSON_SYM {} | INSERT_METHOD {} @@ -14415,6 +14418,7 @@ keyword_sp: | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} + | NOTFOUND_SYM {} | NUMBER_SYM {} | NVARCHAR_SYM {} | OFFSET_SYM {} diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index d379991613b..7ab03be7617 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -178,6 +178,11 @@ void ORAerror(THD *thd, const char *s) Lex_field_type_st Lex_field_type; Lex_dyncol_type_st Lex_dyncol_type; Lex_for_loop_st for_loop; + struct + { + LEX_STRING name; + uint offset; + } sp_cursor_name_and_offset; /* pointers */ Create_field *create_field; @@ -555,6 +560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token IPC_SYM %token IS /* SQL-2003-R */ %token ISOLATION /* SQL-2003-R */ +%token ISOPEN_SYM /* Oracle-N */ %token ISSUER_SYM %token ITERATE_SYM %token JOIN_SYM /* SQL-2003-R */ @@ -664,6 +670,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %token NONE_SYM /* SQL-2003-R */ %token NOT2_SYM %token NOT_SYM /* SQL-2003-R */ +%token NOTFOUND_SYM /* Oracle-R */ %token NOW_SYM %token NO_SYM /* SQL-2003-R */ %token NO_WAIT_SYM @@ -975,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %left '&' %left SHIFT_LEFT SHIFT_RIGHT %left '-' '+' -%left '*' '/' '%' DIV_SYM MOD_SYM +%left '*' '/' DIV_SYM MOD_SYM %left '^' %left NEG '~' %right NOT_SYM NOT2_SYM @@ -1103,6 +1110,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); window_func_expr window_func simple_window_func + explicit_cursor_attr function_call_keyword function_call_nonkeyword function_call_generic @@ -1291,6 +1299,7 @@ END_OF_INPUT %type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list %type <spblock_handlers> sp_block_statements_and_exceptions %type <sp_instr_addr> sp_instr_addr +%type <sp_cursor_name_and_offset> sp_cursor_name_and_offset %type <num> opt_exception_clause exception_handlers %type <lex> sp_cursor_stmt %type <spname> sp_name @@ -8598,12 +8607,6 @@ bit_expr: if ($$ == NULL) MYSQL_YYABORT; } - | bit_expr '%' bit_expr %prec '%' - { - $$= new (thd->mem_root) Item_func_mod(thd, $1, $3); - if ($$ == NULL) - MYSQL_YYABORT; - } | bit_expr DIV_SYM bit_expr %prec DIV_SYM { $$= new (thd->mem_root) Item_func_int_div(thd, $1, $3); @@ -8740,6 +8743,44 @@ dyncall_create_list: } ; +sp_cursor_name_and_offset: + ident + { + LEX *lex= Lex; + $$.name= $1; + if (!lex->spcont || + !lex->spcont->find_cursor($1, &$$.offset, false)) + my_yyabort_error((ER_SP_CURSOR_MISMATCH, MYF(0), $1.str)); + } + ; + +explicit_cursor_attr: + sp_cursor_name_and_offset '%' ISOPEN_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_isopen(thd, $1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' FOUND_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_found(thd, $1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' NOTFOUND_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_notfound(thd, $1.name, $1.offset))) + MYSQL_YYABORT; + } + | sp_cursor_name_and_offset '%' ROWCOUNT_SYM + { + if (!($$= new (thd->mem_root) + Item_func_cursor_rowcount(thd, $1.name, $1.offset))) + MYSQL_YYABORT; + } + ; + /* Expressions that the parser allows in a column DEFAULT clause without parentheses. These expressions cannot end with a COLLATE clause. @@ -8904,6 +8945,7 @@ column_default_non_parenthesized_expr: simple_expr: column_default_non_parenthesized_expr + | explicit_cursor_attr | simple_expr COLLATE_SYM ident_or_text %prec NEG { Item *i1= new (thd->mem_root) Item_string(thd, $3.str, @@ -14306,6 +14348,7 @@ keyword_sp: | IO_SYM {} | IPC_SYM {} | ISOLATION {} + | ISOPEN_SYM {} | ISSUER_SYM {} | JSON_SYM {} | INSERT_METHOD {} @@ -14375,6 +14418,7 @@ keyword_sp: | NO_WAIT_SYM {} | NODEGROUP_SYM {} | NONE_SYM {} + | NOTFOUND_SYM {} | NUMBER_SYM {} | NVARCHAR_SYM {} | OFFSET_SYM {} |