diff options
author | Alexander Barkov <bar@mariadb.org> | 2016-10-14 16:52:33 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-04-05 15:02:53 +0400 |
commit | 4ed804aa4dc6a03d5a983a0aaf71b39e4702e8f3 (patch) | |
tree | be2115e6c59097079db4c75be877dd7f5a4674d1 | |
parent | 4d3818d30d97e6fbdb5130d73716401e883c7371 (diff) | |
download | mariadb-git-4ed804aa4dc6a03d5a983a0aaf71b39e4702e8f3.tar.gz |
MDEV-10587 sql_mode=ORACLE: User defined exceptions
-rw-r--r-- | mysql-test/suite/compat/oracle/r/exception.result | 161 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/exception.test | 167 | ||||
-rw-r--r-- | sql/sp_pcontext.cc | 144 | ||||
-rw-r--r-- | sql/sp_pcontext.h | 41 | ||||
-rw-r--r-- | sql/sp_rcontext.cc | 8 | ||||
-rw-r--r-- | sql/sp_rcontext.h | 5 | ||||
-rw-r--r-- | sql/sql_class.cc | 11 | ||||
-rw-r--r-- | sql/sql_class.h | 34 | ||||
-rw-r--r-- | sql/sql_error.cc | 13 | ||||
-rw-r--r-- | sql/sql_error.h | 161 | ||||
-rw-r--r-- | sql/sql_signal.cc | 13 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 10 |
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)) |