13.6 Compound Statements

This section describes the syntax for the note 'BEGIN ... END': begin-end. compound statement and other statements that can be used in the body of stored programs: Stored procedures and functions, triggers, and events. These objects are defined in terms of SQL code that is stored on the server for later invocation (see note stored-objects::).

A compound statement is a block that can contain other blocks; declarations for variables, condition handlers, and cursors; and flow control constructs such as loops and conditional tests.

 File: manual.info.tmp, Node: begin-end, Next: statement-labels, Prev: sql-compound-statements, Up: sql-compound-statements

13.6.1 BEGIN ... END Compound Statement

 [BEGIN_LABEL:] BEGIN
     [STATEMENT_LIST]
 END [END_LABEL]

*note 'BEGIN ... END': begin-end. syntax is used for writing compound statements, which can appear within stored programs (stored procedures and functions, triggers, and events). A compound statement can contain multiple statements, enclosed by the 'BEGIN' and 'END' keywords. STATEMENT_LIST represents a list of one or more statements, each terminated by a semicolon (';') statement delimiter. The STATEMENT_LIST itself is optional, so the empty compound statement ('BEGIN END') is legal.

*note 'BEGIN ... END': begin-end. blocks can be nested.

Use of multiple statements requires that a client is able to send statement strings containing the ';' statement delimiter. In the note 'mysql': mysql. command-line client, this is handled with the 'delimiter' command. Changing the ';' end-of-statement delimiter (for example, to '//') permit ';' to be used in a program body. For an example, see note stored-programs-defining::.

A note 'BEGIN ... END': begin-end. block can be labeled. See note statement-labels::.

The optional '[NOT] ATOMIC' clause is not supported. This means that no transactional savepoint is set at the start of the instruction block and the 'BEGIN' clause used in this context has no effect on the current transaction.

Note:

Within all stored programs, the parser treats note 'BEGIN [WORK]': commit. as the beginning of a note 'BEGIN ... END': begin-end. block. To begin a transaction in this context, use *note 'START TRANSACTION': commit. instead.

 File: manual.info.tmp, Node: statement-labels, Next: declare, Prev: begin-end, Up: sql-compound-statements

13.6.2 Statement Labels

 [BEGIN_LABEL:] BEGIN
     [STATEMENT_LIST]
 END [END_LABEL]

 [BEGIN_LABEL:] LOOP
     STATEMENT_LIST
 END LOOP [END_LABEL]

 [BEGIN_LABEL:] REPEAT
     STATEMENT_LIST
 UNTIL SEARCH_CONDITION
 END REPEAT [END_LABEL]

 [BEGIN_LABEL:] WHILE SEARCH_CONDITION DO
     STATEMENT_LIST
 END WHILE [END_LABEL]

Labels are permitted for note 'BEGIN ... END': begin-end. blocks and for the note 'LOOP': loop, note 'REPEAT': repeat, and note 'WHILE': while. statements. Label use for those statements follows these rules:

To refer to a label within the labeled construct, use an note 'ITERATE': iterate. or note 'LEAVE': leave. statement. The following example uses those statements to continue iterating or terminate the loop:

 CREATE PROCEDURE doiterate(p1 INT)
 BEGIN
   label1: LOOP
     SET p1 = p1 + 1;
     IF p1 < 10 THEN ITERATE label1; END IF;
     LEAVE label1;
   END LOOP label1;
 END;

The scope of a block label does not include the code for handlers declared within the block. For details, see *note declare-handler::.

 File: manual.info.tmp, Node: declare, Next: stored-program-variables, Prev: statement-labels, Up: sql-compound-statements

13.6.3 DECLARE Statement

The *note 'DECLARE': declare. statement is used to define various items local to a program:

note 'DECLARE': declare. is permitted only inside a note 'BEGIN ... END': begin-end. compound statement and must be at its start, before any other statements.

Declarations must follow a certain order. Cursor declarations must appear before handler declarations. Variable and condition declarations must appear before cursor or handler declarations.

 File: manual.info.tmp, Node: stored-program-variables, Next: flow-control-statements, Prev: declare, Up: sql-compound-statements

13.6.4 Variables in Stored Programs

System variables and user-defined variables can be used in stored programs, just as they can be used outside stored-program context. In addition, stored programs can use 'DECLARE' to define local variables, and stored routines (procedures and functions) can be declared to take parameters that communicate values between the routine and its caller.

For information about the scope of local variables and how MySQL resolves ambiguous names, see *note local-variable-scope::.

It is not permitted to assign the value 'DEFAULT' to stored procedure or function parameters or stored program local variables (for example with a 'SET VAR_NAME = DEFAULT' statement). In MySQL 5.7, this results in a syntax error.

 File: manual.info.tmp, Node: declare-local-variable, Next: local-variable-scope, Prev: stored-program-variables, Up: stored-program-variables

13.6.4.1 Local Variable DECLARE Statement .........................................

 DECLARE VAR_NAME [, VAR_NAME] ... TYPE [DEFAULT VALUE]

This statement declares local variables within stored programs. To provide a default value for a variable, include a 'DEFAULT' clause. The value can be specified as an expression; it need not be a constant. If the 'DEFAULT' clause is missing, the initial value is 'NULL'.

Local variables are treated like stored routine parameters with respect to data type and overflow checking. See *note create-procedure::.

Variable declarations must appear before cursor or handler declarations.

Local variable names are not case-sensitive. Permissible characters and quoting rules are the same as for other identifiers, as described in *note identifiers::.

The scope of a local variable is the *note 'BEGIN ... END': begin-end. block within which it is declared. The variable can be referred to in blocks nested within the declaring block, except those blocks that declare a variable with the same name.

For examples of variable declarations, see *note local-variable-scope::.

 File: manual.info.tmp, Node: local-variable-scope, Prev: declare-local-variable, Up: stored-program-variables

13.6.4.2 Local Variable Scope and Resolution ............................................

The scope of a local variable is the *note 'BEGIN ... END': begin-end. block within which it is declared. The variable can be referred to in blocks nested within the declaring block, except those blocks that declare a variable with the same name.

Because local variables are in scope only during stored program execution, references to them are not permitted in prepared statements created within a stored program. Prepared statement scope is the current session, not the stored program, so the statement could be executed after the program ends, at which point the variables would no longer be in scope. For example, 'SELECT ... INTO LOCAL_VAR' cannot be used as a prepared statement. This restriction also applies to stored procedure and function parameters. See *note prepare::.

A local variable should not have the same name as a table column. If an SQL statement, such as a *note 'SELECT ... INTO': select. statement, contains a reference to a column and a declared local variable with the same name, MySQL currently interprets the reference as the name of a variable. Consider the following procedure definition:

 CREATE PROCEDURE sp1 (x VARCHAR(5))
 BEGIN
   DECLARE xname VARCHAR(5) DEFAULT 'bob';
   DECLARE newname VARCHAR(5);
   DECLARE xid INT;

   SELECT xname, id INTO newname, xid
     FROM table1 WHERE xname = xname;
   SELECT newname;
 END;

MySQL interprets 'xname' in the *note 'SELECT': select. statement as a reference to the 'xname' variable rather than the 'xname' column. Consequently, when the procedure 'sp1()'is called, the 'newname' variable returns the value ''bob'' regardless of the value of the 'table1.xname' column.

Similarly, the cursor definition in the following procedure contains a *note 'SELECT': select. statement that refers to 'xname'. MySQL interprets this as a reference to the variable of that name rather than a column reference.

 CREATE PROCEDURE sp2 (x VARCHAR(5))
 BEGIN
   DECLARE xname VARCHAR(5) DEFAULT 'bob';
   DECLARE newname VARCHAR(5);
   DECLARE xid INT;
   DECLARE done TINYINT DEFAULT 0;
   DECLARE cur1 CURSOR FOR SELECT xname, id FROM table1;
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

   OPEN cur1;
   read_loop: LOOP
     FETCH FROM cur1 INTO newname, xid;
     IF done THEN LEAVE read_loop; END IF;
     SELECT newname;
   END LOOP;
   CLOSE cur1;
 END;

See also *note stored-program-restrictions::.

 File: manual.info.tmp, Node: flow-control-statements, Next: cursors, Prev: stored-program-variables, Up: sql-compound-statements

13.6.5 Flow Control Statements

MySQL supports the note 'IF': if, note 'CASE': case, note 'ITERATE': iterate, note 'LEAVE': leave. note 'LOOP': loop, note 'WHILE': while, and note 'REPEAT': repeat. constructs for flow control within stored programs. It also supports note 'RETURN': return. within stored functions.

Many of these constructs contain other statements, as indicated by the grammar specifications in the following sections. Such constructs may be nested. For example, an note 'IF': if. statement might contain a note 'WHILE': while. loop, which itself contains a *note 'CASE': case. statement.

MySQL does not support 'FOR' loops.

 File: manual.info.tmp, Node: case, Next: if, Prev: flow-control-statements, Up: flow-control-statements

13.6.5.1 CASE Statement .......................

 CASE CASE_VALUE
     WHEN WHEN_VALUE THEN STATEMENT_LIST
     [WHEN WHEN_VALUE THEN STATEMENT_LIST] ...
     [ELSE STATEMENT_LIST]
 END CASE

Or:

 CASE
     WHEN SEARCH_CONDITION THEN STATEMENT_LIST
     [WHEN SEARCH_CONDITION THEN STATEMENT_LIST] ...
     [ELSE STATEMENT_LIST]
 END CASE

The *note 'CASE': case. statement for stored programs implements a complex conditional construct.

Note:

There is also a 'CASE' operator, which differs from the note 'CASE': case. statement described here. See note flow-control-functions::. The *note 'CASE': case. statement cannot have an 'ELSE NULL' clause, and it is terminated with 'END CASE' instead of 'END'.

