summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mysql-test/suite/compat/oracle/r/exception.result161
-rw-r--r--mysql-test/suite/compat/oracle/t/exception.test167
-rw-r--r--sql/sp_pcontext.cc144
-rw-r--r--sql/sp_pcontext.h41
-rw-r--r--sql/sp_rcontext.cc8
-rw-r--r--sql/sp_rcontext.h5
-rw-r--r--sql/sql_class.cc11
-rw-r--r--sql/sql_class.h34
-rw-r--r--sql/sql_error.cc13
-rw-r--r--sql/sql_error.h161
-rw-r--r--sql/sql_signal.cc13
-rw-r--r--sql/sql_yacc_ora.yy10
12 files changed, 655 insertions, 113 deletions
diff --git a/mysql-test/suite/compat/oracle/r/exception.result b/mysql-test/suite/compat/oracle/r/exception.result
index 7366f147f2a..9c40252a163 100644
--- a/mysql-test/suite/compat/oracle/r/exception.result
+++ b/mysql-test/suite/compat/oracle/r/exception.result
@@ -220,3 +220,164 @@ DROP TABLE t1;
#
# End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
#
+#
+# MDEV-10587 sql_mode=ORACLE: User defined exceptions
+#
+#
+# Checking that duplicate WHEN clause is not allowed
+#
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+e EXCEPTION;
+BEGIN
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN e THEN RETURN 'Got exception e';
+WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+ERROR 42000: Duplicate handler declared in the same block
+#
+# Checking that raised user exceptions are further caught by name
+#
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+e EXCEPTION;
+f EXCEPTION;
+BEGIN
+IF c = 'e' THEN RAISE e; END IF;
+IF c = 'f' THEN RAISE f; END IF;
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+SELECT f1('');
+f1('')
+Got no exceptions
+SELECT f1('e');
+f1('e')
+Got exception e
+SELECT f1('f');
+ERROR 45000: Unhandled user-defined exception condition
+DROP FUNCTION f1;
+#
+# Checking that raised user exceptions are further caught by OTHERS
+#
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+e EXCEPTION;
+f EXCEPTION;
+BEGIN
+IF c = 'e' THEN RAISE e; END IF;
+IF c = 'f' THEN RAISE f; END IF;
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN OTHERS THEN RETURN 'Got some exception';
+END;
+$$
+SELECT f1('');
+f1('')
+Got no exceptions
+SELECT f1('e');
+f1('e')
+Got some exception
+SELECT f1('f');
+f1('f')
+Got some exception
+DROP FUNCTION f1;
+#
+# Checking that 'WHEN e .. WHEN f' does not produce ER_SP_DUP_HANDLER
+#
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+e EXCEPTION;
+f EXCEPTION;
+a VARCHAR(64):='';
+BEGIN
+BEGIN
+IF c = 'e' THEN RAISE e; END IF;
+IF c = 'f' THEN RAISE f; END IF;
+EXCEPTION
+WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE e; END;
+WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE f; END;
+END;
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+SELECT f1('');
+f1('')
+Got no exceptions
+SELECT f1('e');
+f1('e')
+Got EXCEPTION1/e; Got EXCEPTION2/OTHERS;
+SELECT f1('f');
+f1('f')
+Got EXCEPTION1/f; Got EXCEPTION2/OTHERS;
+DROP FUNCTION f1;
+#
+# Checking that resignaled user exceptions are further caught by name
+#
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+e EXCEPTION;
+f EXCEPTION;
+a VARCHAR(64):='';
+BEGIN
+BEGIN
+IF c = 'e' THEN RAISE e; END IF;
+IF c = 'f' THEN RAISE f; END IF;
+EXCEPTION
+WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+END;
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN e THEN RETURN a || 'Got EXCEPTION2/e;';
+END;
+$$
+SELECT f1('');
+f1('')
+Got no exceptions
+SELECT f1('e');
+f1('e')
+Got EXCEPTION1/e; Got EXCEPTION2/e;
+SELECT f1('f');
+ERROR 45000: Unhandled user-defined exception condition
+DROP FUNCTION f1;
+#
+# Checking that resignaled user exceptions are further caught by OTHERS
+#
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+e EXCEPTION;
+f EXCEPTION;
+a VARCHAR(64):='';
+BEGIN
+BEGIN
+IF c = 'e' THEN RAISE e; END IF;
+IF c = 'f' THEN RAISE f; END IF;
+EXCEPTION
+WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+END;
+RETURN 'Got no exceptions';
+EXCEPTION
+WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+SELECT f1('');
+f1('')
+Got no exceptions
+SELECT f1('e');
+f1('e')
+Got EXCEPTION1/e; Got EXCEPTION2/OTHERS;
+SELECT f1('f');
+f1('f')
+Got EXCEPTION1/f; Got EXCEPTION2/OTHERS;
+DROP FUNCTION f1;
+#
+# End of MDEV-10587 sql_mode=ORACLE: User defined exceptions
+#
diff --git a/mysql-test/suite/compat/oracle/t/exception.test b/mysql-test/suite/compat/oracle/t/exception.test
index 2bb27433a4d..07061abb6bc 100644
--- a/mysql-test/suite/compat/oracle/t/exception.test
+++ b/mysql-test/suite/compat/oracle/t/exception.test
@@ -264,3 +264,170 @@ DROP TABLE t1;
--echo #
--echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo #
+
+
+--echo #
+--echo # MDEV-10587 sql_mode=ORACLE: User defined exceptions
+--echo #
+
+--echo #
+--echo # Checking that duplicate WHEN clause is not allowed
+--echo #
+
+DELIMITER $$;
+--error ER_SP_DUP_HANDLER
+CREATE FUNCTION f1() RETURN VARCHAR
+AS
+ e EXCEPTION;
+BEGIN
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN 'Got exception e';
+ WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+DELIMITER ;$$
+
+
+--echo #
+--echo # Checking that raised user exceptions are further caught by name
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN 'Got exception e';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+--error ER_SIGNAL_EXCEPTION
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that raised user exceptions are further caught by OTHERS
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN 'Got some exception';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that 'WHEN e .. WHEN f' does not produce ER_SP_DUP_HANDLER
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE e; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE f; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that resignaled user exceptions are further caught by name
+--echo #
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN e THEN RETURN a || 'Got EXCEPTION2/e;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+--error ER_SIGNAL_EXCEPTION
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # Checking that resignaled user exceptions are further caught by OTHERS
+--echo #
+
+DELIMITER $$;
+CREATE FUNCTION f1(c VARCHAR) RETURN VARCHAR
+AS
+ e EXCEPTION;
+ f EXCEPTION;
+ a VARCHAR(64):='';
+BEGIN
+ BEGIN
+ IF c = 'e' THEN RAISE e; END IF;
+ IF c = 'f' THEN RAISE f; END IF;
+ EXCEPTION
+ WHEN e THEN BEGIN a:='Got EXCEPTION1/e; '; RAISE; END;
+ WHEN f THEN BEGIN a:='Got EXCEPTION1/f; '; RAISE; END;
+ END;
+ RETURN 'Got no exceptions';
+EXCEPTION
+ WHEN OTHERS THEN RETURN a || 'Got EXCEPTION2/OTHERS;';
+END;
+$$
+DELIMITER ;$$
+SELECT f1('');
+SELECT f1('e');
+SELECT f1('f');
+DROP FUNCTION f1;
+
+
+--echo #
+--echo # End of MDEV-10587 sql_mode=ORACLE: User defined exceptions
+--echo #
diff --git a/sql/sp_pcontext.cc b/sql/sp_pcontext.cc
index 0937dfdb081..047d0f50aaa 100644
--- a/sql/sp_pcontext.cc
+++ b/sql/sp_pcontext.cc
@@ -27,10 +27,41 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const
{
DBUG_ASSERT(cv);
+ /*
+ The following test disallows duplicate handlers,
+ including user defined exceptions with the same WHEN clause:
+ DECLARE
+ a EXCEPTION;
+ b EXCEPTION;
+ BEGIN
+ RAUSE a;
+ EXCEPTION
+ WHEN a THEN RETURN 'a0';
+ WHEN a THEN RETURN 'a1';
+ END
+ */
if (this == cv)
return true;
- if (type != cv->type)
+ /*
+ The test below considers two conditions of the same type as equal
+ (except for the user defined exceptions) to avoid declaring duplicate
+ handlers.
+
+ All user defined conditions have type==SQLSTATE
+ with the same SQL state and error code.
+ It's OK to have multiple user defined conditions:
+ DECLARE
+ a EXCEPTION;
+ b EXCEPTION;
+ BEGIN
+ RAISE a;
+ EXCEPTION
+ WHEN a THEN RETURN 'a';
+ WHEN b THEN RETURN 'b';
+ END;
+ */
+ if (type != cv->type || m_is_user_defined || cv->m_is_user_defined)
return false;
switch (type)
@@ -353,9 +384,57 @@ bool sp_pcontext::check_duplicate_handler(
}
+bool sp_condition_value::matches(const Sql_condition_identity &value,
+ const sp_condition_value *found_cv) const
+{
+ bool user_value_matched= !value.get_user_condition_value() ||
+ this == value.get_user_condition_value();
+
+ switch (type)
+ {
+ case sp_condition_value::ERROR_CODE:
+ return user_value_matched &&
+ value.get_sql_errno() == get_sql_errno() &&
+ (!found_cv || found_cv->type > sp_condition_value::ERROR_CODE);
+
+ case sp_condition_value::SQLSTATE:
+ return user_value_matched &&
+ Sql_state::eq(&value) &&
+ (!found_cv || found_cv->type > sp_condition_value::SQLSTATE);
+
+ case sp_condition_value::WARNING:
+ return user_value_matched &&
+ (value.Sql_state::is_warning() ||
+ value.get_level() == Sql_condition::WARN_LEVEL_WARN) &&
+ !found_cv;
+
+ case sp_condition_value::NOT_FOUND:
+ return user_value_matched &&
+ value.Sql_state::is_not_found() &&
+ !found_cv;
+
+ case sp_condition_value::EXCEPTION:
+ /*
+ In sql_mode=ORACLE this construct should catch both errors and warnings:
+ EXCEPTION
+ WHEN OTHERS THEN ...;
+ E.g. NO_DATA_FOUND is more like a warning than an error,
+ and it should be caught.
+
+ We don't check user_value_matched here.
+ "WHEN OTHERS" catches all user defined exception.
+ */
+ return (((current_thd->variables.sql_mode & MODE_ORACLE) ||
+ (value.Sql_state::is_exception() &&
+ value.get_level() == Sql_condition::WARN_LEVEL_ERROR)) &&
+ !found_cv);
+ }
+ return false;
+}
+
+
sp_handler*
-sp_pcontext::find_handler(const Sql_state_errno *value,
- Sql_condition::enum_warning_level level) const
+sp_pcontext::find_handler(const Sql_condition_identity &value) const
{
sp_handler *found_handler= NULL;
sp_condition_value *found_cv= NULL;
@@ -369,61 +448,10 @@ sp_pcontext::find_handler(const Sql_state_errno *value,
while ((cv= li++))
{
- switch (cv->type)
+ if (cv->matches(value, found_cv))
{
- case sp_condition_value::ERROR_CODE:
- if (value->get_sql_errno() == cv->get_sql_errno() &&
- (!found_cv ||
- found_cv->type > sp_condition_value::ERROR_CODE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::SQLSTATE:
- if (cv->Sql_state::eq(value) &&
- (!found_cv ||
- found_cv->type > sp_condition_value::SQLSTATE))
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::WARNING:
- if ((value->Sql_state::is_warning() ||
- level == Sql_condition::WARN_LEVEL_WARN) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::NOT_FOUND:
- if (value->Sql_state::is_not_found() && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
-
- case sp_condition_value::EXCEPTION:
- /*
- In sql_mode=ORACLE this construct should catch errors and warnings:
- EXCEPTION
- WHEN OTHERS THEN ...;
- E.g. NO_DATA_FOUND is more like a warning than an error,
- and it should be caught.
- */
- if (((current_thd->variables.sql_mode & MODE_ORACLE) ||
- (value->Sql_state::is_exception() &&
- level == Sql_condition::WARN_LEVEL_ERROR)) && !found_cv)
- {
- found_cv= cv;
- found_handler= h;
- }
- break;
+ found_cv= cv;
+ found_handler= h;
}
}
}
@@ -466,7 +494,7 @@ sp_pcontext::find_handler(const Sql_state_errno *value,
if (!p || !p->m_parent)
return NULL;
- return p->m_parent->find_handler(value, level);
+ return p->m_parent->find_handler(value);
}
diff --git a/sql/sp_pcontext.h b/sql/sp_pcontext.h
index d97643e5ade..f30596cc02b 100644
--- a/sql/sp_pcontext.h
+++ b/sql/sp_pcontext.h
@@ -129,6 +129,7 @@ public:
class sp_condition_value : public Sql_alloc, public Sql_state_errno
{
+ bool m_is_user_defined;
public:
enum enum_type
{
@@ -146,23 +147,27 @@ public:
sp_condition_value(uint _mysqlerr)
:Sql_alloc(),
Sql_state_errno(_mysqlerr),
+ m_is_user_defined(false),
type(ERROR_CODE)
{ }
sp_condition_value(uint _mysqlerr, const char *_sql_state)
:Sql_alloc(),
Sql_state_errno(_mysqlerr, _sql_state),
+ m_is_user_defined(false),
type(ERROR_CODE)
{ }
- sp_condition_value(const char *_sql_state)
+ sp_condition_value(const char *_sql_state, bool is_user_defined= false)
:Sql_alloc(),
Sql_state_errno(0, _sql_state),
+ m_is_user_defined(is_user_defined),
type(SQLSTATE)
{ }
sp_condition_value(enum_type _type)
:Sql_alloc(),
+ m_is_user_defined(false),
type(_type)
{
DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
@@ -174,8 +179,39 @@ public:
///
/// @return true if the instances are equal, false otherwise.
bool equals(const sp_condition_value *cv) const;
+
+
+ /**
+ Checks if this condition is OK for search.
+ See also sp_context::find_handler().
+
+ @param identity - The condition identity
+ @param found_cv - A previously found matching condition or NULL.
+ @return true - If the current value matches identity and
+ makes a stronger match than the previously
+ found condition found_cv.
+ @return false - If the current value does not match identity,
+ of the current value makes a weaker match than found_cv.
+ */
+ bool matches(const Sql_condition_identity &identity,
+ const sp_condition_value *found_cv) const;
+
+ Sql_user_condition_identity get_user_condition_identity() const
+ {
+ return Sql_user_condition_identity(m_is_user_defined ? this : NULL);
+ }
};
+
+class sp_condition_value_user_defined: public sp_condition_value
+{
+public:
+ sp_condition_value_user_defined()
+ :sp_condition_value("45000", true)
+ { }
+};
+
+
///////////////////////////////////////////////////////////////////////////
/// This class represents 'DECLARE CONDITION' statement.
@@ -516,8 +552,7 @@ public:
/// @param level The SQL condition level
///
/// @return a pointer to the found SQL-handler or NULL.
- sp_handler *find_handler(const Sql_state_errno *value,
- Sql_condition::enum_warning_level level) const;
+ sp_handler *find_handler(const Sql_condition_identity &identity) const;
/////////////////////////////////////////////////////////////////////////
// Cursors.
diff --git a/sql/sp_rcontext.cc b/sql/sp_rcontext.cc
index 242e1b122d5..488fb85a69f 100644
--- a/sql/sp_rcontext.cc
+++ b/sql/sp_rcontext.cc
@@ -228,7 +228,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
if (thd->is_error())
{
found_handler=
- cur_spi->m_ctx->find_handler(da, Sql_condition::WARN_LEVEL_ERROR);
+ cur_spi->m_ctx->find_handler(da->get_error_condition_identity());
if (found_handler)
found_condition= da->get_error_condition();
@@ -244,7 +244,8 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
{
found_condition=
new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root,
- da, da->message());
+ da->get_error_condition_identity(),
+ da->message());
}
}
else if (da->current_statement_warn_count())
@@ -261,8 +262,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
{
- const sp_handler *handler=
- cur_spi->m_ctx->find_handler(c, c->get_level());
+ const sp_handler *handler= cur_spi->m_ctx->find_handler(*c);
if (handler)
{
found_handler= handler;
diff --git a/sql/sp_rcontext.h b/sql/sp_rcontext.h
index d2e1f5fec58..d040186ec34 100644
--- a/sql/sp_rcontext.h
+++ b/sql/sp_rcontext.h
@@ -120,7 +120,8 @@ public:
/// standard SQL-condition processing (Diagnostics_area should contain an
/// object for active SQL-condition, not just information stored in DA's
/// fields).
- class Sql_condition_info : public Sql_alloc, public Sql_state_errno_level
+ class Sql_condition_info : public Sql_alloc,
+ public Sql_condition_identity
{
public:
/// Text message.
@@ -132,7 +133,7 @@ public:
/// @param arena Query arena for SP
Sql_condition_info(const Sql_condition *_sql_condition,
Query_arena *arena)
- :Sql_state_errno_level(*_sql_condition)
+ :Sql_condition_identity(*_sql_condition)
{
message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
}
diff --git a/sql/sql_class.cc b/sql/sql_class.cc
index 5ced78ec52a..25052e84e96 100644
--- a/sql/sql_class.cc
+++ b/sql/sql_class.cc
@@ -1066,9 +1066,10 @@ void THD::raise_note_printf(uint sql_errno, ...)
}
Sql_condition* THD::raise_condition(uint sql_errno,
- const char* sqlstate,
- Sql_condition::enum_warning_level level,
- const char* msg)
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
+ const char* msg)
{
Diagnostics_area *da= get_stmt_da();
Sql_condition *cond= NULL;
@@ -1127,7 +1128,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!da->is_error())
{
set_row_count_func(-1);
- da->set_error_status(sql_errno, msg, sqlstate, cond);
+ da->set_error_status(sql_errno, msg, sqlstate, ucid, cond);
}
}
@@ -1141,7 +1142,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
sql_errno == ER_OUTOFMEMORY)))
{
- cond= da->push_warning(this, sql_errno, sqlstate, level, msg);
+ cond= da->push_warning(this, sql_errno, sqlstate, level, ucid, msg);
}
DBUG_RETURN(cond);
}
diff --git a/sql/sql_class.h b/sql/sql_class.h
index 6345e8c1cdf..86cb6036be3 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -3939,8 +3939,42 @@ private:
raise_condition(uint sql_errno,
const char* sqlstate,
Sql_condition::enum_warning_level level,
+ const char* msg)
+ {
+ return raise_condition(sql_errno, sqlstate, level,
+ Sql_user_condition_identity(), msg);
+ }
+
+ /**
+ Raise a generic or a user defined SQL condition.
+ @param ucid - the user condition identity
+ (or an empty identity if not a user condition)
+ @param sql_errno - the condition error number
+ @param sqlstate - the condition SQLSTATE
+ @param level - the condition level
+ @param msg - the condition message text
+ @return The condition raised, or NULL
+ */
+ Sql_condition*
+ raise_condition(uint sql_errno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
const char* msg);
+ Sql_condition*
+ raise_condition(const Sql_condition *cond)
+ {
+ Sql_condition *raised= raise_condition(cond->get_sql_errno(),
+ cond->get_sqlstate(),
+ cond->get_level(),
+ *cond/*Sql_user_condition_identity*/,
+ cond->get_message_text());
+ if (raised)
+ raised->copy_opt_attributes(cond);
+ return raised;
+ }
+
public:
/** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt);
diff --git a/sql/sql_error.cc b/sql/sql_error.cc
index 2e9b1dabea4..745471a1365 100644
--- a/sql/sql_error.cc
+++ b/sql/sql_error.cc
@@ -307,7 +307,8 @@ Diagnostics_area::reset_diagnostics_area()
m_can_overwrite_status= FALSE;
/** Don't take chances in production */
m_message[0]= '\0';
- m_sql_errno= 0;
+ Sql_state_errno::clear();
+ Sql_user_condition_identity::clear();
m_affected_rows= 0;
m_last_insert_id= 0;
m_statement_warn_count= 0;
@@ -406,6 +407,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
set_error_status(sql_errno,
ER(sql_errno),
mysql_errno_to_sqlstate(sql_errno),
+ Sql_user_condition_identity(),
NULL);
}
@@ -419,6 +421,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
@param sql_errno SQL-condition error number
@param message SQL-condition message
@param sqlstate SQL-condition state
+ @param ucid User defined condition identity
@param error_condition SQL-condition object representing the error state
@note Note, that error_condition may be NULL. It happens if a) OOM error is
@@ -429,6 +432,7 @@ void
Diagnostics_area::set_error_status(uint sql_errno,
const char *message,
const char *sqlstate,
+ const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition)
{
DBUG_ENTER("set_error_status");
@@ -455,7 +459,8 @@ Diagnostics_area::set_error_status(uint sql_errno,
return;
#endif
- set_condition_value(sql_errno, sqlstate);
+ Sql_state_errno::set(sql_errno, sqlstate);
+ Sql_user_condition_identity::set(ucid);
strmake_buf(m_message, message);
get_warning_info()->set_error_condition(error_condition);
@@ -647,7 +652,7 @@ void Warning_info::reserve_space(THD *thd, uint count)
}
Sql_condition *Warning_info::push_warning(THD *thd,
- const Sql_state_errno_level *value,
+ const Sql_condition_identity *value,
const char *msg)
{
Sql_condition *cond= NULL;
@@ -657,7 +662,7 @@ Sql_condition *Warning_info::push_warning(THD *thd,
if (m_allow_unlimited_warnings ||
m_warn_list.elements() < thd->variables.max_error_count)
{
- cond= new (& m_warn_root) Sql_condition(& m_warn_root, value, msg);
+ cond= new (& m_warn_root) Sql_condition(& m_warn_root, *value, msg);
if (cond)
m_warn_list.push_back(cond);
}
diff --git a/sql/sql_error.h b/sql/sql_error.h
index d80ce218769..bbe97333210 100644
--- a/sql/sql_error.h
+++ b/sql/sql_error.h
@@ -27,6 +27,7 @@
class THD;
class my_decimal;
+class sp_condition_value;
///////////////////////////////////////////////////////////////////////////
@@ -130,15 +131,11 @@ public:
uint get_sql_errno() const
{ return m_sql_errno; }
- void set_condition_value(uint sql_errno, const char *sqlstate)
+ void set(uint sql_errno, const char *sqlstate)
{
m_sql_errno= sql_errno;
set_sqlstate(sqlstate);
}
- void set_condition_value(const Sql_state_errno *other)
- {
- *this= *other;
- }
void clear()
{
m_sql_errno= 0;
@@ -194,6 +191,88 @@ public:
};
+/*
+ class Sql_user_condition_identity.
+ Instances of this class uniquely idetify user defined conditions (EXCEPTION).
+
+ SET sql_mode=ORACLE;
+ CREATE PROCEDURE p1
+ AS
+ a EXCEPTION;
+ BEGIN
+ RAISE a;
+ EXCEPTION
+ WHEN a THEN NULL;
+ END;
+
+ Currently a user defined condition is identified by a pointer to
+ its parse time sp_condition_value instance. This can change when
+ we add packages. See MDEV-10591.
+*/
+class Sql_user_condition_identity
+{
+protected:
+ const sp_condition_value *m_user_condition_value;
+public:
+ Sql_user_condition_identity()
+ :m_user_condition_value(NULL)
+ { }
+ Sql_user_condition_identity(const sp_condition_value *value)
+ :m_user_condition_value(value)
+ { }
+ const sp_condition_value *get_user_condition_value() const
+ { return m_user_condition_value; }
+
+ void set(const Sql_user_condition_identity &identity)
+ {
+ *this= identity;
+ }
+ void clear()
+ {
+ m_user_condition_value= NULL;
+ }
+};
+
+
+/**
+ class Sql_condition_identity.
+ Instances of this class uniquely identify conditions
+ (including user-defined exceptions for sql_mode=ORACLE)
+ and store everything that is needed for handler search
+ purposes in sp_pcontext::find_handler().
+*/
+class Sql_condition_identity: public Sql_state_errno_level,
+ public Sql_user_condition_identity
+{
+public:
+ Sql_condition_identity()
+ { }
+ Sql_condition_identity(const Sql_state_errno_level &st,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(st),
+ Sql_user_condition_identity(ucid)
+ { }
+ Sql_condition_identity(const Sql_state_errno &st,
+ enum_warning_level level,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(st, level),
+ Sql_user_condition_identity(ucid)
+ { }
+ Sql_condition_identity(uint sqlerrno,
+ const char* sqlstate,
+ enum_warning_level level,
+ const Sql_user_condition_identity &ucid)
+ :Sql_state_errno_level(sqlerrno, sqlstate, level),
+ Sql_user_condition_identity(ucid)
+ { }
+ void clear()
+ {
+ Sql_state_errno_level::clear();
+ Sql_user_condition_identity::clear();
+ }
+};
+
+
class Sql_condition_items
{
protected:
@@ -262,7 +341,7 @@ protected:
or an exception condition (error, not found).
*/
class Sql_condition : public Sql_alloc,
- public Sql_state_errno_level,
+ public Sql_condition_identity,
public Sql_condition_items
{
public:
@@ -342,6 +421,12 @@ private:
DBUG_ASSERT(mem_root != NULL);
}
+ Sql_condition(MEM_ROOT *mem_root, const Sql_user_condition_identity &ucid)
+ :Sql_condition_identity(Sql_state_errno_level(), ucid),
+ m_mem_root(mem_root)
+ {
+ DBUG_ASSERT(mem_root != NULL);
+ }
/**
Constructor for a fixed message text.
@param mem_root - memory root
@@ -350,25 +435,13 @@ private:
@param msg - the message text for this condition
*/
Sql_condition(MEM_ROOT *mem_root,
- const Sql_state_errno_level *value,
+ const Sql_condition_identity &value,
const char *msg)
- :Sql_state_errno_level(*value),
+ :Sql_condition_identity(value),
m_mem_root(mem_root)
{
DBUG_ASSERT(mem_root != NULL);
- DBUG_ASSERT(value->get_sql_errno() != 0);
- DBUG_ASSERT(msg != NULL);
- set_builtin_message_text(msg);
- }
-
- Sql_condition(MEM_ROOT *mem_root,
- const Sql_state_errno *value,
- const char *msg)
- :Sql_state_errno_level(*value, Sql_condition::WARN_LEVEL_ERROR),
- m_mem_root(mem_root)
- {
- DBUG_ASSERT(mem_root != NULL);
- DBUG_ASSERT(value->get_sql_errno() != 0);
+ DBUG_ASSERT(value.get_sql_errno() != 0);
DBUG_ASSERT(msg != NULL);
set_builtin_message_text(msg);
}
@@ -409,7 +482,7 @@ private:
*/
void clear()
{
- Sql_state_errno_level::clear();
+ Sql_condition_identity::clear();
Sql_condition_items::clear();
m_message_text.length(0);
}
@@ -664,15 +737,13 @@ private:
counters.
@param thd Thread context.
- @param sql_errno SQL-condition error number.
- @param sqlstate SQL-condition state.
- @param level SQL-condition level.
+ @param identity SQL-condition identity
@param msg SQL-condition message.
@return a pointer to the added SQL-condition.
*/
Sql_condition *push_warning(THD *thd,
- const Sql_state_errno_level *value,
+ const Sql_condition_identity *identity,
const char* msg);
/**
@@ -829,7 +900,8 @@ public:
Can not be assigned twice per statement.
*/
-class Diagnostics_area: public Sql_state_errno
+class Diagnostics_area: public Sql_state_errno,
+ public Sql_user_condition_identity
{
private:
/** The type of the counted and doubly linked list of conditions. */
@@ -881,8 +953,19 @@ public:
void set_error_status(uint sql_errno,
const char *message,
const char *sqlstate,
+ const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition);
+ void set_error_status(uint sql_errno,
+ const char *message,
+ const char *sqlstate,
+ const Sql_condition *error_condition)
+ {
+ set_error_status(sql_errno, message, sqlstate,
+ Sql_user_condition_identity(),
+ error_condition);
+ }
+
void disable_status();
void reset_diagnostics_area();
@@ -944,6 +1027,18 @@ public:
return m_statement_warn_count;
}
+ /**
+ Get the current errno, state and id of the user defined condition
+ and return them as Sql_condition_identity.
+ */
+ Sql_condition_identity get_error_condition_identity() const
+ {
+ DBUG_ASSERT(m_status == DA_ERROR);
+ return Sql_condition_identity(*this /*Sql_state_errno*/,
+ Sql_condition::WARN_LEVEL_ERROR,
+ *this /*Sql_user_condition_identity*/);
+ }
+
/* Used to count any warnings pushed after calling set_ok_status(). */
void increment_warning()
{
@@ -1040,12 +1135,22 @@ public:
uint sql_errno_arg,
const char* sqlstate,
Sql_condition::enum_warning_level level,
+ const Sql_user_condition_identity &ucid,
const char* msg)
{
- Sql_state_errno_level tmp(sql_errno_arg, sqlstate, level);
+ Sql_condition_identity tmp(sql_errno_arg, sqlstate, level, ucid);
return get_warning_info()->push_warning(thd, &tmp, msg);
}
+ Sql_condition *push_warning(THD *thd,
+ uint sqlerrno,
+ const char* sqlstate,
+ Sql_condition::enum_warning_level level,
+ const char* msg)
+ {
+ return push_warning(thd, sqlerrno, sqlstate, level,
+ Sql_user_condition_identity(), msg);
+ }
void mark_sql_conditions_for_removal()
{ get_warning_info()->mark_sql_conditions_for_removal(); }
diff --git a/sql/sql_signal.cc b/sql/sql_signal.cc
index a4a31207a7d..828149f3035 100644
--- a/sql/sql_signal.cc
+++ b/sql/sql_signal.cc
@@ -353,13 +353,7 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) ||
(cond->m_level == Sql_condition::WARN_LEVEL_ERROR));
- Sql_condition *raised= NULL;
- raised= thd->raise_condition(cond->get_sql_errno(),
- cond->get_sqlstate(),
- cond->get_level(),
- cond->get_message_text());
- if (raised)
- raised->copy_opt_attributes(cond);
+ (void) thd->raise_condition(cond);
if (cond->m_level == Sql_condition::WARN_LEVEL_WARN)
{
@@ -373,7 +367,8 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
bool Sql_cmd_signal::execute(THD *thd)
{
bool result= TRUE;
- Sql_condition cond(thd->mem_root);
+ DBUG_ASSERT(m_cond);
+ Sql_condition cond(thd->mem_root, m_cond->get_user_condition_identity());
DBUG_ENTER("Sql_cmd_signal::execute");
@@ -427,7 +422,7 @@ bool Sql_cmd_resignal::execute(THD *thd)
DBUG_RETURN(result);
}
- Sql_condition signaled_err(thd->mem_root, signaled, signaled->message);
+ Sql_condition signaled_err(thd->mem_root, *signaled, signaled->message);
if (m_cond)
{
diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy
index 9ddadee5ffd..db1a20e110b 100644
--- a/sql/sql_yacc_ora.yy
+++ b/sql/sql_yacc_ora.yy
@@ -2416,6 +2416,16 @@ sp_decl_body:
$$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1;
}
+ | ident_directly_assignable EXCEPTION_SYM
+ {
+ sp_condition_value *spcond= new (thd->mem_root)
+ sp_condition_value_user_defined();
+ if (!spcond ||
+ Lex->spcont->declare_condition(thd, $1, spcond))
+ MYSQL_YYABORT;
+ $$.vars= $$.hndlrs= $$.curs= 0;
+ $$.conds= 1;
+ }
| sp_handler_type HANDLER_SYM FOR_SYM
{
if (Lex->sp_handler_declaration_init(thd, $1))