Commit 4ed804aa authored by Alexander Barkov's avatar Alexander Barkov

MDEV-10587 sql_mode=ORACLE: User defined exceptions

parent 4d3818d3
...@@ -220,3 +220,164 @@ DROP TABLE t1; ...@@ -220,3 +220,164 @@ DROP TABLE t1;
# #
# End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions # 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
#
...@@ -264,3 +264,170 @@ DROP TABLE t1; ...@@ -264,3 +264,170 @@ DROP TABLE t1;
--echo # --echo #
--echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions --echo # End of MDEV-10840 sql_mode=ORACLE: RAISE statement for predefined exceptions
--echo # --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 #
...@@ -27,10 +27,41 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const ...@@ -27,10 +27,41 @@ bool sp_condition_value::equals(const sp_condition_value *cv) const
{ {
DBUG_ASSERT(cv); 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) if (this == cv)
return true; 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; return false;
switch (type) switch (type)
...@@ -353,9 +384,57 @@ bool sp_pcontext::check_duplicate_handler( ...@@ -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_handler*
sp_pcontext::find_handler(const Sql_state_errno *value, sp_pcontext::find_handler(const Sql_condition_identity &value) const
Sql_condition::enum_warning_level level) const
{ {
sp_handler *found_handler= NULL; sp_handler *found_handler= NULL;
sp_condition_value *found_cv= NULL; sp_condition_value *found_cv= NULL;
...@@ -369,61 +448,10 @@ sp_pcontext::find_handler(const Sql_state_errno *value, ...@@ -369,61 +448,10 @@ sp_pcontext::find_handler(const Sql_state_errno *value,
while ((cv= li++)) while ((cv= li++))
{ {
switch (cv->type) if (cv->matches(value, found_cv))
{ {
case sp_condition_value::ERROR_CODE: found_cv= cv;
if (value->get_sql_errno() == cv->get_sql_errno() && found_handler= h;
(!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;
} }
} }
} }
...@@ -466,7 +494,7 @@ sp_pcontext::find_handler(const Sql_state_errno *value, ...@@ -466,7 +494,7 @@ sp_pcontext::find_handler(const Sql_state_errno *value,
if (!p || !p->m_parent) if (!p || !p->m_parent)
return NULL; return NULL;
return p->m_parent->find_handler(value, level); return p->m_parent->find_handler(value);
} }
......
...@@ -129,6 +129,7 @@ class sp_label : public Sql_alloc ...@@ -129,6 +129,7 @@ class sp_label : public Sql_alloc
class sp_condition_value : public Sql_alloc, public Sql_state_errno class sp_condition_value : public Sql_alloc, public Sql_state_errno
{ {
bool m_is_user_defined;
public: public:
enum enum_type enum enum_type
{ {
...@@ -146,23 +147,27 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno ...@@ -146,23 +147,27 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno
sp_condition_value(uint _mysqlerr) sp_condition_value(uint _mysqlerr)
:Sql_alloc(), :Sql_alloc(),
Sql_state_errno(_mysqlerr), Sql_state_errno(_mysqlerr),
m_is_user_defined(false),
type(ERROR_CODE) type(ERROR_CODE)
{ } { }
sp_condition_value(uint _mysqlerr, const char *_sql_state) sp_condition_value(uint _mysqlerr, const char *_sql_state)
:Sql_alloc(), :Sql_alloc(),
Sql_state_errno(_mysqlerr, _sql_state), Sql_state_errno(_mysqlerr, _sql_state),
m_is_user_defined(false),
type(ERROR_CODE) 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_alloc(),
Sql_state_errno(0, _sql_state), Sql_state_errno(0, _sql_state),
m_is_user_defined(is_user_defined),
type(SQLSTATE) type(SQLSTATE)
{ } { }
sp_condition_value(enum_type _type) sp_condition_value(enum_type _type)
:Sql_alloc(), :Sql_alloc(),
m_is_user_defined(false),
type(_type) type(_type)
{ {
DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE); DBUG_ASSERT(type != ERROR_CODE && type != SQLSTATE);
...@@ -174,8 +179,39 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno ...@@ -174,8 +179,39 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno
/// ///
/// @return true if the instances are equal, false otherwise. /// @return true if the instances are equal, false otherwise.
bool equals(const sp_condition_value *cv) const; 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. /// This class represents 'DECLARE CONDITION' statement.
...@@ -516,8 +552,7 @@ class sp_pcontext : public Sql_alloc ...@@ -516,8 +552,7 @@ class sp_pcontext : public Sql_alloc
/// @param level The SQL condition level /// @param level The SQL condition level
/// ///
/// @return a pointer to the found SQL-handler or NULL. /// @return a pointer to the found SQL-handler or NULL.
sp_handler *find_handler(const Sql_state_errno *value, sp_handler *find_handler(const Sql_condition_identity &identity) const;
Sql_condition::enum_warning_level level) const;
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
// Cursors. // Cursors.
......
...@@ -228,7 +228,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, ...@@ -228,7 +228,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
if (thd->is_error()) if (thd->is_error())
{ {
found_handler= 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) if (found_handler)
found_condition= da->get_error_condition(); found_condition= da->get_error_condition();
...@@ -244,7 +244,8 @@ bool sp_rcontext::handle_sql_condition(THD *thd, ...@@ -244,7 +244,8 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
{ {
found_condition= found_condition=
new (callers_arena->mem_root) Sql_condition(callers_arena->mem_root, 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()) else if (da->current_statement_warn_count())
...@@ -261,8 +262,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd, ...@@ -261,8 +262,7 @@ bool sp_rcontext::handle_sql_condition(THD *thd,
if (c->get_level() == Sql_condition::WARN_LEVEL_WARN || if (c->get_level() == Sql_condition::WARN_LEVEL_WARN ||
c->get_level() == Sql_condition::WARN_LEVEL_NOTE) c->get_level() == Sql_condition::WARN_LEVEL_NOTE)
{ {
const sp_handler *handler= const sp_handler *handler= cur_spi->m_ctx->find_handler(*c);
cur_spi->m_ctx->find_handler(c, c->get_level());
if (handler) if (handler)
{ {
found_handler= handler; found_handler= handler;
......
...@@ -120,7 +120,8 @@ class sp_rcontext : public Sql_alloc ...@@ -120,7 +120,8 @@ class sp_rcontext : public Sql_alloc
/// standard SQL-condition processing (Diagnostics_area should contain an /// standard SQL-condition processing (Diagnostics_area should contain an
/// object for active SQL-condition, not just information stored in DA's /// object for active SQL-condition, not just information stored in DA's
/// fields). /// 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: public:
/// Text message. /// Text message.
...@@ -132,7 +133,7 @@ class sp_rcontext : public Sql_alloc ...@@ -132,7 +133,7 @@ class sp_rcontext : public Sql_alloc
/// @param arena Query arena for SP /// @param arena Query arena for SP
Sql_condition_info(const Sql_condition *_sql_condition, Sql_condition_info(const Sql_condition *_sql_condition,
Query_arena *arena) 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()); message= strdup_root(arena->mem_root, _sql_condition->get_message_text());
} }
......
...@@ -1066,9 +1066,10 @@ void THD::raise_note_printf(uint sql_errno, ...) ...@@ -1066,9 +1066,10 @@ void THD::raise_note_printf(uint sql_errno, ...)
} }
Sql_condition* THD::raise_condition(uint sql_errno, Sql_condition* THD::raise_condition(uint sql_errno,
const char* sqlstate, const char* sqlstate,
Sql_condition::enum_warning_level level, Sql_condition::enum_warning_level level,
const char* msg) const Sql_user_condition_identity &ucid,
const char* msg)
{ {
Diagnostics_area *da= get_stmt_da(); Diagnostics_area *da= get_stmt_da();
Sql_condition *cond= NULL; Sql_condition *cond= NULL;
...@@ -1127,7 +1128,7 @@ Sql_condition* THD::raise_condition(uint sql_errno, ...@@ -1127,7 +1128,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!da->is_error()) if (!da->is_error())
{ {
set_row_count_func(-1); 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, ...@@ -1141,7 +1142,7 @@ Sql_condition* THD::raise_condition(uint sql_errno,
if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY || if (!(is_fatal_error && (sql_errno == EE_OUTOFMEMORY ||
sql_errno == ER_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); DBUG_RETURN(cond);
} }
......
...@@ -3939,8 +3939,42 @@ class THD :public Statement, ...@@ -3939,8 +3939,42 @@ class THD :public Statement,
raise_condition(uint sql_errno, raise_condition(uint sql_errno,
const char* sqlstate, const char* sqlstate,
Sql_condition::enum_warning_level level, 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); 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: public:
/** Overloaded to guard query/query_length fields */ /** Overloaded to guard query/query_length fields */
virtual void set_statement(Statement *stmt); virtual void set_statement(Statement *stmt);
......
...@@ -307,7 +307,8 @@ Diagnostics_area::reset_diagnostics_area() ...@@ -307,7 +307,8 @@ Diagnostics_area::reset_diagnostics_area()
m_can_overwrite_status= FALSE; m_can_overwrite_status= FALSE;
/** Don't take chances in production */ /** Don't take chances in production */
m_message[0]= '\0'; m_message[0]= '\0';
m_sql_errno= 0; Sql_state_errno::clear();
Sql_user_condition_identity::clear();
m_affected_rows= 0; m_affected_rows= 0;
m_last_insert_id= 0; m_last_insert_id= 0;
m_statement_warn_count= 0; m_statement_warn_count= 0;
...@@ -406,6 +407,7 @@ Diagnostics_area::set_error_status(uint sql_errno) ...@@ -406,6 +407,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
set_error_status(sql_errno, set_error_status(sql_errno,
ER(sql_errno), ER(sql_errno),
mysql_errno_to_sqlstate(sql_errno), mysql_errno_to_sqlstate(sql_errno),
Sql_user_condition_identity(),
NULL); NULL);
} }
...@@ -419,6 +421,7 @@ Diagnostics_area::set_error_status(uint sql_errno) ...@@ -419,6 +421,7 @@ Diagnostics_area::set_error_status(uint sql_errno)
@param sql_errno SQL-condition error number @param sql_errno SQL-condition error number
@param message SQL-condition message @param message SQL-condition message
@param sqlstate SQL-condition state @param sqlstate SQL-condition state
@param ucid User defined condition identity
@param error_condition SQL-condition object representing the error state @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 @note Note, that error_condition may be NULL. It happens if a) OOM error is
...@@ -429,6 +432,7 @@ void ...@@ -429,6 +432,7 @@ void
Diagnostics_area::set_error_status(uint sql_errno, Diagnostics_area::set_error_status(uint sql_errno,
const char *message, const char *message,
const char *sqlstate, const char *sqlstate,
const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition) const Sql_condition *error_condition)
{ {
DBUG_ENTER("set_error_status"); DBUG_ENTER("set_error_status");
...@@ -455,7 +459,8 @@ Diagnostics_area::set_error_status(uint sql_errno, ...@@ -455,7 +459,8 @@ Diagnostics_area::set_error_status(uint sql_errno,
return; return;
#endif #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); strmake_buf(m_message, message);
get_warning_info()->set_error_condition(error_condition); get_warning_info()->set_error_condition(error_condition);
...@@ -647,7 +652,7 @@ void Warning_info::reserve_space(THD *thd, uint count) ...@@ -647,7 +652,7 @@ void Warning_info::reserve_space(THD *thd, uint count)
} }
Sql_condition *Warning_info::push_warning(THD *thd, Sql_condition *Warning_info::push_warning(THD *thd,
const Sql_state_errno_level *value, const Sql_condition_identity *value,
const char *msg) const char *msg)
{ {
Sql_condition *cond= NULL; Sql_condition *cond= NULL;
...@@ -657,7 +662,7 @@ Sql_condition *Warning_info::push_warning(THD *thd, ...@@ -657,7 +662,7 @@ Sql_condition *Warning_info::push_warning(THD *thd,
if (m_allow_unlimited_warnings || if (m_allow_unlimited_warnings ||
m_warn_list.elements() < thd->variables.max_error_count) 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) if (cond)
m_warn_list.push_back(cond); m_warn_list.push_back(cond);
} }
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
class THD; class THD;
class my_decimal; class my_decimal;
class sp_condition_value;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
...@@ -130,15 +131,11 @@ class Sql_state_errno: public Sql_state ...@@ -130,15 +131,11 @@ class Sql_state_errno: public Sql_state
uint get_sql_errno() const uint get_sql_errno() const
{ return m_sql_errno; } { 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; m_sql_errno= sql_errno;
set_sqlstate(sqlstate); set_sqlstate(sqlstate);
} }
void set_condition_value(const Sql_state_errno *other)
{
*this= *other;
}
void clear() void clear()
{ {
m_sql_errno= 0; m_sql_errno= 0;
...@@ -194,6 +191,88 @@ class Sql_state_errno_level: public Sql_state_errno ...@@ -194,6 +191,88 @@ class Sql_state_errno_level: public Sql_state_errno
}; };
/*
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 class Sql_condition_items
{ {
protected: protected:
...@@ -262,7 +341,7 @@ class Sql_condition_items ...@@ -262,7 +341,7 @@ class Sql_condition_items
or an exception condition (error, not found). or an exception condition (error, not found).
*/ */
class Sql_condition : public Sql_alloc, class Sql_condition : public Sql_alloc,
public Sql_state_errno_level, public Sql_condition_identity,
public Sql_condition_items public Sql_condition_items
{ {
public: public:
...@@ -342,6 +421,12 @@ class Sql_condition : public Sql_alloc, ...@@ -342,6 +421,12 @@ class Sql_condition : public Sql_alloc,
DBUG_ASSERT(mem_root != NULL); 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. Constructor for a fixed message text.
@param mem_root - memory root @param mem_root - memory root
...@@ -350,25 +435,13 @@ class Sql_condition : public Sql_alloc, ...@@ -350,25 +435,13 @@ class Sql_condition : public Sql_alloc,
@param msg - the message text for this condition @param msg - the message text for this condition
*/ */
Sql_condition(MEM_ROOT *mem_root, Sql_condition(MEM_ROOT *mem_root,
const Sql_state_errno_level *value, const Sql_condition_identity &value,
const char *msg) const char *msg)
:Sql_state_errno_level(*value), :Sql_condition_identity(value),
m_mem_root(mem_root) m_mem_root(mem_root)
{ {
DBUG_ASSERT(mem_root != NULL); 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);
}
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(msg != NULL); DBUG_ASSERT(msg != NULL);
set_builtin_message_text(msg); set_builtin_message_text(msg);
} }
...@@ -409,7 +482,7 @@ class Sql_condition : public Sql_alloc, ...@@ -409,7 +482,7 @@ class Sql_condition : public Sql_alloc,
*/ */
void clear() void clear()
{ {
Sql_state_errno_level::clear(); Sql_condition_identity::clear();
Sql_condition_items::clear(); Sql_condition_items::clear();
m_message_text.length(0); m_message_text.length(0);
} }
...@@ -664,15 +737,13 @@ class Warning_info ...@@ -664,15 +737,13 @@ class Warning_info
counters. counters.
@param thd Thread context. @param thd Thread context.
@param sql_errno SQL-condition error number. @param identity SQL-condition identity
@param sqlstate SQL-condition state.
@param level SQL-condition level.
@param msg SQL-condition message. @param msg SQL-condition message.
@return a pointer to the added SQL-condition. @return a pointer to the added SQL-condition.
*/ */
Sql_condition *push_warning(THD *thd, Sql_condition *push_warning(THD *thd,
const Sql_state_errno_level *value, const Sql_condition_identity *identity,
const char* msg); const char* msg);
/** /**
...@@ -829,7 +900,8 @@ class ErrConvDecimal : public ErrConv ...@@ -829,7 +900,8 @@ class ErrConvDecimal : public ErrConv
Can not be assigned twice per statement. 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: private:
/** The type of the counted and doubly linked list of conditions. */ /** The type of the counted and doubly linked list of conditions. */
...@@ -881,8 +953,19 @@ class Diagnostics_area: public Sql_state_errno ...@@ -881,8 +953,19 @@ class Diagnostics_area: public Sql_state_errno
void set_error_status(uint sql_errno, void set_error_status(uint sql_errno,
const char *message, const char *message,
const char *sqlstate, const char *sqlstate,
const Sql_user_condition_identity &ucid,
const Sql_condition *error_condition); 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 disable_status();
void reset_diagnostics_area(); void reset_diagnostics_area();
...@@ -944,6 +1027,18 @@ class Diagnostics_area: public Sql_state_errno ...@@ -944,6 +1027,18 @@ class Diagnostics_area: public Sql_state_errno
return m_statement_warn_count; 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(). */ /* Used to count any warnings pushed after calling set_ok_status(). */
void increment_warning() void increment_warning()
{ {
...@@ -1040,12 +1135,22 @@ class Diagnostics_area: public Sql_state_errno ...@@ -1040,12 +1135,22 @@ class Diagnostics_area: public Sql_state_errno
uint sql_errno_arg, uint sql_errno_arg,
const char* sqlstate, const char* sqlstate,
Sql_condition::enum_warning_level level, Sql_condition::enum_warning_level level,
const Sql_user_condition_identity &ucid,
const char* msg) 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); 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() void mark_sql_conditions_for_removal()
{ get_warning_info()->mark_sql_conditions_for_removal(); } { get_warning_info()->mark_sql_conditions_for_removal(); }
......
...@@ -353,13 +353,7 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond) ...@@ -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) || DBUG_ASSERT((cond->m_level == Sql_condition::WARN_LEVEL_WARN) ||
(cond->m_level == Sql_condition::WARN_LEVEL_ERROR)); (cond->m_level == Sql_condition::WARN_LEVEL_ERROR));
Sql_condition *raised= NULL; (void) thd->raise_condition(cond);
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);
if (cond->m_level == Sql_condition::WARN_LEVEL_WARN) 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) ...@@ -373,7 +367,8 @@ bool Sql_cmd_common_signal::raise_condition(THD *thd, Sql_condition *cond)
bool Sql_cmd_signal::execute(THD *thd) bool Sql_cmd_signal::execute(THD *thd)
{ {
bool result= TRUE; 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"); DBUG_ENTER("Sql_cmd_signal::execute");
...@@ -427,7 +422,7 @@ bool Sql_cmd_resignal::execute(THD *thd) ...@@ -427,7 +422,7 @@ bool Sql_cmd_resignal::execute(THD *thd)
DBUG_RETURN(result); 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) if (m_cond)
{ {
......
...@@ -2416,6 +2416,16 @@ sp_decl_body: ...@@ -2416,6 +2416,16 @@ sp_decl_body:
$$.vars= $$.hndlrs= $$.curs= 0; $$.vars= $$.hndlrs= $$.curs= 0;
$$.conds= 1; $$.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 | sp_handler_type HANDLER_SYM FOR_SYM
{ {
if (Lex->sp_handler_declaration_init(thd, $1)) if (Lex->sp_handler_declaration_init(thd, $1))
......
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