For the first syntax, CASE_VALUE is an expression. This value is compared to the WHEN_VALUE expression in each 'WHEN' clause until one of them is equal. When an equal WHEN_VALUE is found, the corresponding 'THEN' clause STATEMENT_LIST executes. If no WHEN_VALUE is equal, the 'ELSE' clause STATEMENT_LIST executes, if there is one.

This syntax cannot be used to test for equality with 'NULL' because 'NULL = NULL' is false. See *note working-with-null::.

For the second syntax, each 'WHEN' clause SEARCH_CONDITION expression is evaluated until one is true, at which point its corresponding 'THEN' clause STATEMENT_LIST executes. If no SEARCH_CONDITION is equal, the 'ELSE' clause STATEMENT_LIST executes, if there is one.

If no WHEN_VALUE or SEARCH_CONDITION matches the value tested and the *note 'CASE': case. statement contains no 'ELSE' clause, a 'Case not found for CASE statement' error results.

Each STATEMENT_LIST consists of one or more SQL statements; an empty STATEMENT_LIST is not permitted.

To handle situations where no value is matched by any 'WHEN' clause, use an 'ELSE' containing an empty *note 'BEGIN ... END': begin-end. block, as shown in this example. (The indentation used here in the 'ELSE' clause is for purposes of clarity only, and is not otherwise significant.)

 DELIMITER |

 CREATE PROCEDURE p()
   BEGIN
     DECLARE v INT DEFAULT 1;

     CASE v
       WHEN 2 THEN SELECT v;
       WHEN 3 THEN SELECT 0;
       ELSE
         BEGIN
         END;
     END CASE;
   END;
   |

 File: manual.info.tmp, Node: if, Next: iterate, Prev: case, Up: flow-control-statements

13.6.5.2 IF Statement .....................

 IF SEARCH_CONDITION THEN STATEMENT_LIST
     [ELSEIF SEARCH_CONDITION THEN STATEMENT_LIST] ...
     [ELSE STATEMENT_LIST]
 END IF

The *note 'IF': if. statement for stored programs implements a basic conditional construct.

Note:

There is also an 'IF()' function, which differs from the note 'IF': if. statement described here. See note flow-control-functions::. The *note 'IF': if. statement can have 'THEN', 'ELSE', and 'ELSEIF' clauses, and it is terminated with 'END IF'.

If a given SEARCH_CONDITION evaluates to true, the corresponding 'THEN' or 'ELSEIF' clause STATEMENT_LIST executes. If no SEARCH_CONDITION matches, the 'ELSE' clause STATEMENT_LIST executes.

Each STATEMENT_LIST consists of one or more SQL statements; an empty STATEMENT_LIST is not permitted.

An 'IF ... END IF' block, like all other flow-control blocks used within stored programs, must be terminated with a semicolon, as shown in this example:

 DELIMITER //

 CREATE FUNCTION SimpleCompare(n INT, m INT)
   RETURNS VARCHAR(20)

   BEGIN
     DECLARE s VARCHAR(20);

     IF n > m THEN SET s = '>';
     ELSEIF n = m THEN SET s = '=';
     ELSE SET s = '<';
     END IF;

     SET s = CONCAT(n, ' ', s, ' ', m);

     RETURN s;
   END //

 DELIMITER ;

As with other flow-control constructs, 'IF ... END IF' blocks may be nested within other flow-control constructs, including other note 'IF': if. statements. Each note 'IF': if. must be terminated by its own 'END IF' followed by a semicolon. You can use indentation to make nested flow-control blocks more easily readable by humans (although this is not required by MySQL), as shown here:

 DELIMITER //

 CREATE FUNCTION VerboseCompare (n INT, m INT)
   RETURNS VARCHAR(50)

   BEGIN
     DECLARE s VARCHAR(50);

     IF n = m THEN SET s = 'equals';
     ELSE
       IF n > m THEN SET s = 'greater';
       ELSE SET s = 'less';
       END IF;

       SET s = CONCAT('is ', s, ' than');
     END IF;

     SET s = CONCAT(n, ' ', s, ' ', m, '.');

     RETURN s;
   END //

 DELIMITER ;

In this example, the inner *note 'IF': if. is evaluated only if 'n' is not equal to 'm'.

 File: manual.info.tmp, Node: iterate, Next: leave, Prev: if, Up: flow-control-statements

13.6.5.3 ITERATE Statement ..........................

 ITERATE LABEL

note 'ITERATE': iterate. can appear only within note 'LOOP': loop, note 'REPEAT': repeat, and note 'WHILE': while. statements. *note 'ITERATE': iterate. means 'start the loop again.'

For an example, see *note loop::.

 File: manual.info.tmp, Node: leave, Next: loop, Prev: iterate, Up: flow-control-statements

13.6.5.4 LEAVE Statement ........................

 LEAVE LABEL

This statement is used to exit the flow control construct that has the given label. If the label is for the outermost stored program block, *note 'LEAVE': leave. exits the program.

note 'LEAVE': leave. can be used within note 'BEGIN ... END': begin-end. or loop constructs (note 'LOOP': loop, note 'REPEAT': repeat, *note 'WHILE': while.).

For an example, see *note loop::.

 File: manual.info.tmp, Node: loop, Next: repeat, Prev: leave, Up: flow-control-statements

13.6.5.5 LOOP Statement .......................

 [BEGIN_LABEL:] LOOP
     STATEMENT_LIST
 END LOOP [END_LABEL]

note 'LOOP': loop. implements a simple loop construct, enabling repeated execution of the statement list, which consists of one or more statements, each terminated by a semicolon (';') statement delimiter. The statements within the loop are repeated until the loop is terminated. Usually, this is accomplished with a note 'LEAVE': leave. statement. Within a stored function, *note 'RETURN': return. can also be used, which exits the function entirely.

Neglecting to include a loop-termination statement results in an infinite loop.

A note 'LOOP': loop. statement can be labeled. For the rules regarding label use, see note statement-labels::.

Example:

 CREATE PROCEDURE doiterate(p1 INT)
 BEGIN
   label1: LOOP
     SET p1 = p1 + 1;
     IF p1 < 10 THEN
       ITERATE label1;
     END IF;
     LEAVE label1;
   END LOOP label1;
   SET @x = p1;
 END;

 File: manual.info.tmp, Node: repeat, Next: return, Prev: loop, Up: flow-control-statements

13.6.5.6 REPEAT Statement .........................

 [BEGIN_LABEL:] REPEAT
     STATEMENT_LIST
 UNTIL SEARCH_CONDITION
 END REPEAT [END_LABEL]

The statement list within a note 'REPEAT': repeat. statement is repeated until the SEARCH_CONDITION expression is true. Thus, a note 'REPEAT': repeat. always enters the loop at least once. STATEMENT_LIST consists of one or more statements, each terminated by a semicolon (';') statement delimiter.

A note 'REPEAT': repeat. statement can be labeled. For the rules regarding label use, see note statement-labels::.

Example:

 mysql> delimiter //

 mysql> CREATE PROCEDURE dorepeat(p1 INT)
        BEGIN
          SET @x = 0;
          REPEAT
            SET @x = @x + 1;
          UNTIL @x > p1 END REPEAT;
        END
        //
 Query OK, 0 rows affected (0.00 sec)

 mysql> CALL dorepeat(1000)//
 Query OK, 0 rows affected (0.00 sec)

 mysql> SELECT @x//
 +------+
 | @x   |
 +------+
 | 1001 |
 +------+
 1 row in set (0.00 sec)

 File: manual.info.tmp, Node: return, Next: while, Prev: repeat, Up: flow-control-statements

13.6.5.7 RETURN Statement .........................

 RETURN EXPR

The note 'RETURN': return. statement terminates execution of a stored function and returns the value EXPR to the function caller. There must be at least one note 'RETURN': return. statement in a stored function. There may be more than one if the function has multiple exit points.

This statement is not used in stored procedures, triggers, or events. The *note 'LEAVE': leave. statement can be used to exit a stored program of those types.

 File: manual.info.tmp, Node: while, Prev: return, Up: flow-control-statements

13.6.5.8 WHILE Statement ........................

 [BEGIN_LABEL:] WHILE SEARCH_CONDITION DO
     STATEMENT_LIST
 END WHILE [END_LABEL]

The statement list within a *note 'WHILE': while. statement is repeated as long as the SEARCH_CONDITION expression is true. STATEMENT_LIST consists of one or more SQL statements, each terminated by a semicolon (';') statement delimiter.

A note 'WHILE': while. statement can be labeled. For the rules regarding label use, see note statement-labels::.

Example:

 CREATE PROCEDURE dowhile()
 BEGIN
   DECLARE v1 INT DEFAULT 5;

   WHILE v1 > 0 DO
     ...
     SET v1 = v1 - 1;
   END WHILE;
 END;

 File: manual.info.tmp, Node: cursors, Next: condition-handling, Prev: flow-control-statements, Up: sql-compound-statements

13.6.6 Cursors

MySQL supports cursors inside stored programs. The syntax is as in embedded SQL. Cursors have these properties:

Cursor declarations must appear before handler declarations and after variable and condition declarations.

Example:

 CREATE PROCEDURE curdemo()
 BEGIN
   DECLARE done INT DEFAULT FALSE;
   DECLARE a CHAR(16);
   DECLARE b, c INT;
   DECLARE cur1 CURSOR FOR SELECT id,data FROM test.t1;
   DECLARE cur2 CURSOR FOR SELECT i FROM test.t2;
   DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;

   OPEN cur1;
   OPEN cur2;

   read_loop: LOOP
     FETCH cur1 INTO a, b;
     FETCH cur2 INTO c;
     IF done THEN
       LEAVE read_loop;
     END IF;
     IF b < c THEN
       INSERT INTO test.t3 VALUES (a,b);
     ELSE
       INSERT INTO test.t3 VALUES (a,c);
     END IF;
   END LOOP;

   CLOSE cur1;
   CLOSE cur2;
 END;

 File: manual.info.tmp, Node: close, Next: declare-cursor, Prev: cursors, Up: cursors

