diff --git a/mysql-test/suite/compat/oracle/r/trigger.result b/mysql-test/suite/compat/oracle/r/trigger.result new file mode 100644 index 00000000000..c53f9600052 --- /dev/null +++ b/mysql-test/suite/compat/oracle/r/trigger.result @@ -0,0 +1,82 @@ +set sql_mode=ORACLE; +:NEW.a := 1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 +:OLD.a := 1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 +:OLa.a := 1; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a := 1' at line 1 +SELECT :NEW.a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a' at line 1 +SELECT :OLD.a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a' at line 1 +SELECT :OLa.a; +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'a' at line 1 +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:= 10; +INSERT INTO t1 VALUES (); +SELECT * FROM t1; +a +10 +DROP TRIGGER tr1; +DROP TABLE t1; +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW :NEW.a:= 10; +INSERT INTO t1 VALUES (); +SELECT * FROM t1; +a +10 +DROP TRIGGER tr1; +DROP TABLE t1; +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW +BEGIN +IF :NEW.a IS NULL +THEN +:NEW.a:= 10; +END IF; +END; +/ +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +a +10 +DROP TRIGGER tr1; +DROP TABLE t1; +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE UPDATE ON t1 FOR EACH ROW +BEGIN +IF :OLD.a IS NULL +THEN +:NEW.a:= 10; +END IF; +END; +/ +INSERT INTO t1 VALUES (NULL); +UPDATE t1 SET a=NULL; +SELECT * FROM t1; +a +10 +DROP TRIGGER tr1; +DROP TABLE t1; +CREATE TABLE t1 (a INT, b INT, c INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 +FOR EACH ROW +DECLARE +cnt INT := 0; +BEGIN +IF :NEW.a IS NULL THEN cnt:=cnt+1; END IF; +IF :NEW.b IS NULL THEN cnt:=cnt+1; END IF; +IF :NEW.c IS NULL THEN :NEW.c:=cnt; END IF; +END; +/ +INSERT INTO t1 VALUES (); +INSERT INTO t1 VALUES (1, NULL, NULL); +INSERT INTO t1 VALUES (NULL, 1, NULL); +INSERT INTO t1 VALUES (1, 1, NULL); +SELECT * FROM t1; +a b c +NULL NULL 2 +1 NULL 1 +NULL 1 1 +1 1 0 +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/t/trigger.test b/mysql-test/suite/compat/oracle/t/trigger.test new file mode 100644 index 00000000000..7c9fa5b3858 --- /dev/null +++ b/mysql-test/suite/compat/oracle/t/trigger.test @@ -0,0 +1,86 @@ +set sql_mode=ORACLE; + +--error ER_PARSE_ERROR +:NEW.a := 1; +--error ER_PARSE_ERROR +:OLD.a := 1; +--error ER_PARSE_ERROR +:OLa.a := 1; + +--error ER_PARSE_ERROR +SELECT :NEW.a; +--error ER_PARSE_ERROR +SELECT :OLD.a; +--error ER_PARSE_ERROR +SELECT :OLa.a; + +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW NEW.a:= 10; +INSERT INTO t1 VALUES (); +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + + +CREATE TABLE t1 (a INT); +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW :NEW.a:= 10; +INSERT INTO t1 VALUES (); +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + + +CREATE TABLE t1 (a INT); +DELIMITER /; +CREATE TRIGGER tr1 BEFORE INSERT ON t1 FOR EACH ROW +BEGIN + IF :NEW.a IS NULL + THEN + :NEW.a:= 10; + END IF; +END; +/ +DELIMITER ;/ +INSERT INTO t1 VALUES (NULL); +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + +CREATE TABLE t1 (a INT); +DELIMITER /; +CREATE TRIGGER tr1 BEFORE UPDATE ON t1 FOR EACH ROW +BEGIN + IF :OLD.a IS NULL + THEN + :NEW.a:= 10; + END IF; +END; +/ +DELIMITER ;/ +INSERT INTO t1 VALUES (NULL); +UPDATE t1 SET a=NULL; +SELECT * FROM t1; +DROP TRIGGER tr1; +DROP TABLE t1; + + + +CREATE TABLE t1 (a INT, b INT, c INT); +DELIMITER /; +CREATE TRIGGER tr1 BEFORE INSERT ON t1 +FOR EACH ROW +DECLARE + cnt INT := 0; +BEGIN + IF :NEW.a IS NULL THEN cnt:=cnt+1; END IF; + IF :NEW.b IS NULL THEN cnt:=cnt+1; END IF; + IF :NEW.c IS NULL THEN :NEW.c:=cnt; END IF; +END; +/ +DELIMITER ;/ +INSERT INTO t1 VALUES (); +INSERT INTO t1 VALUES (1, NULL, NULL); +INSERT INTO t1 VALUES (NULL, 1, NULL); +INSERT INTO t1 VALUES (1, 1, NULL); +SELECT * FROM t1; +DROP TABLE t1; diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index e67536a838d..f7edcb43f9c 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -5132,6 +5132,15 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable, } +bool LEX::is_trigger_new_or_old_reference(const LEX_STRING name) +{ + return sphead && sphead->m_type == TYPE_ENUM_TRIGGER && + name.length == 3 && + (!my_strcasecmp(system_charset_info, name.str, "NEW") || + !my_strcasecmp(system_charset_info, name.str, "OLD")); +} + + bool LEX::init_internal_variable(struct sys_var_with_base *variable, LEX_STRING dbname, LEX_STRING name) { @@ -5140,9 +5149,7 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable, thd->parse_error(); return true; } - if (sphead && sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, dbname.str, "NEW") || - !my_strcasecmp(system_charset_info, dbname.str, "OLD"))) + if (is_trigger_new_or_old_reference(dbname)) { if (dbname.str[0]=='O' || dbname.str[0]=='o') { @@ -5828,6 +5835,46 @@ bool LEX::sp_while_loop_finalize(THD *thd) } +Item *LEX::create_and_link_Item_trigger_field(THD *thd, const char *name, + bool new_row) +{ + Item_trigger_field *trg_fld; + + if (trg_chistics.event == TRG_EVENT_INSERT && !new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT"); + return NULL; + } + + if (trg_chistics.event == TRG_EVENT_DELETE && new_row) + { + my_error(ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE"); + return NULL; + } + + DBUG_ASSERT(!new_row || + (trg_chistics.event == TRG_EVENT_INSERT || + trg_chistics.event == TRG_EVENT_UPDATE)); + + const bool tmp_read_only= + !(new_row && trg_chistics.action_time == TRG_ACTION_BEFORE); + trg_fld= new (thd->mem_root) + Item_trigger_field(thd, current_context(), + new_row ? + Item_trigger_field::NEW_ROW: + Item_trigger_field::OLD_ROW, + name, SELECT_ACL, tmp_read_only); + /* + Let us add this item to list of all Item_trigger_field objects + in trigger. + */ + if (trg_fld) + trg_table_fields.link_in_list(trg_fld, &trg_fld->next_trg_field); + + return trg_fld; +} + + #ifdef MYSQL_SERVER uint binlog_unsafe_map[256]; diff --git a/sql/sql_lex.h b/sql/sql_lex.h index b9cb4e35687..940a54ff009 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -3117,6 +3117,11 @@ public: const char *start_in_q, const char *end_in_q); + bool is_trigger_new_or_old_reference(const LEX_STRING name); + + Item *create_and_link_Item_trigger_field(THD *thd, const char *name, + bool new_row); + void sp_block_init(THD *thd, const LEX_STRING label); void sp_block_init(THD *thd) { diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index f0e36bf3784..febe0d95239 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -13829,45 +13829,13 @@ simple_ident_q: we can't meet simple_ident_nospvar in trigger now. But it should be changed in future. */ - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + if (lex->is_trigger_new_or_old_reference($1)) { - Item_trigger_field *trg_fld; bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); - if (lex->trg_chistics.event == TRG_EVENT_INSERT && - !new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT")); - - if (lex->trg_chistics.event == TRG_EVENT_DELETE && - new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE")); - - DBUG_ASSERT(!new_row || - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); - const bool tmp_read_only= - !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); - trg_fld= new (thd->mem_root) - Item_trigger_field(thd, Lex->current_context(), - new_row ? - Item_trigger_field::NEW_ROW: - Item_trigger_field::OLD_ROW, - $3.str, - SELECT_ACL, - tmp_read_only); - if (trg_fld == NULL) + if (!($$= lex->create_and_link_Item_trigger_field(thd, $3.str, + new_row))) MYSQL_YYABORT; - - /* - Let us add this item to list of all Item_trigger_field objects - in trigger. - */ - lex->trg_table_fields.link_in_list(trg_fld, - &trg_fld->next_trg_field); - - $$= trg_fld; } else { diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index 46344a59b88..360f17b7f7d 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -13482,45 +13482,13 @@ simple_ident_q: we can't meet simple_ident_nospvar in trigger now. But it should be changed in future. */ - if (lex->sphead && lex->sphead->m_type == TYPE_ENUM_TRIGGER && - (!my_strcasecmp(system_charset_info, $1.str, "NEW") || - !my_strcasecmp(system_charset_info, $1.str, "OLD"))) + if (lex->is_trigger_new_or_old_reference($1)) { - Item_trigger_field *trg_fld; bool new_row= ($1.str[0]=='N' || $1.str[0]=='n'); - if (lex->trg_chistics.event == TRG_EVENT_INSERT && - !new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "OLD", "on INSERT")); - - if (lex->trg_chistics.event == TRG_EVENT_DELETE && - new_row) - my_yyabort_error((ER_TRG_NO_SUCH_ROW_IN_TRG, MYF(0), "NEW", "on DELETE")); - - DBUG_ASSERT(!new_row || - (lex->trg_chistics.event == TRG_EVENT_INSERT || - lex->trg_chistics.event == TRG_EVENT_UPDATE)); - const bool tmp_read_only= - !(new_row && lex->trg_chistics.action_time == TRG_ACTION_BEFORE); - trg_fld= new (thd->mem_root) - Item_trigger_field(thd, Lex->current_context(), - new_row ? - Item_trigger_field::NEW_ROW: - Item_trigger_field::OLD_ROW, - $3.str, - SELECT_ACL, - tmp_read_only); - if (trg_fld == NULL) + if (!($$= lex->create_and_link_Item_trigger_field(thd, $3.str, + new_row))) MYSQL_YYABORT; - - /* - Let us add this item to list of all Item_trigger_field objects - in trigger. - */ - lex->trg_table_fields.link_in_list(trg_fld, - &trg_fld->next_trg_field); - - $$= trg_fld; } else { @@ -13545,6 +13513,23 @@ simple_ident_q: MYSQL_YYABORT; } } + | ':' ident '.' ident + { + LEX *lex= Lex; + if (lex->is_trigger_new_or_old_reference($2)) + { + bool new_row= ($2.str[0]=='N' || $2.str[0]=='n'); + if (!($$= Lex->create_and_link_Item_trigger_field(thd, + $4.str, + new_row))) + MYSQL_YYABORT; + } + else + { + thd->parse_error(); + MYSQL_YYABORT; + } + } | '.' ident '.' ident { LEX *lex= thd->lex; @@ -14343,15 +14328,25 @@ set_assign: } set_expr_or_default { - sp_pcontext *spc= Lex->spcont; - sp_variable *spv= spc->find_variable($1.base_name, false); + if ($1.var == trg_new_row_fake_var) + { + /* We are in trigger and assigning value to field of new row */ + if (Lex->set_trigger_new_row(&$1.base_name, $4) || + Lex->sphead->restore_lex(thd)) + MYSQL_YYABORT; + } + else + { + sp_pcontext *spc= Lex->spcont; + sp_variable *spv= spc->find_variable($1.base_name, false); - /* It is a local variable. */ - if (Lex->set_local_variable(spv, $4)) - MYSQL_YYABORT; + /* It is a local variable. */ + if (Lex->set_local_variable(spv, $4)) + MYSQL_YYABORT; - if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) - MYSQL_YYABORT; + if (sp_create_assignment_instr(thd, yychar == YYEMPTY)) + MYSQL_YYABORT; + } } ; @@ -14671,6 +14666,16 @@ internal_variable_name_directly_assignable: if (Lex->init_default_internal_variable(&$$, $3)) MYSQL_YYABORT; } + | ':' ident_directly_assignable '.' ident + { + if (!Lex->is_trigger_new_or_old_reference($2)) + { + thd->parse_error(); + MYSQL_YYABORT; + } + if (Lex->init_internal_variable(&$$, $2, $4)) + MYSQL_YYABORT; + } ; transaction_characteristics: