# Copyright (C) 2008-2009 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 # USA # Some notes: # - DECLARE ... CONDITION must be before any DECLARE ... HANDLER # - DECLARE ... HANDLER must be before any statements like INSERT/CALL etc. # - If a handler refers to a condition than this condition must be already defined. query_init: { $width = 100 ; return undef }; query: set_variable | update | create_procedure | create_procedure | create_function | create_function | create_procedure1 | create_procedure1 | drop_procedure | drop_function | drop_procedure1 | drop_function | call_procedure | call_procedure | call_procedure | call_procedure | SET @@max_sp_recursion_depth = _digit ; create_function: CREATE FUNCTION function_name ( arg1 variable_type, arg2 variable_type , arg3 variable_type) RETURNS variable_type BEGIN procedure ; RETURN value ; END ; drop_function: DROP FUNCTION function_name ; function_name: f1 | f2 | f3 ; create_procedure: CREATE PROCEDURE procedure_name ( arg1 variable_type , arg2 variable_type , arg3 variable_type ) BEGIN procedure ; END ; procedure_name: p1 | p2 | p3 ; procedure: declare_variable ; declare_condition ; declare_handler ; procedure_statement ; procedure_statement ; procedure_statement ; procedure_statement ; procedure_statement ; drop_procedure: DROP PROCEDURE procedure_name ; call_procedure: CALL procedure_name ( value , value , value ) | CALL procedure_name1 () ; procedure_statement: set_variable | signal_resignal | if | call_procedure | update ; create_procedure1: # Some brute force generated nesting. # Note: # 1. $begin_count # The number of "BEGIN" and "END" must be equal. Therefore we count the number of "BEGIN" without closing "END" in $begin_count. # add_left: Add one or two "BEGIN" # add_right: Add sometimes (In 50 % of all cases where $begin_count > 1) an "END" # create_end: Add all missing "END" so that we do not get a syntax error. # 2. $cond_count # The number of defined conditions. { $begin_count = 1 ; $cond_count = 0 ; return undef } create_begin BEGIN middle ; create_end | { $begin_count = 0 ; $cond_count = 0 ; return undef } create_begin add_left ; middle ; add_right ; create_end | { $begin_count = 0 ; $cond_count = 0 ; return undef } create_begin add_left ; add_left ; middle ; add_right ; add_right ; create_end | { $begin_count = 0 ; $cond_count = 0 ; return undef } create_begin add_left ; add_left ; add_left ; middle ; add_right ; add_right ; add_right ; create_end | { $begin_count = 0 ; $cond_count = 0 ; return undef } create_begin add_left ; add_left ; add_left ; add_left ; middle ; add_right ; add_right ; add_right ; add_right ; create_end ; create_begin: CREATE PROCEDURE procedure_name1 () ; procedure_name1: { $procedure_name = 'p1_'.$prng->int(1,$width) } ; add_left: { $begin_count++ ; return undef } BEGIN some_statement | { $begin_count++ ; $begin_count++ ; return undef } BEGIN some_declare_condition DECLARE handler_type HANDLER FOR handler_condition_list BEGIN handler_action ; middle: some_statement ; add_right: some_statement { if ( 0 == $prng->int(0,1) && $begin_count > 1 ) { $begin_count-- ; return "; END " } else { return undef } } ; create_end: some_statement { $val = '' ; while ($begin_count > 0) { $begin_count-- ; $val = $val.'; END' } ; return $val } ; some_statement: # No error, no warning SELECT 1 | signal | call_procedure | # Get an error (table does not exist) SELECT COUNT(*) FROM t_not_exists | # Get a warning (truncation) SELECT CAST('ABC' AS CHAR(2)) | # Row not found SELECT * FROM (SELECT 1 AS f1) A WHERE f1 = 0 ; some_declare_condition: | declare_condition1 ; | declare_condition1 ; declare_condition1 ; ; declare_condition1: { $cond_count++ ; return undef } DECLARE condition_name_create CONDITION FOR condition_value ; condition_name_create: # 10 % of all cases the first condition name we ever create condition. # This can end up in # - there is nowhere a condition with this name within the already defined part of the procedure # --> success (the condition name gets accepted) # - there is somewhere a condition with this name within the already defined part of the procedure # - success (this already existing condition is within another block) # - failure (this already existing condition is within another block) { if ( 10 == $prng->int(1,10) ) { return 'cond1' } else { return 'cond'.$cond_count } } ; condition_name_use: # In case $cond_count == 0 we get an error because the condition does not exist { return 'cond'.$cond_count } ; handler_type: # UNDO is not supported # UNDO | CONTINUE | EXIT ; signal: SIGNAL signal_condition_value optional_signal_information ; resignal: RESIGNAL optional_signal_information | RESIGNAL signal_condition_value optional_signal_information ; optional_signal_information: | SET signal_information_list ; handler_action: some_statement ; resignal | some_statement ; drop_procedure1: DROP PROCEDURE procedure_name1 ; # This is currently unused and might be removed later. # declaration: # declare_handler | # declare_condition | # declare_variable ; update: UPDATE _table SET _field = value ; if: IF variable_name = value THEN signal_resignal ; ELSEIF variable_name = value THEN signal_resignal ; ELSE signal_resignal ; END IF ; declare_variable: DECLARE variable_name variable_type default_value; set_variable: SET at_variable_name = value ; value: CONVERT( _varchar(512) USING some_charset ) | _varchar(512) | _digit | at_variable_name | function_name ( _english , _digit , at_variable_name ) | RPAD( _letter , some_size , 'A123456789' ) ; some_size: 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 32 + 1 | 32 + 1 | 32 + 1 | 32 + 1 | 32 + 1 | 32 + 1 | 32 + 1 | 32 + 1 | 64 + 1 | 64 + 1 | 64 + 1 | 64 + 1 | 256 + 1 | 256 + 1 | 256 * 256 + 1 ; some_charset: UTF8 | BINARY | LATIN1 ; variable_name: var1 ; #| var2 | var3 ; at_variable_name: @var1 ; #| @var2 | @var3 ; variable_type: INTEGER | VARCHAR(32) ; default_value: # If the default is NULL than we assign in some situation NULL to # condition_information_item --> error | DEFAULT _english | DEFAULT _digit ; declare_condition: DECLARE condition_name CONDITION FOR condition_value ; condition_name: cond1 ; #| cond2 | cond3 ; signal_resignal: signal | resignal ; signal_information_list: signal_information | signal_information , signal_information ; value_keyword: | VALUE ; signal_information: condition_information_item = simple_value_specification ; declare_handler: DECLARE handler_type HANDLER FOR handler_condition_list procedure_statement ; sql_state_value: SQLSTATE value_keyword sqlstate_value ; signal_condition_value: # Notes: # - MySQL error codes like 1022 (ER_DUP_KEY) are not supported by signal/resignal # - SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE # Therefore we use condition_name less frequent. sql_state_value | sql_state_value | sql_state_value | sql_state_value | condition_name ; handler_condition_value: signal_condition_value | SQLWARNING | NOT FOUND | SQLEXCEPTION | mysql_error_code ; handler_condition_list: # This is used in DECLARE ... HANDLER ... handler_condition_value | handler_condition_value , handler_condition_value ; condition_value: # This is used in DECLARE ... CONDITION .... # SIGNAL/RESIGNAL can only use a CONDITION defined with SQLSTATE. # Therefore we decrease the likelihood of mysql_error_code. sql_state_value | sql_state_value | sql_state_value | sql_state_value | mysql_error_code ; condition_information_item: CLASS_ORIGIN | SUBCLASS_ORIGIN | CONSTRAINT_CATALOG | CONSTRAINT_SCHEMA | CONSTRAINT_NAME | CATALOG_NAME | SCHEMA_NAME | TABLE_NAME | COLUMN_NAME | CURSOR_NAME | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MESSAGE_TEXT | MYSQL_ERRNO | MYSQL_ERRNO | MYSQL_ERRNO | MYSQL_ERRNO | MYSQL_ERRNO ; simple_value_specification: _varchar(512) | _english | _digit | variable_name | at_variable_name ; mysql_error_code: # 0 causes an error in the context where this grammar item is used. # Therefore it should be rare. 0 | 1022 | 1022 | # ER_DUP_KEY 1062 | 1062 | # ER_DUP_ENTRY 1106 | 1106 | # ER_UNKNOWN_PROCEDURE 1305 | 1305 | # ER_SP_DOES_NOT_EXIST 1146 | 1146 | # ER_NO_SUCH_TABLE 1319 | 1319 # ER_SP_COND_MISMATCH ; sqlstate_value: '42000' | # ER_UNKNOWN_PROCEDURE,ER_SP_DOES_NOT_EXIST,ER_UNKNOWN_PROCEDURE,...) '42S02' | # ER_NO_SUCH_TABLE 'HY000' | # generic '23000' | # duplicate value (ER_DUP_KEY,ER_DUP_ENTRY) { $state_number = 46000 + $prng->int(1,20) ; return "'".$state_number."'" } ; _table: B | C ;