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;
#
# 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 @@ class sp_label : public Sql_alloc
class sp_condition_value : public Sql_alloc, public Sql_state_errno
{
bool m_is_user_defined;
public:
enum enum_type
{
......@@ -146,23 +147,27 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno
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 @@ class sp_condition_value : public Sql_alloc, public Sql_state_errno
///
/// @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 @@ class sp_pcontext : public Sql_alloc
/// @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 @@ class sp_rcontext : public Sql_alloc
/// 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 @@ class sp_rcontext : public Sql_alloc
/// @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 @@ class THD :public Statement,
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);
}
......
......@@ -27,6 +27,7 @@
class THD;
class my_decimal;
class sp_condition_value;
///////////////////////////////////////////////////////////////////////////
......@@ -130,15 +131,11 @@ class Sql_state_errno: public Sql_state
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 @@ 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
{
protected:
......@@ -262,7 +341,7 @@ class Sql_condition_items
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 @@ class Sql_condition : public Sql_alloc,
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 @@ class Sql_condition : public Sql_alloc,
@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 @@ class Sql_condition : public Sql_alloc,
*/
void clear()
{
Sql_state_errno_level::clear();
Sql_condition_identity::clear();
Sql_condition_items::clear();
m_message_text.length(0);
}
......@@ -664,15 +737,13 @@ class Warning_info
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 @@ class ErrConvDecimal : public ErrConv
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 @@ class Diagnostics_area: public Sql_state_errno
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 @@ class Diagnostics_area: public Sql_state_errno
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 @@ class Diagnostics_area: public Sql_state_errno
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))
......
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