WL#2110: Stored Procedures: Implement SIGNAL
Affects: Server-5.5
—
Status: Complete
MySQL will implement the standard SQL (SQL:2003) SIGNAL statement, which is used to signal a rule violation (i.e. an error or a warning) during routine execution. History: Discussion of SIGNAL has been going on since 2003. At the Malta conference it was said to be "due for 5.1". In the Santa Cruz Dev-Mt Offsite meeting (2006-08), Brian and Monty made some time estimates and statements about dependencies (see "Progress Reports" for details). In the Customer Advisory Board meeting (2006-12), customers said that SIGNAL even without RESIGNAL would be useful. Rationale: There are feature requests. It's in the standard. It's been part of the plan for some time, and it's on the roadmap's "must have" list now for version 6.x. There's no other good user-controlled way to report errors. Compatibility: IBM DB2 supports SIGNAL in the standard-SQL manner. Sybase SQL Anywhere supports it in a slightly less standard manner. Oracle and SQL Server don't support SIGNAL, but do provide similar functionality for diagnostic checking within stored procedures (Oracle provides the RAISEstatement; SQL Server the RAISERROR statement). We don't need yet to store all signal information in the diagnostics area in the server, since the only way to retrieve that data is with GET DIAGNOSTICS (WL#2111), which might come later. The WL dependencies are: - SIGNAL can be implemented by itself, - RESIGNAL depends on SIGNAL - GET DIAGNOSTICS depends on both SIGNAL and RESIGNAL for testing reasons.
Function -------- SIGNAL is the way that we say "return error" or "return warning" in standard SQL. Without it, users have to resort to measures like "deliberately refer to a nonexistent table" in order to cause a routine to return an error. That's inelegant, and gives too little control over the error's characteristics (errno, type, sqlstate, message). SIGNAL provides error/warning information to a handler, to an outer portion of the application, or to the client. It's part of ANSI/ISO non-core Feature P002 Computational completeness. Syntax ------::= SIGNAL { | } [ SET ] ::= SQLSTATE [VALUE] ::= [ { , }... ] ::= = ::= { CLASS_ORIGIN | SUBCLASS_ORIGIN | CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | CATALOG_NAME | SCHEMA_NAME | TABLE_NAME | COLUMN_NAME | CURSOR_NAME | MESSAGE_TEXT | MYSQL_ERRNO } The SET clause is optional. If it is included, it may contain multiple signal items, in a comma-delimited list of = pairs. The standard document says " = ". For us that will mean variables (DECLAREd variables, @variables, @@variables), parameters (i.e. input parameters of functions or procedures), and literals, and not NULLs. We discussed allowing of any sort as a MySQL extension, but that will not happen. By the way, a character literal may include an _introducer. Each may be specified only once in the SET clause. Otherwise, the implementor will decide an appropriate error. Tentatively the implementor has decided on "Duplicate condition information item: ...". SIGNAL condition_name is illegal unless the condition is defined with SQLSTATE. In other words: DECLARE x CONDITION FOR 1234; SIGNAL x; will cause an error. The implementor will decide what the error is. Tentatively the implementor has decided on "SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE". Examples: CREATE PROCEDURE p () BEGIN DECLARE specialty CONDITION FOR SQLSTATE '12345'; IF @a = 0 THEN SIGNAL SQLSTATE VALUE '12345'; END IF; IF @a = 1 THEN SIGNAL specialty; END IF; IF @a = 2 THEN SIGNAL SQLSTATE '01000' SET MESSAGE_TEXT = 'Hi!'; END IF; SIGNAL SQLSTATE '03000' SET MESSAGE_TEXT = 'Hi!', MYSQL_ERRNO = 12345; END// Extension: SIGNAL is legal outside compound statements ------------------------------------------------------ Technically, SIGNAL is non-preparable so it could only appear within a compound statement (BEGIN/END block), which would limit its use to within a stored procedure, function, trigger, or event. (Or a dynamic compound statement, see WL#3696.) However, we will allow SIGNAL outside compound statements. So these statements are legal right after a mysql-client prompt: SIGNAL SQLSTATE '77777'; CREATE TRIGGER t BEFORE INSERT ON t FOR EACH ROW SIGNAL SQLSTATE '77777'; CREATE EVENT e ON SCHEDULE EVERY 1 SECOND DO SIGNAL SQLSTATE '77777'; That does not mean PREPARE stmt1 FROM 'SIGNAL ...'; is legal. "Preparing any statement" is a separate worklog task. Rules ----- If the statement is: SIGNAL ; then the specified has to be a condition that was declared in some scope that applies to the SIGNAL statement. If the is declared multiple times, the declaration with the most local scope applies. Example: CREATE PROCEDURE p (divisor INT) BEGIN DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012'; IF divisor = 0 THEN BEGIN DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012'; SIGNAL divide_by_zero; END; END IF; END If the statement is: SIGNAL ; then the SQLSTATE specified will be used to signal the condition specified. Example: CREATE PROCEDURE p (divisor INT) BEGIN IF divisor = 0 THEN SIGNAL SQLSTATE '22012'; END IF; END The SQLSTATE value should not start with '00'. Otherwise, the implementor will decide an appropriate error. Tentatively the implementor has chosen "Bad SQLSTATE ...". When SIGNAL is executed, it has five effects, in the following order: (1) First, the diagnostics area is completely cleared. So if the SIGNAL is in a DECLARE HANDLER then any pending errors or warnings are gone. So is 'row count'. (2) Second, certain items in the diagnostics area are set to specific values, as follows: a) NUMBER is set to 1 (one). b) MORE is set to 'N'. c) COMMAND_FUNCTION is set to 'SIGNAL'. d) DYNAMIC_FUNCTION is set to a zero-length string. e) CLASS_ORIGIN, SUBCLASS_ORIGIN, CONSTRAINT_CATALOG, CONSTRAINT_SCHEMA, CONSTRAINT_NAME, CATALOG_NAME, SCHEMA_NAME, TABLE_NAME, COLUMN_NAME, CURSOR_NAME, and MESSAGE_TEXT are each set to a zero-length string. f) MESSAGE_LENGTH and MESSAGE_OCTET_LENGTH are each set to 0 (zero). /* MySQL doesn't yet use any of the above, except "message" so the implementor may ignore this. */ (3) Third, if the statement is "SIGNAL ", then the CONDITION_IDENTIFIER value for the first condition area within the diagnostics area is set to ; otherwise CONDITION_IDENTIFIER is set to a zero-length string. /* MySQL doesn't yet use CONDITION_IDENTIFIER so the implementor may ignore this. */ (4) Fourth, if the statement is "SIGNAL ", or with a that corresponds to an SQLSTATE value (e.g. with DECLARE divide_by_zero CONDITION FOR '22012'), then the RETURNED_SQLSTATE value for the first condition area within the diagnostics area is set to that SQLSTATE; otherwise RETURNED_SQLSTATE is set to a zero-length string. /* This is always the case, in our implementation. */ (5) Fifth, an implicit RESIGNAL statement is executed, causing the signaled exception condition to be raised in the diagnostics area. -- (a) If SIGNAL includes a SET clause, then the RESIGNAL statement executed is: RESIGNAL SET ; -- Otherwise, the RESIGNAL statement executed is: RESIGNAL; /* This is just "for the record". The idea of the implicit RESIGNAL is merely that: that's how handlers are activated in standard SQL. The implementor may ignore this. */ Condition information items: descriptions ----------------------------------------- All condition information items except MYSQL_ERRNO are "standard". MYSQL_ERRNO is a MySQL extension. MYSQL_ERRNO and MESSAGE_TEXT are the only items that users see in error returns. The other items are settable, but the settings have no effect on anything except GET DIAGNOSTICS. item name Why Definition --------- --- ---------- CLASS_ORIGIN standard VARCHAR(64) SUBCLASS_ORIGIN standard VARCHAR(64) CONSTRAINT_CATALOG standard VARCHAR(64) CONSTRAINT_SCHEMA standard VARCHAR(64) CONSTRAINT_NAME standard VARCHAR(64) CATALOG_NAME standard VARCHAR(64) SCHEMA_NAME standard VARCHAR(64) TABLE_NAME standard VARCHAR(64) COLUMN_NAME standard VARCHAR(64) CURSOR_NAME standard VARCHAR(64) MESSAGE_TEXT standard VARCHAR(128) MYSQL_ERRNO MySQL extension UNSIGNED SMALLINT All items are nullable but in fact you can't assign NULLs. All CHAR/VARCHAR items are UTF8 (we discussed possible use of connection character set instead, but nobody liked that). Since it's UTF8, then it may have 4-byte characters, so VARCHAR(128) can require 512 bytes. Junk non-UTF8 characters are not allowed, the implementor may either convert them to "?"s or disallow them. The item name "MYSQL_ERRNO", instead of code or errno or sqlcode or native_error or error_code or mysql_error_code or error_number or error_message_number, was chosen by a vote. The voters were Roland Bouman, Konstantin Osipov. Condition information items: default values ------------------------------------------- One always specifies SQLSTATE directly or indirectly, and the first two letters of SQLSTATE are the class: '00' success, '01' warning, '02' not found, else error. We will figure out some of the default values for condition information items based on the class. If class = '00' /* Illegal */ If class = '01' CLASS_ORIGIN = SUBCLASS_ORIGIN = ''; CONSTRAINT_CATALOG = CONSTRAINT_SCHEMA = CONSTRAINT_NAME = ''; CATALOG_NAME = SCHEMA_NAME = TABLE_NAME = COLUMN_NAME = ''; CURSOR_NAME = ''; MESSAGE_TEXT = 'Unhandled user-defined warning condition'; MYSQL_ERRNO = ER_SIGNAL_WARN If class = '02' CLASS_ORIGIN = SUBCLASS_ORIGIN = ''; CONSTRAINT_CATALOG = CONSTRAINT_SCHEMA = CONSTRAINT_NAME = ''; CATALOG_NAME = SCHEMA_NAME = TABLE_NAME = COLUMN_NAME = ''; CURSOR_NAME = ''; MESSAGE_TEXT = 'Unhandled user-defined not found condition'; MYSQL_ERRNO = ER_SIGNAL_NOT_FOUND If class > '02' If (class is a class listed in SQL standard) CLASS_ORIGIN = SUBCLASS_ORIGIN = ''; else CLASS_ORIGIN = SUBCLASS_ORIGIN = ''; CONSTRAINT_CATALOG = CONSTRAINT_SCHEMA = CONSTRAINT_NAME = ''; CATALOG_NAME = SCHEMA_NAME = TABLE_NAME = COLUMN_NAME = ''; CURSOR_NAME = ''; MESSAGE_TEXT = 'Unhandled user-defined exception condition'; MYSQL_ERRNO = ER_SIGNAL_EXCEPTION Thus MySQL is determining default MYSQL_ERRNO based on SQLSTATE class. DB2 does that too. Effect on handlers, cursors, statements, and transactions --------------------------------------------------------- The error class determines how severe the error is. We ignore sql_mode, that is, 'strict' doesn't matter. We ignore IGNORE, that is, user-generated errors are never ignored. If class = '00' /* illegal */ If class = '01' /* It's a warning. @@warning_count goes up. SHOW WARNINGS shows it. SQLWARNING handlers catch it. If it's unhandled in a function, statements don't end. */ If class = '02' /* It's a not found. NOT FOUND handlers catch it. There's no effect on cursors. If it's unhandled in a function, statements end. */ If class > '02' but class <> '40' /* It's an exception. SQLEXCEPTION handlers catch it. If it's unhandled in a function, statements end. */ If class = '40' /* Treat this as an ordinary exception. It's a severe exception, the Transaction Rollback class. In standard SQL: No handlers catch it, and statements end if it's unhandled in a function, and transactions end, automatic rollback. But MySQL won't follow the standard in this respect. And MySQL won't have special treatment for other severe exceptions ("fatal errors"), e.g. out-of-memory. */ For example: DELIMITER // CREATE FUNCTION f () RETURNS INT BEGIN SIGNAL SQLSTATE '01234'; RETURN 5; END// DELIMITER ; CREATE TABLE t (s1 INT); INSERT INTO t VALUES (f()); The result is: a 5 is inserted into table t. No error or warning appears for the user. However, as the MySQL Reference Manual says: "12.5.5.42. SHOW WARNINGS Syntax Statements that do not use tables and do not generate messages have no effect on the message list." So RETURN does not clear. Making message list more standard-conformant in this respect might be nice, but isn't a requirement for this worklog. Nifty Ideas ----------- The following ideas are nifty, but not standard and not utterly necessary. So we'll never implement them. 1. SIGNAL MYSQL_ERRNO nnnn; Instead of "SIGNAL SQLSTATE ...", say "SIGNAL MYSQL_ERRNO ...". Then you can add SQLSTATE as a condition information item, so that you can say: SIGNAL MYSQL_ERRNO 9999 SET SQLSTATE = '99999'; We won't support the above. But you can have MYSQL_ERRNO and a condition information item, so that you can say: SIGNAL SQLSTATE '99999' SET MYSQL_ERRNO = 9999; 2. Determine default MESSAGE_TEXT from MYSQL_ERRNO. If MYSQL_ERRNO is an error number with an assigned meaning in MySQL, then it has an associated mesage. So if SIGNAL specifies MYSQL_ERRNO but not MESSAGE_TEXT, then let MESSAGE_TEXT = associated message. For example: SIGNAL SQLSTATE '3D000' SET MYSQL_ERRNO = 1046; Since /include/mysqld_error.h says "#define ER_NO_DB_ERROR 1046" MESSAGE_TEXT will be 'No database selected'. Or, if language = German, 'Keine Datenbank ausgewählt'. Things get trickier if the default message has '%s' in it. We won't support the above. 3. Allow multiple conditions. For example: SIGNAL SQLSTATE '12345, SQLSTATE '01234'; We have no use for this while we only support one condition area. We won't support the above. 4. Have a condition information item for severity. Instead of depending on the SQLSTATE class, the user could state that severity = note, warning, error, not found, rollback. PostgreSQL has something like this. We won't support the above. 5. Have condition information items for any situation which could be returned in an error packet or OK packet via the protocol. For example: ROW_COUNT. We won't support the above. 6. Allow PREPARE stmt1 FROM 'SIGNAL "22222"'. We won't support the above. 7. Allow assignment of an error constant to MYSQL_ERRNO. For example SIGNAL SQLSTATE '12345' SET MYSQL_ERRNO = 1469; becomes SIGNAL SQLSTATE '12345' SET MYSQL_ERRNO = ER_HOSTNAME. We won't support the above. 8. Figure out some special behaviour for IGNORE, see WL#4103. Since WL#4103 does not specify using the SQLSTATE class, this would have to be a new condition information item: IGNORABLE_TYPE = { 0 | 1 | 2 }. We won't support the above. No SIGNAL error can be skipped due to IGNORE. 9. Allow multiple diagnostics areas. (A standard-conformant "diagnostics area stack" is not in any worklog task, but might happen someday.) Then allow SIGNAL to state the area, e.g. SIGNAL AREA 2 SQLSTATE '12345'; We won't support the above. A. Access from Storage Engine API. We know that storage engines have some difficulty changing or customizing error messages. We will provide a simple routine so that any storage engine can do the equivalent of SIGNAL SQLSTATE '...' SET MYSQL_ERRNO = ....., MESSAGE_TEXT = '...'. We won't support the above. B. Access from routine written in another language. For example, a UDF writer might want to force an error. We won't support the above. C. Allow SIGNAL to cause a note-level warning. This could happen with SIGNAL ... SET SEVERITY = 'note'; We won't support the above. Privileges ---------- No special privileges are required to execute the SIGNAL statement. Examples -------- CREATE PROCEDURE p (divisor INT) BEGIN DECLARE divide_by_zero CONDITION FOR SQLSTATE '22012'; IF divisor = 0 THEN SIGNAL divide_by_zero SET MESSAGE_TEXT = 'Problem: Division by zero happened!'; END IF; END Result: "CALL p(0)" will cause an error with SQLSTATE '22012' and message "Problem: Division by zero happened!". CREATE PROCEDURE p () BEGIN DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN SIGNAL SQLSTATE VALUE '99999' SET MESSAGE_TEXT = 'An error happened'; END; DROP TABLE no_such_table; END Result: "CALL p()" will reach the DROP TABLE statement. Since there is no table named no_such_table, the error handler comes into play. The error handler destroys the original ("no such table") error and makes a new error, with SQLSTATE value '99999' and message "An error happened". Reserved words -------------- SIGNAL Other DBMSs ----------- DB2 follows the standard. Informix has RAISE EXCEPTION n,n,'text'. InterBase has "CREATE EXCEPTION exception_name; ... EXCEPTION exception_name". Oracle has "DECLARE exception_name EXCEPTION; ... RAISE exception_name", and an EXCEPTION section at end of procedure where one can dump messages. SQL Server has RAISERROR ('text',n,n). Sybase has RAISERROR n 'text', but Sybase SQL Anywhere has SIGNAL. PostgreSQL follows Oracle, but also has tricks for "severity levels". Changes to the manual --------------------- The documentation should recommend that users choose '45000' for a generic SQLSTATE, which means "unhandled user-defined exception". The MySQL Reference Manual mentions SIGNAL statements twice: "The SIGNAL and RESIGNAL statements are not currently supported." "31.4.12: Do stored procedures have a statement for raising application errors? Not in MySQL 6.0. We intend to implement the SQL standard SIGNAL and RESIGNAL statements in a future MySQL release." Notes that may affect documentation ... Inevitably we will see bogus bug reports about some matters that are not due to WL#2110 implementation. The only recourse is to document and patiently explain. * SHOW WARNINGS does not display errors / warnings in order by condition item number. People will think that the first message is the "raised" condition. But it's not. This will be handled when/if we decide what to do about the relationship between SHOW and standard conditions. * Setting MESSAGE_TEXT to a value containing characters outside the ASCII repertoire will appear not to work. But it does. This will be handled by fixing BUG#1406. See email thread * Statements like CREATE TRIGGER t_bi BEFORE INSERT ON t FOR EACH ROW RESIGNAL; may appear bizarre because RESIGNAL apparently isn't in a handler. But it doesn't matter. The point is that RESIGNAL doesn't have to be technically "in", i.e. contained in, a handler declaration. The requirement is that a handler must be active. References ---------- WL#2111 Stored Procedures: Implement GET DIAGNOSTICS WL#2265 Stored Procedures: Implement RESIGNAL BUG#11661 Raising Exceptions from within stored procedures: Support for SIGNAL statement (Feature request) BUG#16999 RAISEERROR or similar (Feature request) Email mentioning row_count and signal: https://intranet.mysql.com/secure/mailarchive/mail.php?folder=52&mail=21762 Dev-private thread "Re: SIGNAL / RESIGNAL / GET DIAGNOSTICS", starting with https://intranet.mysql.com/secure/mailarchive/mail.php?folder=4&mail=23812 "SIGNAL and RESIGNAL statements in condition handlers" http://publib.boulder.ibm.com/infocenter/db2luw/v9/index.jsp?topic=/com.ibm.db2.udb.apdv.sql.doc/doc/c0009026.htm "Sybase SQL Anywhere User's Guide: Errors and warnings in procedures and triggers" http://manuals.sybase.com/onlinebooks/group-pbarc/conn5/sqlug/@Generic__BookTextView/14095;pt=12051/* "RAISERROR" (SQL Server 2008) http://technet.microsoft.com/en-us/library/ms178592(SQL.100).aspx "SIGNAL" (Oracle OLAP) http://www.stanford.edu/dept/itss/docs/oracle/10g/olap.101/b10339/x_reserved020.htm "Declaring Exceptions and Exception Handling Routines" http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28424/adfns_packages.htm#sthref763 "How PL/SQL Exceptions Are Raised" http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28370/errors.htm#sthref1391 "Handling Run-Time PL/SQL Errors" http://download.oracle.com/docs/cd/B28359_01/appdev.111/b28424/adfns_packages.htm#sthref758 "Errors and Messages" (PostgreSQL) http://www.postgresql.org/docs/8.3/static/plpgsql-errors-and-messages.html CSC#2266 statement to abort store procedures https://support.mysql.com/view.php?id=2266
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.