13.6.6.1 Cursor CLOSE Statement ...............................

 CLOSE CURSOR_NAME

This statement closes a previously opened cursor. For an example, see *note cursors::.

An error occurs if the cursor is not open.

If not closed explicitly, a cursor is closed at the end of the *note 'BEGIN ... END': begin-end. block in which it was declared.

 File: manual.info.tmp, Node: declare-cursor, Next: fetch, Prev: close, Up: cursors

13.6.6.2 Cursor DECLARE Statement .................................

 DECLARE CURSOR_NAME CURSOR FOR SELECT_STATEMENT

This statement declares a cursor and associates it with a note 'SELECT': select. statement that retrieves the rows to be traversed by the cursor. To fetch the rows later, use a note 'FETCH': fetch. statement. The number of columns retrieved by the note 'SELECT': select. statement must match the number of output variables specified in the note 'FETCH': fetch. statement.

The *note 'SELECT': select. statement cannot have an 'INTO' clause.

Cursor declarations must appear before handler declarations and after variable and condition declarations.

A stored program may contain multiple cursor declarations, but each cursor declared in a given block must have a unique name. For an example, see *note cursors::.

For information available through *note 'SHOW': show. statements, it is possible in many cases to obtain equivalent information by using a cursor with an 'INFORMATION_SCHEMA' table.

 File: manual.info.tmp, Node: fetch, Next: open, Prev: declare-cursor, Up: cursors

13.6.6.3 Cursor FETCH Statement ...............................

 FETCH [[NEXT] FROM] CURSOR_NAME INTO VAR_NAME [, VAR_NAME] ...

This statement fetches the next row for the note 'SELECT': select. statement associated with the specified cursor (which must be open), and advances the cursor pointer. If a row exists, the fetched columns are stored in the named variables. The number of columns retrieved by the note 'SELECT': select. statement must match the number of output variables specified in the *note 'FETCH': fetch. statement.

If no more rows are available, a No Data condition occurs with SQLSTATE value ''02000''. To detect this condition, you can set up a handler for it (or for a 'NOT FOUND' condition). For an example, see *note cursors::.

Be aware that another operation, such as a 'SELECT' or another 'FETCH', may also cause the handler to execute by raising the same condition. If it is necessary to distinguish which operation raised the condition, place the operation within its own *note 'BEGIN ... END': begin-end. block so that it can be associated with its own handler.

 File: manual.info.tmp, Node: open, Next: cursor-restrictions, Prev: fetch, Up: cursors

13.6.6.4 Cursor OPEN Statement ..............................

 OPEN CURSOR_NAME

This statement opens a previously declared cursor. For an example, see *note cursors::.

 File: manual.info.tmp, Node: cursor-restrictions, Prev: open, Up: cursors

13.6.6.5 Restrictions on Server-Side Cursors ............................................

