mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
MDEV-10579 sql_mode=ORACLE: Triggers: Understand :NEW.c1 and :OLD.c1 instead of NEW.c1 and OLD.c1
This commit is contained in:
parent
ca242117ce
commit
5721ea6ab7
6 changed files with 273 additions and 80 deletions
82
mysql-test/suite/compat/oracle/r/trigger.result
Normal file
82
mysql-test/suite/compat/oracle/r/trigger.result
Normal file
|
@ -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;
|
86
mysql-test/suite/compat/oracle/t/trigger.test
Normal file
86
mysql-test/suite/compat/oracle/t/trigger.test
Normal file
|
@ -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;
|
|
@ -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];
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Add table
Reference in a new issue