Commit 7fa1ad14 authored by Alexander Barkov's avatar Alexander Barkov

MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions

parent 76714a5c
......@@ -354,3 +354,5 @@ CREATE TABLE isopen (isopen int);
DROP TABLE isopen;
CREATE TABLE notfound (notfound int);
DROP TABLE notfound;
CREATE TABLE raise (raise int);
DROP TABLE raise;
......@@ -51,3 +51,153 @@ a
10
DROP PROCEDURE p1;
DROP TABLE t1;
#
# MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
#
#
# RAISE outside of an SP context
#
RAISE NO_DATA_FOUND;
ERROR 42000: Undefined CONDITION: NO_DATA_FOUND
RAISE INVALID_CURSOR;
ERROR 42000: Undefined CONDITION: INVALID_CURSOR
RAISE DUP_VAL_ON_INDEX;
ERROR 42000: Undefined CONDITION: DUP_VAL_ON_INDEX
RAISE TOO_MANY_ROWS;
ERROR 42000: Undefined CONDITION: TOO_MANY_ROWS
RAISE;
ERROR 0K000: RESIGNAL when handler not active
#
# RAISE for an undefinite exception
#
CREATE PROCEDURE p1
AS
BEGIN
RAISE xxx;
END;
$$
ERROR 42000: Undefined CONDITION: xxx
#
# RAISE for predefined exceptions
#
CREATE PROCEDURE p1
AS
BEGIN
RAISE no_data_found;
END;
$$
CALL p1();
Warnings:
Warning 1329 No data - zero rows fetched, selected, or processed
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
BEGIN
RAISE invalid_cursor;
END;
$$
CALL p1();
ERROR 24000: Cursor is not open
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
BEGIN
RAISE dup_val_on_index;
END;
$$
CALL p1();
ERROR 23000: Duplicate entry '%-.192s' for key %d
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
BEGIN
raise too_many_rows;
END;
$$
CALL p1();
ERROR 42000: Result consisted of more than one row
DROP PROCEDURE p1;
#
# RAISE with no exception name (resignal)
#
CREATE PROCEDURE p1()
AS
BEGIN
RAISE;
END;
$$
CALL p1();
ERROR 0K000: RESIGNAL when handler not active
DROP PROCEDURE p1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN RAISE;
WHEN NO_DATA_FOUND THEN RAISE;
END;
$$
CALL p1(0);
Warnings:
Warning 1329 No data - zero rows fetched, selected, or processed
CALL p1(2);
ERROR 42000: Result consisted of more than one row
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
CALL p1(0);
Warnings:
Warning 1329 No data - zero rows fetched, selected, or processed
CALL p1(2);
ERROR 42000: Result consisted of more than one row
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN INVALID_CURSOR THEN RAISE;
END;
$$
CALL p1();
ERROR 24000: Cursor is not open
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
CALL p1();
ERROR 24000: Cursor is not open
DROP PROCEDURE p1;
DROP TABLE t1;
#
# End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
#
......@@ -52,3 +52,194 @@ SELECT @res;
SELECT * FROM t1;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo #
--echo #
--echo # RAISE outside of an SP context
--echo #
--error ER_SP_COND_MISMATCH
RAISE NO_DATA_FOUND;
--error ER_SP_COND_MISMATCH
RAISE INVALID_CURSOR;
--error ER_SP_COND_MISMATCH
RAISE DUP_VAL_ON_INDEX;
--error ER_SP_COND_MISMATCH
RAISE TOO_MANY_ROWS;
--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
RAISE;
--echo #
--echo # RAISE for an undefinite exception
--echo #
DELIMITER $$;
--error ER_SP_COND_MISMATCH
CREATE PROCEDURE p1
AS
BEGIN
RAISE xxx;
END;
$$
DELIMITER ;$$
--echo #
--echo # RAISE for predefined exceptions
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE no_data_found;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE invalid_cursor;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
RAISE dup_val_on_index;
END;
$$
DELIMITER ;$$
--error ER_DUP_ENTRY
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
raise too_many_rows;
END;
$$
DELIMITER ;$$
--error ER_TOO_MANY_ROWS
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # RAISE with no exception name (resignal)
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
BEGIN
RAISE;
END;
$$
DELIMITER ;$$
--error ER_RESIGNAL_WITHOUT_ACTIVE_HANDLER
CALL p1();
DROP PROCEDURE p1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN TOO_MANY_ROWS THEN RAISE;
WHEN NO_DATA_FOUND THEN RAISE;
END;
$$
DELIMITER ;$$
CALL p1(0);
--error ER_TOO_MANY_ROWS
CALL p1(2);
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1(lim INT)
AS
a INT;
BEGIN
SELECT a INTO a FROM t1 LIMIT lim;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
DELIMITER ;$$
CALL p1(0);
--error ER_TOO_MANY_ROWS
CALL p1(2);
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN INVALID_CURSOR THEN RAISE;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(20);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a INT;
CURSOR c IS SELECT a FROM t1;
BEGIN
FETCH c INTO a;
EXCEPTION
WHEN OTHERS THEN RAISE;
END;
$$
DELIMITER ;$$
--error ER_SP_CURSOR_NOT_OPEN
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo #
......@@ -253,3 +253,6 @@ DROP TABLE isopen;
CREATE TABLE notfound (notfound int);
DROP TABLE notfound;
CREATE TABLE raise (raise int);
DROP TABLE raise;
......@@ -476,6 +476,7 @@ static SYMBOL symbols[] = {
{ "QUARTER", SYM(QUARTER_SYM)},
{ "QUERY", SYM(QUERY_SYM)},
{ "QUICK", SYM(QUICK)},
{ "RAISE", SYM(RAISE_SYM)},
{ "RANGE", SYM(RANGE_SYM)},
{ "RAW", SYM(RAW)},
{ "READ", SYM(READ_SYM)},
......
......@@ -289,16 +289,20 @@ 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);
// Warnings
cond_no_data_found(ER_SP_FETCH_NO_DATA, "01000"),
// Errors
cond_invalid_cursor(ER_SP_CURSOR_NOT_OPEN, "24000"),
cond_dup_val_on_index(ER_DUP_ENTRY, "23000"),
cond_too_many_rows(ER_TOO_MANY_ROWS, "42000");
static sp_condition sp_predefined_conditions[]=
{
sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor),
// Warnings
sp_condition(C_STRING_WITH_LEN("NO_DATA_FOUND"), &cond_no_data_found),
// Errors
sp_condition(C_STRING_WITH_LEN("INVALID_CURSOR"), &cond_invalid_cursor),
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)
};
......
......@@ -129,6 +129,10 @@ class sp_label : public Sql_alloc
class sp_condition_value : public Sql_alloc
{
void init_sql_state()
{
sql_state[0]= '\0';
}
public:
enum enum_type
{
......@@ -153,11 +157,21 @@ class sp_condition_value : public Sql_alloc
:Sql_alloc(),
type(ERROR_CODE),
mysqlerr(_mysqlerr)
{ }
{ init_sql_state(); }
sp_condition_value(uint _mysqlerr, const char *_sql_state)
:Sql_alloc(),
type(ERROR_CODE),
mysqlerr(_mysqlerr)
{
memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
sql_state[SQLSTATE_LENGTH]= 0;
}
sp_condition_value(const char *_sql_state)
:Sql_alloc(),
type(SQLSTATE)
type(SQLSTATE),
mysqlerr(0)
{
memcpy(sql_state, _sql_state, SQLSTATE_LENGTH);
sql_state[SQLSTATE_LENGTH]= 0;
......@@ -165,9 +179,11 @@ class sp_condition_value : public Sql_alloc
sp_condition_value(enum_type _type)
:Sql_alloc(),
type(_type)
type(_type),
mysqlerr(0)
{
DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
init_sql_state();
}
/// Check if two instances of sp_condition_value are equal or not.
......@@ -176,6 +192,8 @@ class sp_condition_value : public Sql_alloc
///
/// @return true if the instances are equal, false otherwise.
bool equals(const sp_condition_value *cv) const;
bool has_sql_state() const { return sql_state[0] != '\0'; }
};
///////////////////////////////////////////////////////////////////////////
......
......@@ -30,6 +30,7 @@
#include "sp.h"
#include "sql_select.h"
#include "sql_cte.h"
#include "sql_signal.h"
void LEX::parse_error()
......@@ -5903,6 +5904,24 @@ Item_param *LEX::add_placeholder(THD *thd, char *name,
}
bool LEX::add_signal_statement(THD *thd, const sp_condition_value *v)
{
Yacc_state *state= &thd->m_parser_state->m_yacc;
sql_command= SQLCOM_SIGNAL;
m_sql_cmd= new (thd->mem_root) Sql_cmd_signal(v, state->m_set_signal_info);
return m_sql_cmd == NULL;
}
bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v)
{
Yacc_state *state= &thd->m_parser_state->m_yacc;
sql_command= SQLCOM_RESIGNAL;
m_sql_cmd= new (thd->mem_root) Sql_cmd_resignal(v, state->m_set_signal_info);
return m_sql_cmd == NULL;
}
#ifdef MYSQL_SERVER
uint binlog_unsafe_map[256];
......
......@@ -3197,6 +3197,9 @@ struct LEX: public Query_tables_list
bool sp_for_loop_index_and_bounds(THD *thd, const Lex_for_loop_st &loop);
bool sp_for_loop_finalize(THD *thd, const Lex_for_loop_st &loop);
bool add_signal_statement(THD *thd, const class sp_condition_value *value);
bool add_resignal_statement(THD *thd, const class sp_condition_value *value);
// Check if "KEY IF NOT EXISTS name" used outside of ALTER context
bool check_add_key(DDL_options_st ddl)
{
......
......@@ -102,7 +102,7 @@ void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
/*
SIGNAL is restricted in sql_yacc.yy to only signal SQLSTATE conditions.
*/
DBUG_ASSERT(m_cond->type == sp_condition_value::SQLSTATE);
DBUG_ASSERT(m_cond->has_sql_state());
sqlstate= m_cond->sql_state;
cond->set_sqlstate(sqlstate);
}
......@@ -117,19 +117,25 @@ void Sql_cmd_common_signal::eval_defaults(THD *thd, Sql_condition *cond)
{
/* SQLSTATE class "01": warning. */
assign_defaults(cond, set_defaults,
Sql_condition::WARN_LEVEL_WARN, ER_SIGNAL_WARN);
Sql_condition::WARN_LEVEL_WARN,
m_cond && m_cond->mysqlerr ? m_cond->mysqlerr :
ER_SIGNAL_WARN);
}
else if ((sqlstate[0] == '0') && (sqlstate[1] == '2'))
{
/* SQLSTATE class "02": not found. */
assign_defaults(cond, set_defaults,
Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_NOT_FOUND);
Sql_condition::WARN_LEVEL_ERROR,
m_cond && m_cond->mysqlerr ? m_cond->mysqlerr :
ER_SIGNAL_NOT_FOUND);
}
else
{
/* other SQLSTATE classes : error. */
assign_defaults(cond, set_defaults,
Sql_condition::WARN_LEVEL_ERROR, ER_SIGNAL_EXCEPTION);
Sql_condition::WARN_LEVEL_ERROR,
m_cond && m_cond->mysqlerr ? m_cond->mysqlerr :
ER_SIGNAL_EXCEPTION);
}
}
......
......@@ -1362,6 +1362,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token QUARTER_SYM
%token QUERY_SYM
%token QUICK
%token RAISE_SYM /* Oracle-PLSQL-R */
%token RANGE_SYM /* SQL-2003-R */
%token RANK_SYM
%token RAW /* Oracle */
......@@ -3124,13 +3125,7 @@ sp_hcond:
signal_stmt:
SIGNAL_SYM signal_value opt_set_signal_information
{
LEX *lex= thd->lex;
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_SIGNAL;
lex->m_sql_cmd=
new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info);
if (lex->m_sql_cmd == NULL)
if (Lex->add_signal_statement(thd, $2))
MYSQL_YYABORT;
}
;
......@@ -3251,14 +3246,7 @@ signal_condition_information_item_name:
resignal_stmt:
RESIGNAL_SYM opt_signal_value opt_set_signal_information
{
LEX *lex= thd->lex;
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_RESIGNAL;
lex->m_sql_cmd=
new (thd->mem_root) Sql_cmd_resignal($2,
state->m_set_signal_info);
if (lex->m_sql_cmd == NULL)
if (Lex->add_resignal_statement(thd, $2))
MYSQL_YYABORT;
}
;
......@@ -14449,6 +14437,7 @@ keyword_sp:
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
| RAISE_SYM {}
| RAW {}
| READ_ONLY_SYM {}
| REBUILD_SYM {}
......
......@@ -741,6 +741,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token QUARTER_SYM
%token QUERY_SYM
%token QUICK
%token RAISE_SYM /* Oracle-PLSQL-R */
%token RANGE_SYM /* SQL-2003-R */
%token RANK_SYM
%token RAW /* Oracle */
......@@ -1311,7 +1312,7 @@ END_OF_INPUT
%type <num> index_hint_clause normal_join inner_join
%type <filetype> data_or_xml
%type <NONE> signal_stmt resignal_stmt
%type <NONE> signal_stmt resignal_stmt raise_stmt
%type <diag_condition_item_name> signal_condition_information_item_name
%type <trg_execution_order> trigger_follows_precedes_clause;
......@@ -1460,6 +1461,7 @@ statement:
| preload
| prepare
| purge
| raise_stmt
| release
| rename
| repair
......@@ -2549,16 +2551,24 @@ sp_hcond:
}
;
raise_stmt:
RAISE_SYM opt_set_signal_information
{
if (Lex->add_resignal_statement(thd, NULL))
MYSQL_YYABORT;
}
| RAISE_SYM signal_value opt_set_signal_information
{
if (Lex->add_signal_statement(thd, $2))
MYSQL_YYABORT;
}
;
signal_stmt:
SIGNAL_SYM signal_value opt_set_signal_information
{
LEX *lex= thd->lex;
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_SIGNAL;
lex->m_sql_cmd=
new (thd->mem_root) Sql_cmd_signal($2, state->m_set_signal_info);
if (lex->m_sql_cmd == NULL)
if (Lex->add_signal_statement(thd, $2))
MYSQL_YYABORT;
}
;
......@@ -2572,10 +2582,10 @@ signal_value:
/* SIGNAL foo cannot be used outside of stored programs */
if (lex->spcont == NULL)
my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
cond= lex->spcont->find_condition($1, false);
cond= lex->spcont->find_declared_or_predefined_condition($1);
if (cond == NULL)
my_yyabort_error((ER_SP_COND_MISMATCH, MYF(0), $1.str));
if (cond->type != sp_condition_value::SQLSTATE)
if (!cond->has_sql_state())
my_yyabort_error((ER_SIGNAL_BAD_CONDITION_TYPE, MYF(0)));
$$= cond;
}
......@@ -2679,14 +2689,7 @@ signal_condition_information_item_name:
resignal_stmt:
RESIGNAL_SYM opt_signal_value opt_set_signal_information
{
LEX *lex= thd->lex;
Yacc_state *state= & thd->m_parser_state->m_yacc;
lex->sql_command= SQLCOM_RESIGNAL;
lex->m_sql_cmd=
new (thd->mem_root) Sql_cmd_resignal($2,
state->m_set_signal_info);
if (lex->m_sql_cmd == NULL)
if (Lex->add_resignal_statement(thd, $2))
MYSQL_YYABORT;
}
;
......
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