Commit 76714a5c authored by Alexander Barkov's avatar Alexander Barkov

MDEV-10582 sql_mode=ORACLE: explicit cursor attributes %ISOPEN, %ROWCOUNT, %FOUND, %NOTFOUND

parent 4bb87996
...@@ -350,3 +350,7 @@ CREATE TABLE decode (decode int); ...@@ -350,3 +350,7 @@ CREATE TABLE decode (decode int);
DROP TABLE decode; DROP TABLE decode;
CREATE TABLE rowcount (rowcount int); CREATE TABLE rowcount (rowcount int);
DROP TABLE rowcount; DROP TABLE rowcount;
CREATE TABLE isopen (isopen int);
DROP TABLE isopen;
CREATE TABLE notfound (notfound int);
DROP TABLE notfound;
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;
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;
...@@ -247,3 +247,9 @@ DROP TABLE decode; ...@@ -247,3 +247,9 @@ DROP TABLE decode;
CREATE TABLE rowcount (rowcount int); CREATE TABLE rowcount (rowcount int);
DROP TABLE rowcount; DROP TABLE rowcount;
CREATE TABLE isopen (isopen int);
DROP TABLE isopen;
CREATE TABLE notfound (notfound int);
DROP TABLE notfound;
...@@ -6862,4 +6862,52 @@ void Item_func_last_value::fix_length_and_dec() ...@@ -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;
}
...@@ -725,6 +725,76 @@ class Item_int_func :public Item_func ...@@ -725,6 +725,76 @@ class Item_int_func :public Item_func
}; };
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 class Item_func_connection_id :public Item_int_func
{ {
longlong value; longlong value;
......
...@@ -310,6 +310,7 @@ static SYMBOL symbols[] = { ...@@ -310,6 +310,7 @@ static SYMBOL symbols[] = {
{ "IPC", SYM(IPC_SYM)}, { "IPC", SYM(IPC_SYM)},
{ "IS", SYM(IS)}, { "IS", SYM(IS)},
{ "ISOLATION", SYM(ISOLATION)}, { "ISOLATION", SYM(ISOLATION)},
{ "ISOPEN", SYM(ISOPEN_SYM)},
{ "ISSUER", SYM(ISSUER_SYM)}, { "ISSUER", SYM(ISSUER_SYM)},
{ "ITERATE", SYM(ITERATE_SYM)}, { "ITERATE", SYM(ITERATE_SYM)},
{ "INVOKER", SYM(INVOKER_SYM)}, { "INVOKER", SYM(INVOKER_SYM)},
...@@ -415,6 +416,7 @@ static SYMBOL symbols[] = { ...@@ -415,6 +416,7 @@ static SYMBOL symbols[] = {
{ "NODEGROUP", SYM(NODEGROUP_SYM)}, { "NODEGROUP", SYM(NODEGROUP_SYM)},
{ "NONE", SYM(NONE_SYM)}, { "NONE", SYM(NONE_SYM)},
{ "NOT", SYM(NOT_SYM)}, { "NOT", SYM(NOT_SYM)},
{ "NOTFOUND", SYM(NOTFOUND_SYM)},
{ "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)}, { "NO_WRITE_TO_BINLOG", SYM(NO_WRITE_TO_BINLOG)},
{ "NULL", SYM(NULL_SYM)}, { "NULL", SYM(NULL_SYM)},
{ "NUMBER", SYM(NUMBER_SYM)}, { "NUMBER", SYM(NUMBER_SYM)},
......
...@@ -289,13 +289,15 @@ sp_condition_value *sp_pcontext::find_condition(const LEX_STRING name, ...@@ -289,13 +289,15 @@ sp_condition_value *sp_pcontext::find_condition(const LEX_STRING name,
static sp_condition_value static sp_condition_value
cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN),
cond_no_data_found(ER_SP_FETCH_NO_DATA), cond_no_data_found(ER_SP_FETCH_NO_DATA),
cond_dup_val_on_index(ER_DUP_ENTRY), cond_dup_val_on_index(ER_DUP_ENTRY),
cond_too_many_rows(ER_TOO_MANY_ROWS); 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("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("DUP_VAL_ON_INDEX"), &cond_dup_val_on_index),
sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows) sp_condition(C_STRING_WITH_LEN("TOO_MANY_ROWS"), &cond_too_many_rows)
......
...@@ -425,7 +425,10 @@ sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i) ...@@ -425,7 +425,10 @@ sp_cursor::sp_cursor(THD *thd_arg, sp_lex_keeper *lex_keeper, sp_instr_cpush *i)
result(thd_arg), result(thd_arg),
m_lex_keeper(lex_keeper), m_lex_keeper(lex_keeper),
server_side_cursor(NULL), 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 currsor can't be stored in QC, so we should prevent opening QC for
...@@ -470,6 +473,8 @@ int sp_cursor::close(THD *thd) ...@@ -470,6 +473,8 @@ int sp_cursor::close(THD *thd)
MYF(0)); MYF(0));
return -1; return -1;
} }
m_row_count= m_fetch_count= 0;
m_found= false;
destroy(); destroy();
return 0; return 0;
} }
...@@ -497,6 +502,7 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) ...@@ -497,6 +502,7 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
return -1; return -1;
} }
m_fetch_count++;
DBUG_EXECUTE_IF("bug23032_emit_warning", DBUG_EXECUTE_IF("bug23032_emit_warning",
push_warning(thd, Sql_condition::WARN_LEVEL_WARN, push_warning(thd, Sql_condition::WARN_LEVEL_WARN,
ER_UNKNOWN_ERROR, ER_UNKNOWN_ERROR,
...@@ -514,10 +520,15 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars) ...@@ -514,10 +520,15 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
*/ */
if (! server_side_cursor->is_open()) 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)); my_message(ER_SP_FETCH_NO_DATA, ER_THD(thd, ER_SP_FETCH_NO_DATA), MYF(0));
return -1; return -1;
} }
m_found= true;
m_row_count++;
return 0; return 0;
} }
......
...@@ -452,6 +452,15 @@ class sp_cursor : public Sql_alloc ...@@ -452,6 +452,15 @@ class sp_cursor : public Sql_alloc
my_bool is_open() my_bool is_open()
{ return MY_TEST(server_side_cursor); } { 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); int fetch(THD *, List<sp_variable> *vars);
sp_instr_cpush *get_instr() sp_instr_cpush *get_instr()
...@@ -461,7 +470,10 @@ class sp_cursor : public Sql_alloc ...@@ -461,7 +470,10 @@ class sp_cursor : public Sql_alloc
Select_fetch_into_spvars result; Select_fetch_into_spvars result;
sp_lex_keeper *m_lex_keeper; sp_lex_keeper *m_lex_keeper;
Server_side_cursor *server_side_cursor; 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(); void destroy();
}; // class sp_cursor : public Sql_alloc }; // class sp_cursor : public Sql_alloc
......
...@@ -1181,6 +1181,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1181,6 +1181,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token IPC_SYM %token IPC_SYM
%token IS /* SQL-2003-R */ %token IS /* SQL-2003-R */
%token ISOLATION /* SQL-2003-R */ %token ISOLATION /* SQL-2003-R */
%token ISOPEN_SYM /* Oracle-N */
%token ISSUER_SYM %token ISSUER_SYM
%token ITERATE_SYM %token ITERATE_SYM
%token JOIN_SYM /* SQL-2003-R */ %token JOIN_SYM /* SQL-2003-R */
...@@ -1290,6 +1291,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1290,6 +1291,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token NONE_SYM /* SQL-2003-R */ %token NONE_SYM /* SQL-2003-R */
%token NOT2_SYM %token NOT2_SYM
%token NOT_SYM /* SQL-2003-R */ %token NOT_SYM /* SQL-2003-R */
%token NOTFOUND_SYM /* Oracle-R */
%token NOW_SYM %token NOW_SYM
%token NO_SYM /* SQL-2003-R */ %token NO_SYM /* SQL-2003-R */
%token NO_WAIT_SYM %token NO_WAIT_SYM
...@@ -14346,6 +14348,7 @@ keyword_sp: ...@@ -14346,6 +14348,7 @@ keyword_sp:
| IO_SYM {} | IO_SYM {}
| IPC_SYM {} | IPC_SYM {}
| ISOLATION {} | ISOLATION {}
| ISOPEN_SYM {}
| ISSUER_SYM {} | ISSUER_SYM {}
| JSON_SYM {} | JSON_SYM {}
| INSERT_METHOD {} | INSERT_METHOD {}
...@@ -14415,6 +14418,7 @@ keyword_sp: ...@@ -14415,6 +14418,7 @@ keyword_sp:
| NO_WAIT_SYM {} | NO_WAIT_SYM {}
| NODEGROUP_SYM {} | NODEGROUP_SYM {}
| NONE_SYM {} | NONE_SYM {}
| NOTFOUND_SYM {}
| NUMBER_SYM {} | NUMBER_SYM {}
| NVARCHAR_SYM {} | NVARCHAR_SYM {}
| OFFSET_SYM {} | OFFSET_SYM {}
......
...@@ -178,6 +178,11 @@ void ORAerror(THD *thd, const char *s) ...@@ -178,6 +178,11 @@ void ORAerror(THD *thd, const char *s)
Lex_field_type_st Lex_field_type; Lex_field_type_st Lex_field_type;
Lex_dyncol_type_st Lex_dyncol_type; Lex_dyncol_type_st Lex_dyncol_type;
Lex_for_loop_st for_loop; Lex_for_loop_st for_loop;
struct
{
LEX_STRING name;
uint offset;
} sp_cursor_name_and_offset;
/* pointers */ /* pointers */
Create_field *create_field; Create_field *create_field;
...@@ -555,6 +560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -555,6 +560,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token IPC_SYM %token IPC_SYM
%token IS /* SQL-2003-R */ %token IS /* SQL-2003-R */
%token ISOLATION /* SQL-2003-R */ %token ISOLATION /* SQL-2003-R */
%token ISOPEN_SYM /* Oracle-N */
%token ISSUER_SYM %token ISSUER_SYM
%token ITERATE_SYM %token ITERATE_SYM
%token JOIN_SYM /* SQL-2003-R */ %token JOIN_SYM /* SQL-2003-R */
...@@ -664,6 +670,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -664,6 +670,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token NONE_SYM /* SQL-2003-R */ %token NONE_SYM /* SQL-2003-R */
%token NOT2_SYM %token NOT2_SYM
%token NOT_SYM /* SQL-2003-R */ %token NOT_SYM /* SQL-2003-R */
%token NOTFOUND_SYM /* Oracle-R */
%token NOW_SYM %token NOW_SYM
%token NO_SYM /* SQL-2003-R */ %token NO_SYM /* SQL-2003-R */
%token NO_WAIT_SYM %token NO_WAIT_SYM
...@@ -975,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -975,7 +982,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%left '&' %left '&'
%left SHIFT_LEFT SHIFT_RIGHT %left SHIFT_LEFT SHIFT_RIGHT
%left '-' '+' %left '-' '+'
%left '*' '/' '%' DIV_SYM MOD_SYM %left '*' '/' DIV_SYM MOD_SYM
%left '^' %left '^'
%left NEG '~' %left NEG '~'
%right NOT_SYM NOT2_SYM %right NOT_SYM NOT2_SYM
...@@ -1103,6 +1110,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); ...@@ -1103,6 +1110,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
window_func_expr window_func_expr
window_func window_func
simple_window_func simple_window_func
explicit_cursor_attr
function_call_keyword function_call_keyword
function_call_nonkeyword function_call_nonkeyword
function_call_generic function_call_generic
...@@ -1291,6 +1299,7 @@ END_OF_INPUT ...@@ -1291,6 +1299,7 @@ END_OF_INPUT
%type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list %type <spblock> sp_decl_body sp_decl_body_list opt_sp_decl_body_list
%type <spblock_handlers> sp_block_statements_and_exceptions %type <spblock_handlers> sp_block_statements_and_exceptions
%type <sp_instr_addr> sp_instr_addr %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 <num> opt_exception_clause exception_handlers
%type <lex> sp_cursor_stmt %type <lex> sp_cursor_stmt
%type <spname> sp_name %type <spname> sp_name
...@@ -8598,12 +8607,6 @@ bit_expr: ...@@ -8598,12 +8607,6 @@ bit_expr:
if ($$ == NULL) if ($$ == NULL)
MYSQL_YYABORT; 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 | bit_expr DIV_SYM bit_expr %prec DIV_SYM
{ {
$$= new (thd->mem_root) Item_func_int_div(thd, $1, $3); $$= new (thd->mem_root) Item_func_int_div(thd, $1, $3);
...@@ -8740,6 +8743,44 @@ dyncall_create_list: ...@@ -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 Expressions that the parser allows in a column DEFAULT clause
without parentheses. These expressions cannot end with a COLLATE clause. without parentheses. These expressions cannot end with a COLLATE clause.
...@@ -8904,6 +8945,7 @@ column_default_non_parenthesized_expr: ...@@ -8904,6 +8945,7 @@ column_default_non_parenthesized_expr:
simple_expr: simple_expr:
column_default_non_parenthesized_expr column_default_non_parenthesized_expr
| explicit_cursor_attr
| simple_expr COLLATE_SYM ident_or_text %prec NEG | simple_expr COLLATE_SYM ident_or_text %prec NEG
{ {
Item *i1= new (thd->mem_root) Item_string(thd, $3.str, Item *i1= new (thd->mem_root) Item_string(thd, $3.str,
...@@ -14306,6 +14348,7 @@ keyword_sp: ...@@ -14306,6 +14348,7 @@ keyword_sp:
| IO_SYM {} | IO_SYM {}
| IPC_SYM {} | IPC_SYM {}
| ISOLATION {} | ISOLATION {}
| ISOPEN_SYM {}
| ISSUER_SYM {} | ISSUER_SYM {}
| JSON_SYM {} | JSON_SYM {}
| INSERT_METHOD {} | INSERT_METHOD {}
...@@ -14375,6 +14418,7 @@ keyword_sp: ...@@ -14375,6 +14418,7 @@ keyword_sp:
| NO_WAIT_SYM {} | NO_WAIT_SYM {}
| NODEGROUP_SYM {} | NODEGROUP_SYM {}
| NONE_SYM {} | NONE_SYM {}
| NOTFOUND_SYM {}
| NUMBER_SYM {} | NUMBER_SYM {}
| NVARCHAR_SYM {} | NVARCHAR_SYM {}
| OFFSET_SYM {} | OFFSET_SYM {}
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment