MDEV-10587 sql_mode=ORACLE: User defined exceptions
This commit is contained in:
parent
4d3818d30d
commit
4ed804aa4d
@ -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
|
||||
#
|
||||
|
@ -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 #
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
161
sql/sql_error.h
161
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(); }
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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))
|
||||
|
Loading…
x
Reference in New Issue
Block a user