Server-side cursors are implemented in the C API using the 'mysql_stmt_attr_set()' (https://dev.mysql.com/doc/c-api/5.7/en/mysql-stmt-attr-set.html) function. The same implementation is used for cursors in stored routines. A server-side cursor enables a result set to be generated on the server side, but not transferred to the client except for those rows that the client requests. For example, if a client executes a query but is only interested in the first row, the remaining rows are not transferred.

In MySQL, a server-side cursor is materialized into an internal temporary table. Initially, this is a 'MEMORY' table, but is converted to a 'MyISAM' table when its size exceeds the minimum value of the 'max_heap_table_size' and 'tmp_table_size' system variables. The same restrictions apply to internal temporary tables created to hold the result set for a cursor as for other uses of internal temporary tables. See *note internal-temporary-tables::. One limitation of the implementation is that for a large result set, retrieving its rows through a cursor might be slow.

Cursors are read only; you cannot use a cursor to update rows.

'UPDATE WHERE CURRENT OF' and 'DELETE WHERE CURRENT OF' are not implemented, because updatable cursors are not supported.

Cursors are nonholdable (not held open after a commit).

Cursors are asensitive.

Cursors are nonscrollable.

Cursors are not named. The statement handler acts as the cursor ID.

You can have open only a single cursor per prepared statement. If you need several cursors, you must prepare several statements.

You cannot use a cursor for a statement that generates a result set if the statement is not supported in prepared mode. This includes statements such as note 'CHECK TABLE': check-table, 'HANDLER READ', and note 'SHOW BINLOG EVENTS': show-binlog-events.

 File: manual.info.tmp, Node: condition-handling, Prev: cursors, Up: sql-compound-statements

13.6.7 Condition Handling

Conditions may arise during stored program execution that require special handling, such as exiting the current program block or continuing execution. Handlers can be defined for general conditions such as warnings or exceptions, or for specific conditions such as a particular error code. Specific conditions can be assigned names and referred to that way in handlers.

To name a condition, use the note 'DECLARE ... CONDITION': declare-condition. statement. To declare a handler, use the note 'DECLARE ... HANDLER': declare-handler. statement. See note declare-condition::, and note declare-handler::. For information about how the server chooses handlers when a condition occurs, see *note handler-scope::.

To raise a condition, use the note 'SIGNAL': signal. statement. To modify condition information within a condition handler, use note 'RESIGNAL': resignal. See note declare-condition::, and note declare-handler::.

To retrieve information from the diagnostics area, use the note 'GET DIAGNOSTICS': get-diagnostics. statement (see note get-diagnostics::). For information about the diagnostics area, see *note diagnostics-area::.

 File: manual.info.tmp, Node: declare-condition, Next: declare-handler, Prev: condition-handling, Up: condition-handling

13.6.7.1 DECLARE ... CONDITION Statement ........................................

 DECLARE CONDITION_NAME CONDITION FOR CONDITION_VALUE

 CONDITION_VALUE: {
     MYSQL_ERROR_CODE
   | SQLSTATE [VALUE] SQLSTATE_VALUE
 }

The note 'DECLARE ... CONDITION': declare-condition. statement declares a named error condition, associating a name with a condition that needs specific handling. The name can be referred to in a subsequent note 'DECLARE ... HANDLER': declare-handler. statement (see *note declare-handler::).

Condition declarations must appear before cursor or handler declarations.

The CONDITION_VALUE for *note 'DECLARE ... CONDITION': declare-condition. indicates the specific condition or class of conditions to associate with the condition name. It can take the following forms:

Condition names referred to in note 'SIGNAL': signal. or use note 'RESIGNAL': resignal. statements must be associated with SQLSTATE values, not MySQL error codes.

Using names for conditions can help make stored program code clearer. For example, this handler applies to attempts to drop a nonexistent table, but that is apparent only if you know that 1051 is the MySQL error code for 'unknown table':

 DECLARE CONTINUE HANDLER FOR 1051
   BEGIN
     -- body of handler
   END;

By declaring a name for the condition, the purpose of the handler is more readily seen:

 DECLARE no_such_table CONDITION FOR 1051;
 DECLARE CONTINUE HANDLER FOR no_such_table
   BEGIN
     -- body of handler
   END;

Here is a named condition for the same condition, but based on the corresponding SQLSTATE value rather than the MySQL error code:

 DECLARE no_such_table CONDITION FOR SQLSTATE '42S02';
 DECLARE CONTINUE HANDLER FOR no_such_table
   BEGIN
     -- body of handler
   END;

 File: manual.info.tmp, Node: declare-handler, Next: get-diagnostics, Prev: declare-condition, Up: condition-handling

13.6.7.2 DECLARE ... HANDLER Statement ......................................

 DECLARE HANDLER_ACTION HANDLER
     FOR CONDITION_VALUE [, CONDITION_VALUE] ...
     STATEMENT

 HANDLER_ACTION: {
     CONTINUE
   | EXIT
   | UNDO
 }

 CONDITION_VALUE: {
     MYSQL_ERROR_CODE
   | SQLSTATE [VALUE] SQLSTATE_VALUE
   | CONDITION_NAME
   | SQLWARNING
   | NOT FOUND
   | SQLEXCEPTION
 }

The note 'DECLARE ... HANDLER': declare-handler. statement specifies a handler that deals with one or more conditions. If one of these conditions occurs, the specified STATEMENT executes. STATEMENT can be a simple statement such as 'SET VAR_NAME = VALUE', or a compound statement written using 'BEGIN' and 'END' (see note begin-end::).

Handler declarations must appear after variable or condition declarations.

The HANDLER_ACTION value indicates what action the handler takes after execution of the handler statement:

The CONDITION_VALUE for *note 'DECLARE ... HANDLER': declare-handler. indicates the specific condition or class of conditions that activates the handler. It can take the following forms:

For information about how the server chooses handlers when a condition occurs, see *note handler-scope::.

If a condition occurs for which no handler has been declared, the action taken depends on the condition class:

The following example uses a handler for 'SQLSTATE '23000'', which occurs for a duplicate-key error:

 mysql> CREATE TABLE test.t (s1 INT, PRIMARY KEY (s1));
 Query OK, 0 rows affected (0.00 sec)

 mysql> delimiter //

 mysql> CREATE PROCEDURE handlerdemo ()
        BEGIN
          DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET @x2 = 1;
          SET @x = 1;
          INSERT INTO test.t VALUES (1);
          SET @x = 2;
          INSERT INTO test.t VALUES (1);
          SET @x = 3;
        END;
        //
 Query OK, 0 rows affected (0.00 sec)

 mysql> CALL handlerdemo()//
 Query OK, 0 rows affected (0.00 sec)

 mysql> SELECT @x//
     +------+
     | @x   |
     +------+
     | 3    |
     +------+
     1 row in set (0.00 sec)

Notice that '@x' is '3' after the procedure executes, which shows that execution continued to the end of the procedure after the error occurred. If the note 'DECLARE ... HANDLER': declare-handler. statement had not been present, MySQL would have taken the default action ('EXIT') after the second note 'INSERT': insert. failed due to the 'PRIMARY KEY' constraint, and 'SELECT @x' would have returned '2'.

To ignore a condition, declare a 'CONTINUE' handler for it and associate it with an empty block. For example:

 DECLARE CONTINUE HANDLER FOR SQLWARNING BEGIN END;

The scope of a block label does not include the code for handlers declared within the block. Therefore, the statement associated with a handler cannot use note 'ITERATE': iterate. or note 'LEAVE': leave. to refer to labels for blocks that enclose the handler declaration. Consider the following example, where the *note 'REPEAT': repeat. block has a label of 'retry':

 CREATE PROCEDURE p ()
 BEGIN
   DECLARE i INT DEFAULT 3;
   retry:
     REPEAT
       BEGIN
         DECLARE CONTINUE HANDLER FOR SQLWARNING
           BEGIN
             ITERATE retry;    # illegal
           END;
         IF i < 0 THEN
           LEAVE retry;        # legal
         END IF;
         SET i = i - 1;
       END;
     UNTIL FALSE END REPEAT;
 END;

The 'retry' label is in scope for the *note 'IF': if. statement within the block. It is not in scope for the 'CONTINUE' handler, so the reference there is invalid and results in an error:

 ERROR 1308 (42000): LEAVE with no matching label: retry

To avoid references to outer labels in handlers, use one of these strategies:

 File: manual.info.tmp, Node: get-diagnostics, Next: resignal, Prev: declare-handler, Up: condition-handling

13.6.7.3 GET DIAGNOSTICS Statement ..................................

 GET [CURRENT | STACKED] DIAGNOSTICS {
     STATEMENT_INFORMATION_ITEM
     [, STATEMENT_INFORMATION_ITEM] ...
   | CONDITION CONDITION_NUMBER
     CONDITION_INFORMATION_ITEM
     [, CONDITION_INFORMATION_ITEM] ...
 }

 STATEMENT_INFORMATION_ITEM:
     TARGET = STATEMENT_INFORMATION_ITEM_NAME

 CONDITION_INFORMATION_ITEM:
     TARGET = CONDITION_INFORMATION_ITEM_NAME

 STATEMENT_INFORMATION_ITEM_NAME: {
     NUMBER
   | ROW_COUNT
 }

 CONDITION_INFORMATION_ITEM_NAME: {
     CLASS_ORIGIN
   | SUBCLASS_ORIGIN
   | RETURNED_SQLSTATE
   | MESSAGE_TEXT
   | MYSQL_ERRNO
   | CONSTRAINT_CATALOG
   | CONSTRAINT_SCHEMA
   | CONSTRAINT_NAME
   | CATALOG_NAME
   | SCHEMA_NAME
   | TABLE_NAME
   | COLUMN_NAME
   | CURSOR_NAME
 }

 CONDITION_NUMBER, TARGET:
     (see following discussion)

SQL statements produce diagnostic information that populates the diagnostics area. The note 'GET DIAGNOSTICS': get-diagnostics. statement enables applications to inspect this information. (You can also use note 'SHOW WARNINGS': show-warnings. or *note 'SHOW ERRORS': show-errors. to see conditions or errors.)

No special privileges are required to execute *note 'GET DIAGNOSTICS': get-diagnostics.

The keyword 'CURRENT' means to retrieve information from the current diagnostics area. The keyword 'STACKED' means to retrieve information from the second diagnostics area, which is available only if the current context is a condition handler. If neither keyword is given, the default is to use the current diagnostics area.

The note 'GET DIAGNOSTICS': get-diagnostics. statement is typically used in a handler within a stored program. It is a MySQL extension that note 'GET [CURRENT] DIAGNOSTICS': get-diagnostics. is permitted outside handler context to check the execution of any SQL statement. For example, if you invoke the *note 'mysql': mysql. client program, you can enter these statements at the prompt:

 mysql> DROP TABLE test.no_such_table;
 ERROR 1051 (42S02): Unknown table 'test.no_such_table'
 mysql> GET DIAGNOSTICS CONDITION 1
          @p1 = RETURNED_SQLSTATE, @p2 = MESSAGE_TEXT;
 mysql> SELECT @p1, @p2;
 +-------+------------------------------------+
 | @p1   | @p2                                |
 +-------+------------------------------------+
 | 42S02 | Unknown table 'test.no_such_table' |
 +-------+------------------------------------+

This extension applies only to the current diagnostics area. It does not apply to the second diagnostics area because 'GET STACKED DIAGNOSTICS' is permitted only if the current context is a condition handler. If that is not the case, a 'GET STACKED DIAGNOSTICS when handler not active' error occurs.

For a description of the diagnostics area, see *note diagnostics-area::. Briefly, it contains two kinds of information:

For a statement that produces three conditions, the diagnostics area contains statement and condition information like this:

 Statement information:
   row count
   ... other statement information items ...
 Condition area list:
   Condition area 1:
     error code for condition 1
     error message for condition 1
     ... other condition information items ...
   Condition area 2:
     error code for condition 2:
     error message for condition 2
     ... other condition information items ...
   Condition area 3:
     error code for condition 3
     error message for condition 3
     ... other condition information items ...

*note 'GET DIAGNOSTICS': get-diagnostics. can obtain either statement or condition information, but not both in the same statement:

The retrieval list specifies one or more 'TARGET = ITEM_NAME' assignments, separated by commas. Each assignment names a target variable and either a STATEMENT_INFORMATION_ITEM_NAME or CONDITION_INFORMATION_ITEM_NAME designator, depending on whether the statement retrieves statement or condition information.

Valid TARGET designators for storing item information can be stored procedure or function parameters, stored program local variables declared with *note 'DECLARE': declare, or user-defined variables.

Valid CONDITION_NUMBER designators can be stored procedure or function parameters, stored program local variables declared with *note 'DECLARE': declare, user-defined variables, system variables, or literals. A character literal may include a _CHARSET introducer. A warning occurs if the condition number is not in the range from 1 to the number of condition areas that have information. In this case, the warning is added to the diagnostics area without clearing it.

When a condition occurs, MySQL does not populate all condition items recognized by *note 'GET DIAGNOSTICS': get-diagnostics. For example:

 mysql> GET DIAGNOSTICS CONDITION 1
          @p5 = SCHEMA_NAME, @p6 = TABLE_NAME;
 mysql> SELECT @p5, @p6;
 +------+------+
 | @p5  | @p6  |
 +------+------+
 |      |      |
 +------+------+

In standard SQL, if there are multiple conditions, the first condition relates to the 'SQLSTATE' value returned for the previous SQL statement. In MySQL, this is not guaranteed. To get the main error, you cannot do this:

 GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;

Instead, retrieve the condition count first, then use it to specify which condition number to inspect:

 GET DIAGNOSTICS @cno = NUMBER;
 GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;

For information about permissible statement and condition information items, and which ones are populated when a condition occurs, see *note diagnostics-area-information-items::.

Here is an example that uses note 'GET DIAGNOSTICS': get-diagnostics. and an exception handler in stored procedure context to assess the outcome of an insert operation. If the insert was successful, the procedure uses note 'GET DIAGNOSTICS': get-diagnostics. to get the rows-affected count. This shows that you can use *note 'GET DIAGNOSTICS': get-diagnostics. multiple times to retrieve information about a statement as long as the current diagnostics area has not been cleared.

 CREATE PROCEDURE do_insert(value INT)
 BEGIN
   -- Declare variables to hold diagnostics area information
   DECLARE code CHAR(5) DEFAULT '00000';
   DECLARE msg TEXT;
   DECLARE nrows INT;
   DECLARE result TEXT;
   -- Declare exception handler for failed insert
   DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
     BEGIN
       GET DIAGNOSTICS CONDITION 1
         code = RETURNED_SQLSTATE, msg = MESSAGE_TEXT;
     END;

   -- Perform the insert
   INSERT INTO t1 (int_col) VALUES(value);
   -- Check whether the insert was successful
   IF code = '00000' THEN
     GET DIAGNOSTICS nrows = ROW_COUNT;
     SET result = CONCAT('insert succeeded, row count = ',nrows);
   ELSE
     SET result = CONCAT('insert failed, error = ',code,', message = ',msg);
   END IF;
   -- Say what happened
   SELECT result;
 END;

Suppose that 't1.int_col' is an integer column that is declared as 'NOT NULL'. The procedure produces these results when invoked to insert non-'NULL' and 'NULL' values, respectively:

 mysql> CALL do_insert(1);
 +---------------------------------+
 | result                          |
 +---------------------------------+
 | insert succeeded, row count = 1 |
 +---------------------------------+

 mysql> CALL do_insert(NULL);
 +-------------------------------------------------------------------------+
 | result                                                                  |
 +-------------------------------------------------------------------------+
 | insert failed, error = 23000, message = Column 'int_col' cannot be null |
 +-------------------------------------------------------------------------+

When a condition handler activates, a push to the diagnostics area stack occurs:

The next example shows how 'GET STACKED DIAGNOSTICS' can be used within a handler to obtain information about the handled exception, even after the current diagnostics area has been modified by handler statements.

Within a stored procedure 'p()', we attempt to insert two values into a table that contains a 'TEXT NOT NULL' column. The first value is a non-'NULL' string and the second is 'NULL'. The column prohibits 'NULL' values, so the first insert succeeds but the second causes an exception. The procedure includes an exception handler that maps attempts to insert 'NULL' into inserts of the empty string:

 DROP TABLE IF EXISTS t1;
 CREATE TABLE t1 (c1 TEXT NOT NULL);
 DROP PROCEDURE IF EXISTS p;
 delimiter //
 CREATE PROCEDURE p ()
 BEGIN
   -- Declare variables to hold diagnostics area information
   DECLARE errcount INT;
   DECLARE errno INT;
   DECLARE msg TEXT;
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     -- Here the current DA is nonempty because no prior statements
     -- executing within the handler have cleared it
     GET CURRENT DIAGNOSTICS CONDITION 1
       errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
     SELECT 'current DA before mapped insert' AS op, errno, msg;
     GET STACKED DIAGNOSTICS CONDITION 1
       errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
     SELECT 'stacked DA before mapped insert' AS op, errno, msg;

     -- Map attempted NULL insert to empty string insert
     INSERT INTO t1 (c1) VALUES('');

     -- Here the current DA should be empty (if the INSERT succeeded),
     -- so check whether there are conditions before attempting to
     -- obtain condition information
     GET CURRENT DIAGNOSTICS errcount = NUMBER;
     IF errcount = 0
     THEN
       SELECT 'mapped insert succeeded, current DA is empty' AS op;
     ELSE
       GET CURRENT DIAGNOSTICS CONDITION 1
         errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
       SELECT 'current DA after mapped insert' AS op, errno, msg;
     END IF ;
     GET STACKED DIAGNOSTICS CONDITION 1
       errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
     SELECT 'stacked DA after mapped insert' AS op, errno, msg;
   END;
   INSERT INTO t1 (c1) VALUES('string 1');
   INSERT INTO t1 (c1) VALUES(NULL);
 END;
 //
 delimiter ;
 CALL p();
 SELECT * FROM t1;

When the handler activates, a copy of the current diagnostics area is pushed to the diagnostics area stack. The handler first displays the contents of the current and stacked diagnostics areas, which are both the same initially:

 +---------------------------------+-------+----------------------------+
 | op                              | errno | msg                        |
 +---------------------------------+-------+----------------------------+
 | current DA before mapped insert |  1048 | Column 'c1' cannot be null |
 +---------------------------------+-------+----------------------------+

 +---------------------------------+-------+----------------------------+
 | op                              | errno | msg                        |
 +---------------------------------+-------+----------------------------+
 | stacked DA before mapped insert |  1048 | Column 'c1' cannot be null |
 +---------------------------------+-------+----------------------------+

Statements executing after the *note 'GET DIAGNOSTICS': get-diagnostics. statements may reset the current diagnostics area. statements may reset the current diagnostics area. For example, the handler maps the 'NULL' insert to an empty-string insert and displays the result. The new insert succeeds and clears the current diagnostics area, but the stacked diagnostics area remains unchanged and still contains information about the condition that activated the handler:

 +----------------------------------------------+
 | op                                           |
 +----------------------------------------------+
 | mapped insert succeeded, current DA is empty |
 +----------------------------------------------+

 +--------------------------------+-------+----------------------------+
 | op                             | errno | msg                        |
 +--------------------------------+-------+----------------------------+
 | stacked DA after mapped insert |  1048 | Column 'c1' cannot be null |
 +--------------------------------+-------+----------------------------+

When the condition handler ends, its current diagnostics area is popped from the stack and the stacked diagnostics area becomes the current diagnostics area in the stored procedure.

After the procedure returns, the table contains two rows. The empty row results from the attempt to insert 'NULL' that was mapped to an empty-string insert:

 +----------+
 | c1       |
 +----------+
 | string 1 |
 |          |
 +----------+

In the preceding example, the first two note 'GET DIAGNOSTICS': get-diagnostics. statements within the condition handler that retrieve information from the current and stacked diagnostics areas return the same values. This is not the case if statements that reset the current diagnostics area executed earlier within the handler. Suppose that 'p()' is rewritten to place the note 'DECLARE': declare. statements within the handler definition rather than preceding it:

 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     -- Declare variables to hold diagnostics area information
     DECLARE errcount INT;
     DECLARE errno INT;
     DECLARE msg TEXT;
     GET CURRENT DIAGNOSTICS CONDITION 1
       errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
     SELECT 'current DA before mapped insert' AS op, errno, msg;
     GET STACKED DIAGNOSTICS CONDITION 1
       errno = MYSQL_ERRNO, msg = MESSAGE_TEXT;
     SELECT 'stacked DA before mapped insert' AS op, errno, msg;
 ...

In this case, the result is version dependent:

To avoid this issue within a condition handler when seeking to obtain information about the condition that activated the handler, be sure to access the stacked diagnostics area, not the current diagnostics area.

 File: manual.info.tmp, Node: resignal, Next: signal, Prev: get-diagnostics, Up: condition-handling

13.6.7.4 RESIGNAL Statement ...........................

 RESIGNAL [CONDITION_VALUE]
     [SET SIGNAL_INFORMATION_ITEM
     [, SIGNAL_INFORMATION_ITEM] ...]

 CONDITION_VALUE: {
     SQLSTATE [VALUE] SQLSTATE_VALUE
   | CONDITION_NAME
 }

 SIGNAL_INFORMATION_ITEM:
     CONDITION_INFORMATION_ITEM_NAME = SIMPLE_VALUE_SPECIFICATION

 CONDITION_INFORMATION_ITEM_NAME: {
     CLASS_ORIGIN
   | SUBCLASS_ORIGIN
   | MESSAGE_TEXT
   | MYSQL_ERRNO
   | CONSTRAINT_CATALOG
   | CONSTRAINT_SCHEMA
   | CONSTRAINT_NAME
   | CATALOG_NAME
   | SCHEMA_NAME
   | TABLE_NAME
   | COLUMN_NAME
   | CURSOR_NAME
 }

 CONDITION_NAME, SIMPLE_VALUE_SPECIFICATION:
     (see following discussion)

note 'RESIGNAL': resignal. passes on the error condition information that is available during execution of a condition handler within a compound statement inside a stored procedure or function, trigger, or event. note 'RESIGNAL': resignal. may change some or all information before passing it on. note 'RESIGNAL': resignal. is related to note 'SIGNAL': signal, but instead of originating a condition as note 'SIGNAL': signal. does, note 'RESIGNAL': resignal. relays existing condition information, possibly after modifying it.

note 'RESIGNAL': resignal. makes it possible to both handle an error and return the error information. Otherwise, by executing an SQL statement within the handler, information that caused the handler's activation is destroyed. note 'RESIGNAL': resignal. also can make some procedures shorter if a given handler can handle part of a situation, then pass the condition 'up the line' to another handler.

No privileges are required to execute the *note 'RESIGNAL': resignal. statement.

All forms of note 'RESIGNAL': resignal. require that the current context be a condition handler. Otherwise, note 'RESIGNAL': resignal. is illegal and a 'RESIGNAL when handler not active' error occurs.

To retrieve information from the diagnostics area, use the note 'GET DIAGNOSTICS': get-diagnostics. statement (see note get-diagnostics::). For information about the diagnostics area, see *note diagnostics-area::.

RESIGNAL Overview

For CONDITION_VALUE and SIGNAL_INFORMATION_ITEM, the definitions and rules are the same for note 'RESIGNAL': resignal. as for note 'SIGNAL': signal. For example, the CONDITION_VALUE can be an 'SQLSTATE' value, and the value can indicate errors, warnings, or 'not found.' For additional information, see *note signal::.

The *note 'RESIGNAL': resignal. statement takes CONDITION_VALUE and 'SET' clauses, both of which are optional. This leads to several possible uses:

These use cases all cause changes to the diagnostics and condition areas:

There is a stack of diagnostics areas. When a handler takes control, it pushes a diagnostics area to the top of the stack, so there are two diagnostics areas during handler execution:

The maximum number of condition areas in a diagnostics area is determined by the value of the 'max_error_count' system variable. See *note diagnostics-area-system-variables::.

RESIGNAL Alone

A simple *note 'RESIGNAL': resignal. alone means 'pass on the error with no change.' It restores the last diagnostics area and makes it the current diagnostics area. That is, it 'pops' the diagnostics area stack.

Within a condition handler that catches a condition, one use for *note 'RESIGNAL': resignal. alone is to perform some other actions, and then pass on without change the original condition information (the information that existed before entry into the handler).

Example:

 DROP TABLE IF EXISTS xx;
 delimiter //
 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     SET @error_count = @error_count + 1;
     IF @a = 0 THEN RESIGNAL; END IF;
   END;
   DROP TABLE xx;
 END//
 delimiter ;
 SET @error_count = 0;
 SET @a = 0;
 CALL p();

Suppose that the 'DROP TABLE xx' statement fails. The diagnostics area stack looks like this:

 DA 1. ERROR 1051 (42S02): Unknown table 'xx'

Then execution enters the 'EXIT' handler. It starts by pushing a diagnostics area to the top of the stack, which now looks like this:

 DA 1. ERROR 1051 (42S02): Unknown table 'xx'
 DA 2. ERROR 1051 (42S02): Unknown table 'xx'

At this point, the contents of the first (current) and second (stacked) diagnostics areas are the same. The first diagnostics area may be modified by statements executing subsequently within the handler.

Usually a procedure statement clears the first diagnostics area. 'BEGIN' is an exception, it does not clear, it does nothing. 'SET' is not an exception, it clears, performs the operation, and produces a result of 'success.' The diagnostics area stack now looks like this:

 DA 1. ERROR 0000 (00000): Successful operation
 DA 2. ERROR 1051 (42S02): Unknown table 'xx'

At this point, if '@a = 0', *note 'RESIGNAL': resignal. pops the diagnostics area stack, which now looks like this:

 DA 1. ERROR 1051 (42S02): Unknown table 'xx'

And that is what the caller sees.

If '@a' is not 0, the handler simply ends, which means that there is no more use for the current diagnostics area (it has been 'handled'), so it can be thrown away, causing the stacked diagnostics area to become the current diagnostics area again. The diagnostics area stack looks like this:

 DA 1. ERROR 0000 (00000): Successful operation

The details make it look complex, but the end result is quite useful: Handlers can execute without destroying information about the condition that caused activation of the handler.

RESIGNAL with New Signal Information

*note 'RESIGNAL': resignal. with a 'SET' clause provides new signal information, so the statement means 'pass on the error with changes':

 RESIGNAL SET SIGNAL_INFORMATION_ITEM [, SIGNAL_INFORMATION_ITEM] ...;

As with note 'RESIGNAL': resignal. alone, the idea is to pop the diagnostics area stack so that the original information goes out. Unlike note 'RESIGNAL': resignal. alone, anything specified in the 'SET' clause changes.

Example:

 DROP TABLE IF EXISTS xx;
 delimiter //
 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     SET @error_count = @error_count + 1;
     IF @a = 0 THEN RESIGNAL SET MYSQL_ERRNO = 5; END IF;
   END;
   DROP TABLE xx;
 END//
 delimiter ;
 SET @error_count = 0;
 SET @a = 0;
 CALL p();

Remember from the previous discussion that *note 'RESIGNAL': resignal. alone results in a diagnostics area stack like this:

 DA 1. ERROR 1051 (42S02): Unknown table 'xx'

The 'RESIGNAL SET MYSQL_ERRNO = 5' statement results in this stack instead, which is what the caller sees:

 DA 1. ERROR 5 (42S02): Unknown table 'xx'

In other words, it changes the error number, and nothing else.

The *note 'RESIGNAL': resignal. statement can change any or all of the signal information items, making the first condition area of the diagnostics area look quite different.

RESIGNAL with a Condition Value and Optional New Signal Information

*note 'RESIGNAL': resignal. with a condition value means 'push a condition into the current diagnostics area.' If the 'SET' clause is present, it also changes the error information.

 RESIGNAL CONDITION_VALUE
     [SET SIGNAL_INFORMATION_ITEM [, SIGNAL_INFORMATION_ITEM] ...];

This form of note 'RESIGNAL': resignal. restores the last diagnostics area and makes it the current diagnostics area. That is, it 'pops' the diagnostics area stack, which is the same as what a simple note 'RESIGNAL': resignal. alone would do. However, it also changes the diagnostics area depending on the condition value or signal information.

Example:

 DROP TABLE IF EXISTS xx;
 delimiter //
 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     SET @error_count = @error_count + 1;
     IF @a = 0 THEN RESIGNAL SQLSTATE '45000' SET MYSQL_ERRNO=5; END IF;
   END;
   DROP TABLE xx;
 END//
 delimiter ;
 SET @error_count = 0;
 SET @a = 0;
 SET @@max_error_count = 2;
 CALL p();
 SHOW ERRORS;

This is similar to the previous example, and the effects are the same, except that if *note 'RESIGNAL': resignal. happens, the current condition area looks different at the end. (The reason the condition adds to rather than replaces the existing condition is the use of a condition value.)

The *note 'RESIGNAL': resignal. statement includes a condition value ('SQLSTATE '45000''), so it adds a new condition area, resulting in a diagnostics area stack that looks like this:

 DA 1. (condition 2) ERROR 1051 (42S02): Unknown table 'xx'
       (condition 1) ERROR 5 (45000) Unknown table 'xx'

The result of note 'CALL p()': call. and note 'SHOW ERRORS': show-errors. for this example is:

 mysql> CALL p();
 ERROR 5 (45000): Unknown table 'xx'
 mysql> SHOW ERRORS;
 +-------+------+----------------------------------+
 | Level | Code | Message                          |
 +-------+------+----------------------------------+
 | Error | 1051 | Unknown table 'xx'               |
 | Error |    5 | Unknown table 'xx'               |
 +-------+------+----------------------------------+

RESIGNAL Requires Condition Handler Context

All forms of note 'RESIGNAL': resignal. require that the current context be a condition handler. Otherwise, note 'RESIGNAL': resignal. is illegal and a 'RESIGNAL when handler not active' error occurs. For example:

 mysql> CREATE PROCEDURE p () RESIGNAL;
 Query OK, 0 rows affected (0.00 sec)

 mysql> CALL p();
 ERROR 1645 (0K000): RESIGNAL when handler not active

Here is a more difficult example:

 delimiter //
 CREATE FUNCTION f () RETURNS INT
 BEGIN
   RESIGNAL;
   RETURN 5;
 END//
 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION SET @a=f();
   SIGNAL SQLSTATE '55555';
 END//
 delimiter ;
 CALL p();

*note 'RESIGNAL': resignal. occurs within the stored function 'f()'. Although 'f()' itself is invoked within the context of the 'EXIT' handler, execution within 'f()' has its own context, which is not handler context. Thus, 'RESIGNAL' within 'f()' results in a 'handler not active' error.

 File: manual.info.tmp, Node: signal, Next: handler-scope, Prev: resignal, Up: condition-handling

13.6.7.5 SIGNAL Statement .........................

 SIGNAL CONDITION_VALUE
     [SET SIGNAL_INFORMATION_ITEM
     [, SIGNAL_INFORMATION_ITEM] ...]

 CONDITION_VALUE: {
     SQLSTATE [VALUE] SQLSTATE_VALUE
   | CONDITION_NAME
 }

 SIGNAL_INFORMATION_ITEM:
     CONDITION_INFORMATION_ITEM_NAME = SIMPLE_VALUE_SPECIFICATION

 CONDITION_INFORMATION_ITEM_NAME: {
     CLASS_ORIGIN
   | SUBCLASS_ORIGIN
   | MESSAGE_TEXT
   | MYSQL_ERRNO
   | CONSTRAINT_CATALOG
   | CONSTRAINT_SCHEMA
   | CONSTRAINT_NAME
   | CATALOG_NAME
   | SCHEMA_NAME
   | TABLE_NAME
   | COLUMN_NAME
   | CURSOR_NAME
 }

 CONDITION_NAME, SIMPLE_VALUE_SPECIFICATION:
     (see following discussion)

note 'SIGNAL': signal. is the way to 'return' an error. note 'SIGNAL': signal. provides error information to a handler, to an outer portion of the application, or to the client. Also, it provides control over the error's characteristics (error number, 'SQLSTATE' value, message). Without *note 'SIGNAL': signal, it is necessary to resort to workarounds such as deliberately referring to a nonexistent table to cause a routine to return an error.

No privileges are required to execute the *note 'SIGNAL': signal. statement.

To retrieve information from the diagnostics area, use the note 'GET DIAGNOSTICS': get-diagnostics. statement (see note get-diagnostics::). For information about the diagnostics area, see *note diagnostics-area::.

SIGNAL Overview

The CONDITION_VALUE in a note 'SIGNAL': signal. statement indicates the error value to be returned. It can be an 'SQLSTATE' value (a 5-character string literal) or a CONDITION_NAME that refers to a named condition previously defined with note 'DECLARE ... CONDITION': declare-condition. (see *note declare-condition::).

An 'SQLSTATE' value can indicate errors, warnings, or 'not found.' The first two characters of the value indicate its error class, as discussed in note signal-condition-information-items::. Some signal values cause statement termination; see note signal-effects::.

The 'SQLSTATE' value for a note 'SIGNAL': signal. statement should not start with ''00'' because such values indicate success and are not valid for signaling an error. This is true whether the 'SQLSTATE' value is specified directly in the note 'SIGNAL': signal. statement or in a named condition referred to in the statement. If the value is invalid, a 'Bad SQLSTATE' error occurs.

To signal a generic 'SQLSTATE' value, use ''45000'', which means 'unhandled user-defined exception.'

The *note 'SIGNAL': signal. statement optionally includes a 'SET' clause that contains multiple signal items, in a list of CONDITION_INFORMATION_ITEM_NAME = SIMPLE_VALUE_SPECIFICATION assignments, separated by commas.

Each CONDITION_INFORMATION_ITEM_NAME may be specified only once in the 'SET' clause. Otherwise, a 'Duplicate condition information item' error occurs.

Valid SIMPLE_VALUE_SPECIFICATION designators can be specified using stored procedure or function parameters, stored program local variables declared with *note 'DECLARE': declare, user-defined variables, system variables, or literals. A character literal may include a _CHARSET introducer.

For information about permissible CONDITION_INFORMATION_ITEM_NAME values, see *note signal-condition-information-items::.

The following procedure signals an error or warning depending on the value of 'pval', its input parameter:

 CREATE PROCEDURE p (pval INT)
 BEGIN
   DECLARE specialty CONDITION FOR SQLSTATE '45000';
   IF pval = 0 THEN
     SIGNAL SQLSTATE '01000';
   ELSEIF pval = 1 THEN
     SIGNAL SQLSTATE '45000'
       SET MESSAGE_TEXT = 'An error occurred';
   ELSEIF pval = 2 THEN
     SIGNAL specialty
       SET MESSAGE_TEXT = 'An error occurred';
   ELSE
     SIGNAL SQLSTATE '01000'
       SET MESSAGE_TEXT = 'A warning occurred', MYSQL_ERRNO = 1000;
     SIGNAL SQLSTATE '45000'
       SET MESSAGE_TEXT = 'An error occurred', MYSQL_ERRNO = 1001;
   END IF;
 END;

If 'pval' is 0, 'p()' signals a warning because 'SQLSTATE' values that begin with ''01'' are signals in the warning class. The warning does not terminate the procedure, and can be seen with *note 'SHOW WARNINGS': show-warnings. after the procedure returns.

If 'pval' is 1, 'p()' signals an error and sets the 'MESSAGE_TEXT' condition information item. The error terminates the procedure, and the text is returned with the error information.

If 'pval' is 2, the same error is signaled, although the 'SQLSTATE' value is specified using a named condition in this case.

If 'pval' is anything else, 'p()' first signals a warning and sets the message text and error number condition information items. This warning does not terminate the procedure, so execution continues and 'p()' then signals an error. The error does terminate the procedure. The message text and error number set by the warning are replaced by the values set by the error, which are returned with the error information.

note 'SIGNAL': signal. is typically used within stored programs, but it is a MySQL extension that it is permitted outside handler context. For example, if you invoke the note 'mysql': mysql. client program, you can enter any of these statements at the prompt:

 SIGNAL SQLSTATE '77777';

 CREATE TRIGGER t_bi BEFORE INSERT ON t
   FOR EACH ROW SIGNAL SQLSTATE '77777';

 CREATE EVENT e ON SCHEDULE EVERY 1 SECOND
   DO SIGNAL SQLSTATE '77777';

*note 'SIGNAL': signal. executes according to the following rules:

If the *note 'SIGNAL': signal. statement indicates a particular 'SQLSTATE' value, that value is used to signal the condition specified. Example:

 CREATE PROCEDURE p (divisor INT)
 BEGIN
   IF divisor = 0 THEN
     SIGNAL SQLSTATE '22012';
   END IF;
 END;

If the note 'SIGNAL': signal. statement uses a named condition, the condition must be declared in some scope that applies to the note 'SIGNAL': signal. statement, and must be defined using an 'SQLSTATE' value, not a MySQL error number. Example:

 CREATE PROCEDURE p (divisor INT)
 BEGIN
   DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012';
   IF divisor = 0 THEN
     SIGNAL divide_by_zero;
   END IF;
 END;

If the named condition does not exist in the scope of the *note 'SIGNAL': signal. statement, an 'Undefined CONDITION' error occurs.

If *note 'SIGNAL': signal. refers to a named condition that is defined with a MySQL error number rather than an 'SQLSTATE' value, a 'SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE' error occurs. The following statements cause that error because the named condition is associated with a MySQL error number:

 DECLARE no_such_table CONDITION FOR 1051;
 SIGNAL no_such_table;

If a condition with a given name is declared multiple times in different scopes, the declaration with the most local scope applies. Consider the following procedure:

 CREATE PROCEDURE p (divisor INT)
 BEGIN
   DECLARE my_error CONDITION FOR SQLSTATE '45000';
   IF divisor = 0 THEN
     BEGIN
       DECLARE my_error CONDITION FOR SQLSTATE '22012';
       SIGNAL my_error;
     END;
   END IF;
   SIGNAL my_error;
 END;

If 'divisor' is 0, the first *note 'SIGNAL': signal. statement executes. The innermost 'my_error' condition declaration applies, raising 'SQLSTATE' ''22012''.

If 'divisor' is not 0, the second *note 'SIGNAL': signal. statement executes. The outermost 'my_error' condition declaration applies, raising 'SQLSTATE' ''45000''.

For information about how the server chooses handlers when a condition occurs, see *note handler-scope::.

Signals can be raised within exception handlers:

 CREATE PROCEDURE p ()
 BEGIN
   DECLARE EXIT HANDLER FOR SQLEXCEPTION
   BEGIN
     SIGNAL SQLSTATE VALUE '99999'
       SET MESSAGE_TEXT = 'An error occurred';
   END;
   DROP TABLE no_such_table;
 END;

'CALL p()' reaches the *note 'DROP TABLE': drop-table. statement. There is no table named 'no_such_table', so the error handler is activated. The error handler destroys the original error ('no such table') and makes a new error with 'SQLSTATE' ''99999'' and message 'An error occurred'.

Signal Condition Information Items

The following table lists the names of diagnostics area condition information items that can be set in a note 'SIGNAL': signal. (or note 'RESIGNAL': resignal.) statement. All items are standard SQL except 'MYSQL_ERRNO', which is a MySQL extension. For more information about these items see *note diagnostics-area::.

 Item Name             Definition
 ---------             ----------
 CLASS_ORIGIN          VARCHAR(64)
 SUBCLASS_ORIGIN       VARCHAR(64)
 CONSTRAINT_CATALOG    VARCHAR(64)
 CONSTRAINT_SCHEMA     VARCHAR(64)
 CONSTRAINT_NAME       VARCHAR(64)
 CATALOG_NAME          VARCHAR(64)
 SCHEMA_NAME           VARCHAR(64)
 TABLE_NAME            VARCHAR(64)
 COLUMN_NAME           VARCHAR(64)
 CURSOR_NAME           VARCHAR(64)
 MESSAGE_TEXT          VARCHAR(128)
 MYSQL_ERRNO           SMALLINT UNSIGNED

The character set for character items is UTF-8.

It is illegal to assign 'NULL' to a condition information item in a *note 'SIGNAL': signal. statement.

A *note 'SIGNAL': signal. statement always specifies an 'SQLSTATE' value, either directly, or indirectly by referring to a named condition defined with an 'SQLSTATE' value. The first two characters of an 'SQLSTATE' value are its class, and the class determines the default value for the condition information items:

For legal classes, the other condition information items are set as follows:

 CLASS_ORIGIN = SUBCLASS_ORIGIN = '';
 CONSTRAINT_CATALOG = CONSTRAINT_SCHEMA = CONSTRAINT_NAME = '';
 CATALOG_NAME = SCHEMA_NAME = TABLE_NAME = COLUMN_NAME = '';
 CURSOR_NAME = '';

The error values that are accessible after note 'SIGNAL': signal. executes are the 'SQLSTATE' value raised by the note 'SIGNAL': signal. statement and the 'MESSAGE_TEXT' and 'MYSQL_ERRNO' items. These values are available from the C API:

At the SQL level, the output from note 'SHOW WARNINGS': show-warnings. and note 'SHOW ERRORS': show-errors. indicates the 'MYSQL_ERRNO' and 'MESSAGE_TEXT' values in the 'Code' and 'Message' columns.

To retrieve information from the diagnostics area, use the note 'GET DIAGNOSTICS': get-diagnostics. statement (see note get-diagnostics::). For information about the diagnostics area, see *note diagnostics-area::.

Effect of Signals on Handlers, Cursors, and Statements

Signals have different effects on statement execution depending on the signal class. The class determines how severe an error is. MySQL ignores the value of the 'sql_mode' system variable; in particular, strict SQL mode does not matter. MySQL also ignores 'IGNORE': The intent of *note 'SIGNAL': signal. is to raise a user-generated error explicitly, so a signal is never ignored.

In the following descriptions, 'unhandled' means that no handler for the signaled 'SQLSTATE' value has been defined with *note 'DECLARE ... HANDLER': declare-handler.

 File: manual.info.tmp, Node: handler-scope, Next: diagnostics-area, Prev: signal, Up: condition-handling

13.6.7.6 Scope Rules for Handlers .................................

A stored program may include handlers to be invoked when certain conditions occur within the program. The applicability of each handler depends on its location within the program definition and on the condition or conditions that it handles:

Multiple handlers can be declared in different scopes and with different specificities. For example, there might be a specific MySQL error code handler in an outer block, and a general 'SQLWARNING' handler in an inner block. Or there might be handlers for a specific MySQL error code and the general 'SQLWARNING' class in the same block.

Whether a handler is activated depends not only on its own scope and condition value, but on what other handlers are present. When a condition occurs in a stored program, the server searches for applicable handlers in the current scope (current *note 'BEGIN ... END': begin-end. block). If there are no applicable handlers, the search continues outward with the handlers in each successive containing scope (block). When the server finds one or more applicable handlers at a given scope, it chooses among them based on condition precedence:

One implication of the handler selection rules is that if multiple applicable handlers occur in different scopes, handlers with the most local scope take precedence over handlers in outer scopes, even over those for more specific conditions.

If there is no appropriate handler when a condition occurs, the action taken depends on the class of the condition:

The following examples demonstrate how MySQL applies the handler selection rules.

This procedure contains two handlers, one for the specific 'SQLSTATE' value (''42S02'') that occurs for attempts to drop a nonexistent table, and one for the general 'SQLEXCEPTION' class:

 CREATE PROCEDURE p1()
 BEGIN
   DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
     SELECT 'SQLSTATE handler was activated' AS msg;
   DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
     SELECT 'SQLEXCEPTION handler was activated' AS msg;

   DROP TABLE test.t;
 END;

Both handlers are declared in the same block and have the same scope. However, 'SQLSTATE' handlers take precedence over 'SQLEXCEPTION' handlers, so if the table 't' is nonexistent, the *note 'DROP TABLE': drop-table. statement raises a condition that activates the 'SQLSTATE' handler:

 mysql> CALL p1();
 +--------------------------------+
 | msg                            |
 +--------------------------------+
 | SQLSTATE handler was activated |
 +--------------------------------+

This procedure contains the same two handlers. But this time, the *note 'DROP TABLE': drop-table. statement and 'SQLEXCEPTION' handler are in an inner block relative to the 'SQLSTATE' handler:

 CREATE PROCEDURE p2()
 BEGIN -- outer block
     DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
       SELECT 'SQLSTATE handler was activated' AS msg;
   BEGIN -- inner block
     DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
       SELECT 'SQLEXCEPTION handler was activated' AS msg;

     DROP TABLE test.t; -- occurs within inner block
   END;
 END;

In this case, the handler that is more local to where the condition occurs takes precedence. The 'SQLEXCEPTION' handler activates, even though it is more general than the 'SQLSTATE' handler:

 mysql> CALL p2();
 +------------------------------------+
 | msg                                |
 +------------------------------------+
 | SQLEXCEPTION handler was activated |
 +------------------------------------+

In this procedure, one of the handlers is declared in a block inner to the scope of the *note 'DROP TABLE': drop-table. statement:

 CREATE PROCEDURE p3()
 BEGIN -- outer block
   DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
     SELECT 'SQLEXCEPTION handler was activated' AS msg;
   BEGIN -- inner block
     DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
       SELECT 'SQLSTATE handler was activated' AS msg;
   END;

   DROP TABLE test.t; -- occurs within outer block
 END;

Only the 'SQLEXCEPTION' handler applies because the other one is not in scope for the condition raised by the *note 'DROP TABLE': drop-table.:

 mysql> CALL p3();
 +------------------------------------+
 | msg                                |
 +------------------------------------+
 | SQLEXCEPTION handler was activated |
 +------------------------------------+

In this procedure, both handlers are declared in a block inner to the scope of the *note 'DROP TABLE': drop-table. statement:

 CREATE PROCEDURE p4()
 BEGIN -- outer block
   BEGIN -- inner block
     DECLARE CONTINUE HANDLER FOR SQLEXCEPTION
       SELECT 'SQLEXCEPTION handler was activated' AS msg;
     DECLARE CONTINUE HANDLER FOR SQLSTATE '42S02'
       SELECT 'SQLSTATE handler was activated' AS msg;
   END;

   DROP TABLE test.t; -- occurs within outer block
 END;

Neither handler applies because they are not in scope for the *note 'DROP TABLE': drop-table. The condition raised by the statement goes unhandled and terminates the procedure with an error:

 mysql> CALL p4();
 ERROR 1051 (42S02): Unknown table 'test.t'

 File: manual.info.tmp, Node: diagnostics-area, Next: conditions-and-parameters, Prev: handler-scope, Up: condition-handling

13.6.7.7 The MySQL Diagnostics Area ...................................

SQL statements produce diagnostic information that populates the diagnostics area. Standard SQL has a diagnostics area stack, containing a diagnostics area for each nested execution context. Standard SQL also supports *note 'GET STACKED DIAGNOSTICS': get-diagnostics. syntax for referring to the second diagnostics area during condition handler execution. MySQL supports the 'STACKED' keyword as of MySQL 5.7. Before that, MySQL does not support 'STACKED'; there is a single diagnostics area containing information from the most recent statement that wrote to it.

The following discussion describes the structure of the diagnostics area in MySQL, the information items recognized by MySQL, how statements clear and set the diagnostics area, and how diagnostics areas are pushed to and popped from the stack.

Diagnostics Area Structure

The diagnostics area contains two kinds of information:

For a statement that produces three conditions, the diagnostics area contains statement and condition information like this:

 Statement information:
   row count
   ... other statement information items ...
 Condition area list:
   Condition area 1:
     error code for condition 1
     error message for condition 1
     ... other condition information items ...
   Condition area 2:
     error code for condition 2:
     error message for condition 2
     ... other condition information items ...
   Condition area 3:
     error code for condition 3
     error message for condition 3
     ... other condition information items ...

Diagnostics Area Information Items

The diagnostics area contains statement and condition information items. Numeric items are integers. The character set for character items is UTF-8. No item can be 'NULL'. If a statement or condition item is not set by a statement that populates the diagnostics area, its value is 0 or the empty string, depending on the item data type.

The statement information part of the diagnostics area contains these items:

The condition information part of the diagnostics area contains a condition area for each condition. Condition areas are numbered from 1 to the value of the 'NUMBER' statement condition item. If 'NUMBER' is 0, there are no condition areas.

Each condition area contains the items in the following list. All items are standard SQL except 'MYSQL_ERRNO', which is a MySQL extension. The definitions apply for conditions generated other than by a signal (that is, by a note 'SIGNAL': signal. or note 'RESIGNAL': resignal. statement). For nonsignal conditions, MySQL populates only those condition items not described as always empty. The effects of signals on the condition area are described later.

For the 'RETURNED_SQLSTATE', 'MESSAGE_TEXT', and 'MYSQL_ERRNO' values for particular errors, see Server Error Message Reference (https://dev.mysql.com/doc/mysql-errors/5.7/en/server-error-reference.html).

If a note 'SIGNAL': signal. (or note 'RESIGNAL': resignal.) statement populates the diagnostics area, its 'SET' clause can assign to any condition information item except 'RETURNED_SQLSTATE' any value that is legal for the item data type. note 'SIGNAL': signal. also sets the 'RETURNED_SQLSTATE' value, but not directly in its 'SET' clause. That value comes from the note 'SIGNAL': signal. statement 'SQLSTATE' argument.

*note 'SIGNAL': signal. also sets statement information items. It sets 'NUMBER' to 1. It sets 'ROW_COUNT' to −1 for errors and 0 otherwise.

How the Diagnostics Area is Cleared and Populated

Nondiagnostic SQL statements populate the diagnostics area automatically, and its contents can be set explicitly with the note 'SIGNAL': signal. and note 'RESIGNAL': resignal. statements. The diagnostics area can be examined with note 'GET DIAGNOSTICS': get-diagnostics. to extract specific items, or with note 'SHOW WARNINGS': show-warnings. or *note 'SHOW ERRORS': show-errors. to see conditions or errors.

SQL statements clear and set the diagnostics area as follows:

Thus, even a statement that does not normally clear the diagnostics area when it begins executing clears it if the statement raises a condition.

The following example shows the effect of various statements on the diagnostics area, using *note 'SHOW WARNINGS': show-warnings. to display information about conditions stored there.

This *note 'DROP TABLE': drop-table. statement clears the diagnostics area and populates it when the condition occurs:

 mysql> DROP TABLE IF EXISTS test.no_such_table;
 Query OK, 0 rows affected, 1 warning (0.01 sec)

 mysql> SHOW WARNINGS;
 +-------+------+------------------------------------+
 | Level | Code | Message                            |
 +-------+------+------------------------------------+
 | Note  | 1051 | Unknown table 'test.no_such_table' |
 +-------+------+------------------------------------+
 1 row in set (0.00 sec)

This *note 'SET': set-variable. statement generates an error, so it clears and populates the diagnostics area:

 mysql> SET @x = @@x;
 ERROR 1193 (HY000): Unknown system variable 'x'

 mysql> SHOW WARNINGS;
 +-------+------+-----------------------------+
 | Level | Code | Message                     |
 +-------+------+-----------------------------+
 | Error | 1193 | Unknown system variable 'x' |
 +-------+------+-----------------------------+
 1 row in set (0.00 sec)

The previous note 'SET': set-variable. statement produced a single condition, so 1 is the only valid condition number for note 'GET DIAGNOSTICS': get-diagnostics. at this point. The following statement uses a condition number of 2, which produces a warning that is added to the diagnostics area without clearing it:

 mysql> GET DIAGNOSTICS CONDITION 2 @p = MESSAGE_TEXT;
 Query OK, 0 rows affected, 1 warning (0.00 sec)

 mysql> SHOW WARNINGS;
 +-------+------+------------------------------+
 | Level | Code | Message                      |
 +-------+------+------------------------------+
 | Error | 1193 | Unknown system variable 'xx' |
 | Error | 1753 | Invalid condition number     |
 +-------+------+------------------------------+
 2 rows in set (0.00 sec)

Now there are two conditions in the diagnostics area, so the same *note 'GET DIAGNOSTICS': get-diagnostics. statement succeeds:

 mysql> GET DIAGNOSTICS CONDITION 2 @p = MESSAGE_TEXT;
 Query OK, 0 rows affected (0.00 sec)

 mysql> SELECT @p;
 +--------------------------+
 | @p                       |
 +--------------------------+
 | Invalid condition number |
 +--------------------------+
 1 row in set (0.01 sec)

How the Diagnostics Area Stack Works

When a push to the diagnostics area stack occurs, the first (current) diagnostics area becomes the second (stacked) diagnostics area and a new current diagnostics area is created as a copy of it. Diagnostics areas are pushed to and popped from the stack under the following circumstances:

Diagnostics Area-Related System Variables

Certain system variables control or are related to some aspects of the diagnostics area:

Example: If 'max_error_count' is 10, the diagnostics area can contain a maximum of 10 condition areas. Suppose that a statement raises 20 conditions, 12 of which are errors. In that case, the diagnostics area contains the first 10 conditions, 'NUMBER' is 10, 'warning_count' is 20, and 'error_count' is 12.

Changes to the value of 'max_error_count' have no effect until the next attempt to modify the diagnostics area. If the diagnostics area contains 10 condition areas and 'max_error_count' is set to 5, that has no immediate effect on the size or content of the diagnostics area.

 File: manual.info.tmp, Node: conditions-and-parameters, Next: condition-handling-restrictions, Prev: diagnostics-area, Up: condition-handling

13.6.7.8 Condition Handling and OUT or INOUT Parameters .......................................................

If a stored procedure exits with an unhandled exception, modified values of 'OUT' and 'INOUT' parameters are not propogated back to the caller.

If an exception is handled by a 'CONTINUE' or 'EXIT' handler that contains a note 'RESIGNAL': resignal. statement, execution of note 'RESIGNAL': resignal. pops the Diagnostics Area stack, thus signalling the exception (that is, the information that existed before entry into the handler). If the exception is an error, the values of 'OUT' and 'INOUT' parameters are not propogated back to the caller.

 File: manual.info.tmp, Node: condition-handling-restrictions, Prev: conditions-and-parameters, Up: condition-handling

13.6.7.9 Restrictions on Condition Handling ...........................................

note 'SIGNAL': signal, note 'RESIGNAL': resignal, and *note 'GET DIAGNOSTICS': get-diagnostics. are not permissible as prepared statements. For example, this statement is invalid:

 PREPARE stmt1 FROM 'SIGNAL SQLSTATE "02000"';

'SQLSTATE' values in class ''04'' are not treated specially. They are handled the same as other exceptions.

In standard SQL, the first condition relates to the 'SQLSTATE' value returned for the previous SQL statement. In MySQL, this is not guaranteed, so to get the main error, you cannot do this:

 GET DIAGNOSTICS CONDITION 1 @errno = MYSQL_ERRNO;

Instead, do this:

 GET DIAGNOSTICS @cno = NUMBER;
 GET DIAGNOSTICS CONDITION @cno @errno = MYSQL_ERRNO;

 File: manual.info.tmp, Node: sql-server-administration-statements, Next: sql-utility-statements, Prev: sql-compound-statements, Up: sql-statements