Commit 0686c34d authored by Sergei Golubchik's avatar Sergei Golubchik

MDEV-8605 MariaDB not use DEFAULT value even when inserted NULL for NOT NULLABLE column

NOT NULL constraint must be checked *after* the BEFORE triggers.
That is for INSERT and UPDATE statements even NOT NULL fields
must be able to store a NULL temporarily at least while
BEFORE INSERT/UPDATE triggers are running.
parent ad5db17e
...@@ -214,12 +214,14 @@ end if; ...@@ -214,12 +214,14 @@ end if;
end| end|
insert into t3 values (1); insert into t3 values (1);
insert into t1 values (4, "four", 1), (5, "five", 2); insert into t1 values (4, "four", 1), (5, "five", 2);
ERROR 23000: Column 'id' cannot be null Warnings:
Warning 1048 Column 'id' cannot be null
select * from t1; select * from t1;
id data fk id data fk
1 one NULL 1 one NULL
2 two NULL 2 two NULL
4 four 1 4 four 1
0 five 2
select * from t2; select * from t2;
event event
INSERT INTO t1 id=1 data='one' INSERT INTO t1 id=1 data='one'
......
set sql_mode=strict_all_tables;
set time_zone="+02:00";
create table t1 (a int not null, b int, c int);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null,new.b,new.c);
insert t1 values (10, NULL, 1);
insert t1 values (NULL, 2, NULL);
insert t1 values (NULL, NULL, 20);
ERROR 23000: Column 'a' cannot be null
insert t1 values (1, 2, NULL);
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
insert ignore t1 values (NULL, NULL, 30);
Warnings:
Warning 1048 Column 'a' cannot be null
insert ignore t1 values (1, 3, NULL);
Warnings:
Warning 1048 Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
0 NULL 30
0 3 NULL
insert t1 set a=NULL, b=4, c=a;
select * from t1;
a b c
1 NULL 1
2 2 NULL
0 NULL 30
0 3 NULL
4 4 NULL
delete from t1;
insert t1 (a,c) values (10, 1);
insert t1 (a,b) values (NULL, 2);
insert t1 (a,c) values (NULL, 20);
ERROR 23000: Column 'a' cannot be null
insert t1 (a,b) values (1, 2);
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
delete from t1;
insert t1 select 10, NULL, 1;
insert t1 select NULL, 2, NULL;
insert t1 select NULL, NULL, 20;
ERROR 23000: Column 'a' cannot be null
insert t1 select 1, 2, NULL;
ERROR 23000: Column 'a' cannot be null
insert ignore t1 select NULL, NULL, 30;
Warnings:
Warning 1048 Column 'a' cannot be null
insert ignore t1 select 1, 3, NULL;
Warnings:
Warning 1048 Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
0 NULL 30
0 3 NULL
delete from t1;
insert delayed t1 values (10, NULL, 1);
insert delayed t1 values (NULL, 2, NULL);
insert delayed t1 values (NULL, NULL, 20);
ERROR 23000: Column 'a' cannot be null
insert delayed t1 values (1, 2, NULL);
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
insert delayed ignore t1 values (NULL, NULL, 30);
Warnings:
Warning 1048 Column 'a' cannot be null
insert delayed ignore t1 values (1, 3, NULL);
Warnings:
Warning 1048 Column 'a' cannot be null
flush table t1;
select * from t1;
a b c
1 NULL 1
2 2 NULL
0 NULL 30
0 3 NULL
delete from t1;
alter table t1 add primary key (a);
create trigger trgu before update on t1 for each row set new.a=if(new.a is null,new.b,new.c);
insert t1 values (100,100,100), (200,200,200), (300,300,300);
insert t1 values (100,100,100) on duplicate key update a=10, b=NULL, c=1;
insert t1 values (200,200,200) on duplicate key update a=NULL, b=2, c=NULL;
insert t1 values (300,300,300) on duplicate key update a=NULL, b=NULL, c=20;
ERROR 23000: Column 'a' cannot be null
insert t1 values (300,300,300) on duplicate key update a=1, b=2, c=NULL;
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
300 300 300
delete from t1;
insert t1 values (1,100,1), (2,200,2);
replace t1 values (10, NULL, 1);
replace t1 values (NULL, 2, NULL);
replace t1 values (NULL, NULL, 30);
ERROR 23000: Column 'a' cannot be null
replace t1 values (1, 3, NULL);
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
delete from t1;
insert t1 values (100,100,100), (200,200,200), (300,300,300);
update t1 set a=10, b=NULL, c=1 where a=100;
update t1 set a=NULL, b=2, c=NULL where a=200;
update t1 set a=NULL, b=NULL, c=20 where a=300;
ERROR 23000: Column 'a' cannot be null
update t1 set a=1, b=2, c=NULL where a=300;
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
300 300 300
set statement sql_mode='' for update t1 set a=1, b=2, c=NULL where a > 1;
ERROR 23000: Duplicate entry '0' for key 'PRIMARY'
select * from t1;
a b c
1 NULL 1
0 2 NULL
300 300 300
update t1 set a=NULL, b=4, c=a where a=300;
select * from t1;
a b c
1 NULL 1
0 2 NULL
4 4 NULL
delete from t1;
create table t2 (d int, e int);
insert t1 values (100,100,100), (200,200,200), (300,300,300);
insert t2 select a,b from t1;
update t1,t2 set a=10, b=NULL, c=1 where b=d and e=100;
update t1,t2 set a=NULL, b=2, c=NULL where b=d and e=200;
update t1,t2 set a=NULL, b=NULL, c=20 where b=d and e=300;
ERROR 23000: Column 'a' cannot be null
update t1,t2 set a=1, b=2, c=NULL where b=d and e=300;
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
300 300 300
update t1,t2 set a=NULL, b=4, c=a where b=d and e=300;
select * from t1;
a b c
1 NULL 1
2 2 NULL
4 4 300
delete from t1;
insert t2 values (2,2);
create view v1 as select * from t1, t2 where d=2;
insert v1 (a,c) values (10, 1);
insert v1 (a,b) values (NULL, 2);
insert v1 (a,c) values (NULL, 20);
ERROR 23000: Column 'a' cannot be null
insert v1 (a,b) values (1, 2);
ERROR 23000: Column 'a' cannot be null
select * from v1;
a b c d e
1 NULL 1 2 2
2 2 NULL 2 2
delete from t1;
drop view v1;
drop table t2;
load data infile 'mdev8605.txt' into table t1 fields terminated by ',';
ERROR 23000: Column 'a' cannot be null
select * from t1;
a b c
1 NULL 1
2 2 NULL
drop table t1;
create table t1 (a timestamp, b int auto_increment primary key);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null, '2000-10-20 10:20:30', NULL);
set statement timestamp=777777777 for insert t1 (a) values (NULL);
set statement timestamp=888888888 for insert t1 (a) values ('1999-12-11 10:9:8');
select b, a, unix_timestamp(a) from t1;
b a unix_timestamp(a)
1 2000-10-20 10:20:30 972030030
2 1998-03-03 03:34:48 888888888
set statement timestamp=999999999 for update t1 set b=3 where b=2;
select b, a, unix_timestamp(a) from t1;
b a unix_timestamp(a)
1 2000-10-20 10:20:30 972030030
3 2001-09-09 03:46:39 999999999
create trigger trgu before update on t1 for each row set new.a='2011-11-11 11:11:11';
update t1 set b=4 where b=3;
select b, a, unix_timestamp(a) from t1;
b a unix_timestamp(a)
1 2000-10-20 10:20:30 972030030
4 2011-11-11 11:11:11 1321002671
drop table t1;
create table t1 (a int auto_increment primary key);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null, 5, NULL);
insert t1 values (NULL);
insert t1 values (10);
select a from t1;
a
5
6
drop table t1;
create table t1 (a int, b int, c int);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null,new.b,new.c);
insert t1 values (10, NULL, 1);
insert t1 values (NULL, 2, NULL);
insert t1 values (NULL, NULL, 20);
insert t1 values (1, 2, NULL);
select * from t1;
a b c
1 NULL 1
2 2 NULL
NULL NULL 20
NULL 2 NULL
drop table t1;
create table t1 (a1 tinyint not null, a2 timestamp not null,
a3 tinyint not null auto_increment primary key,
b tinyint, c int not null);
create trigger trgi before insert on t1 for each row
begin
if new.b=1 then set new.a1=if(new.c,new.c,null); end if;
if new.b=2 then set new.a2=if(new.c,new.c,null); end if;
if new.b=3 then set new.a3=if(new.c,new.c,null); end if;
end|
set statement timestamp=777777777 for
load data infile 'sep8605.txt' into table t1 fields terminated by ',';
ERROR 23000: Column 'a1' cannot be null
select * from t1;
a1 a2 a3 b c
1 2010-11-12 01:02:03 10 0 0
2 2010-11-12 01:02:03 11 1 2
3 1994-08-25 03:22:57 12 0 0
4 2000-09-08 07:06:05 13 2 908070605
5 1994-08-25 03:22:57 14 2 0
6 2010-11-12 01:02:03 15 0 0
7 2010-11-12 01:02:03 20 3 20
8 2010-11-12 01:02:03 21 3 0
delete from t1;
set statement timestamp=777777777 for
load data infile 'sep8605.txt' into table t1 fields terminated by ','
(@a,a2,a3,b,c) set a1=100-@a;
ERROR 23000: Column 'a1' cannot be null
select 100-a1,a2,a3,b,c from t1;
100-a1 a2 a3 b c
1 2010-11-12 01:02:03 10 0 0
98 2010-11-12 01:02:03 11 1 2
3 1994-08-25 03:22:57 12 0 0
4 2000-09-08 07:06:05 13 2 908070605
5 1994-08-25 03:22:57 14 2 0
6 2010-11-12 01:02:03 22 0 0
7 2010-11-12 01:02:03 20 3 20
8 2010-11-12 01:02:03 23 3 0
delete from t1;
set statement timestamp=777777777 for
load data infile 'fix8605.txt' into table t1 fields terminated by '';
ERROR 23000: Column 'a1' cannot be null
select * from t1;
a1 a2 a3 b c
1 2010-11-12 01:02:03 10 0 0
5 1994-08-25 03:22:57 14 2 0
8 2010-11-12 01:02:03 24 3 0
delete from t1;
set statement timestamp=777777777 for
load xml infile 'xml8605.txt' into table t1 rows identified by '<row>';
ERROR 23000: Column 'a1' cannot be null
select * from t1;
a1 a2 a3 b c
1 2010-11-12 01:02:03 10 0 0
2 2010-11-12 01:02:03 11 1 2
3 1994-08-25 03:22:57 12 0 0
4 2000-09-08 07:06:05 13 2 908070605
5 1994-08-25 03:22:57 14 2 0
6 2010-11-12 01:02:03 25 0 0
7 2010-11-12 01:02:03 20 3 20
8 2010-11-12 01:02:03 26 3 0
drop table t1;
create table t1 (a int not null default 5, b int, c int);
create trigger trgi before insert on t1 for each row set new.b=new.c;
insert t1 values (DEFAULT,2,1);
select * from t1;
a b c
5 1 1
drop table t1;
create table t1 (a int not null, b int not null default 5, c int);
create trigger trgi before insert on t1 for each row
begin
if new.c=1 then set new.a=1, new.b=1; end if;
if new.c=2 then set new.a=NULL, new.b=NULL; end if;
if new.c=3 then set new.a=2; end if;
end|
insert t1 values (9, 9, 1);
insert t1 values (9, 9, 2);
ERROR 23000: Column 'a' cannot be null
insert t1 (a,c) values (9, 3);
select * from t1;
a b c
1 1 1
2 5 3
drop table t1;
...@@ -189,8 +189,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL ...@@ -189,8 +189,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4'; where f122='Test 3.5.9.4';
Warnings: Warnings:
Warning 1048 Column 'f136' cannot be null Warning 1048 Column 'f136' cannot be null
Update tb3 Set f122='Test 3.5.9.4-trig', f136=0, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4';
select f118, f121, f122, f136, f151, f163 from tb3 select f118, f121, f122, f136, f151, f163 from tb3
where f122 like 'Test 3.5.9.4-trig' order by f163; where f122 like 'Test 3.5.9.4-trig' order by f163;
f118 f121 f122 f136 f151 f163 f118 f121 f122 f136 f151 f163
...@@ -198,7 +196,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL ...@@ -198,7 +196,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122, select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163; @tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163 @tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
a NULL Test 3.5.9.4-trig 0 999 NULL a NULL Test 3.5.9.4-trig NULL 999 NULL
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122, select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163; @tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163 @tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163
......
...@@ -190,8 +190,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL ...@@ -190,8 +190,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4'; where f122='Test 3.5.9.4';
Warnings: Warnings:
Warning 1048 Column 'f136' cannot be null Warning 1048 Column 'f136' cannot be null
Update tb3 Set f122='Test 3.5.9.4-trig', f136=0, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4';
select f118, f121, f122, f136, f151, f163 from tb3 select f118, f121, f122, f136, f151, f163 from tb3
where f122 like 'Test 3.5.9.4-trig' order by f163; where f122 like 'Test 3.5.9.4-trig' order by f163;
f118 f121 f122 f136 f151 f163 f118 f121 f122 f136 f151 f163
...@@ -199,7 +197,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL ...@@ -199,7 +197,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122, select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163; @tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163 @tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
a NULL Test 3.5.9.4-trig 0 999 NULL a NULL Test 3.5.9.4-trig NULL 999 NULL
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122, select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163; @tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163 @tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163
......
...@@ -190,8 +190,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL ...@@ -190,8 +190,6 @@ Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4'; where f122='Test 3.5.9.4';
Warnings: Warnings:
Warning 1048 Column 'f136' cannot be null Warning 1048 Column 'f136' cannot be null
Update tb3 Set f122='Test 3.5.9.4-trig', f136=0, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4';
select f118, f121, f122, f136, f151, f163 from tb3 select f118, f121, f122, f136, f151, f163 from tb3
where f122 like 'Test 3.5.9.4-trig' order by f163; where f122 like 'Test 3.5.9.4-trig' order by f163;
f118 f121 f122 f136 f151 f163 f118 f121 f122 f136 f151 f163
...@@ -199,7 +197,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL ...@@ -199,7 +197,7 @@ a NULL Test 3.5.9.4-trig 00000 999 NULL
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122, select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
@tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163; @tr_var_b4_136, @tr_var_b4_151, @tr_var_b4_163;
@tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163 @tr_var_b4_118 @tr_var_b4_121 @tr_var_b4_122 @tr_var_b4_136 @tr_var_b4_151 @tr_var_b4_163
a NULL Test 3.5.9.4-trig 0 999 NULL a NULL Test 3.5.9.4-trig NULL 999 NULL
select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122, select @tr_var_af_118, @tr_var_af_121, @tr_var_af_122,
@tr_var_af_136, @tr_var_af_151, @tr_var_af_163; @tr_var_af_136, @tr_var_af_151, @tr_var_af_163;
@tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163 @tr_var_af_118 @tr_var_af_121 @tr_var_af_122 @tr_var_af_136 @tr_var_af_151 @tr_var_af_163
......
...@@ -186,9 +186,6 @@ let $message= Testcase 3.5.9.4:; ...@@ -186,9 +186,6 @@ let $message= Testcase 3.5.9.4:;
Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL Update tb3 Set f122='Test 3.5.9.4-trig', f136=NULL, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4'; where f122='Test 3.5.9.4';
Update tb3 Set f122='Test 3.5.9.4-trig', f136=0, f151=DEFAULT, f163=NULL
where f122='Test 3.5.9.4';
select f118, f121, f122, f136, f151, f163 from tb3 select f118, f121, f122, f136, f151, f163 from tb3
where f122 like 'Test 3.5.9.4-trig' order by f163; where f122 like 'Test 3.5.9.4-trig' order by f163;
select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122, select @tr_var_b4_118, @tr_var_b4_121, @tr_var_b4_122,
......
...@@ -39,7 +39,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3; ...@@ -39,7 +39,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3;
a name old_a old_b truncate(rand_value,4) a name old_a old_b truncate(rand_value,4)
100 log 0 0 0.0000 100 log 0 0 0.0000
101 t1 1 1 0.3203 101 t1 1 1 0.3203
102 t1 0 2 0.5666 102 t1 NULL 2 0.5666
103 t2 1 2 0.9164 103 t2 1 2 0.9164
104 t2 3 0 0.8826 104 t2 3 0 0.8826
105 t2 4 0 0.6635 105 t2 4 0 0.6635
......
...@@ -35,7 +35,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3; ...@@ -35,7 +35,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3;
a name old_a old_b truncate(rand_value,4) a name old_a old_b truncate(rand_value,4)
100 log 0 0 0.0000 100 log 0 0 0.0000
101 t1 1 1 0.3203 101 t1 1 1 0.3203
102 t1 0 2 0.5666 102 t1 NULL 2 0.5666
103 t2 1 2 0.9164 103 t2 1 2 0.9164
104 t2 3 0 0.8826 104 t2 3 0 0.8826
105 t2 4 0 0.6635 105 t2 4 0 0.6635
...@@ -58,7 +58,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3; ...@@ -58,7 +58,7 @@ select a,name, old_a, old_b, truncate(rand_value,4) from t3;
a name old_a old_b truncate(rand_value,4) a name old_a old_b truncate(rand_value,4)
100 log 0 0 0.0000 100 log 0 0 0.0000
101 t1 1 1 0.3203 101 t1 1 1 0.3203
102 t1 0 2 0.5666 102 t1 NULL 2 0.5666
103 t2 1 2 0.9164 103 t2 1 2 0.9164
104 t2 3 0 0.8826 104 t2 3 0 0.8826
105 t2 4 0 0.6635 105 t2 4 0 0.6635
......
...@@ -241,7 +241,6 @@ begin ...@@ -241,7 +241,6 @@ begin
end| end|
delimiter ;| delimiter ;|
insert into t3 values (1); insert into t3 values (1);
--error ER_BAD_NULL_ERROR
insert into t1 values (4, "four", 1), (5, "five", 2); insert into t1 values (4, "four", 1), (5, "five", 2);
select * from t1; select * from t1;
select * from t2; select * from t2;
......
#
# MDEV-8605 MariaDB not use DEFAULT value even when inserted NULL for NOT NULLABLE column.
#
set sql_mode=strict_all_tables;
set time_zone="+02:00";
create table t1 (a int not null, b int, c int);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null,new.b,new.c);
# INSERT
insert t1 values (10, NULL, 1);
insert t1 values (NULL, 2, NULL);
--error ER_BAD_NULL_ERROR
insert t1 values (NULL, NULL, 20);
--error ER_BAD_NULL_ERROR
insert t1 values (1, 2, NULL);
select * from t1;
# INSERT IGNORE
insert ignore t1 values (NULL, NULL, 30);
insert ignore t1 values (1, 3, NULL);
select * from t1;
# fields in the value list
insert t1 set a=NULL, b=4, c=a;
select * from t1;
delete from t1;
#insert (column list)
insert t1 (a,c) values (10, 1);
insert t1 (a,b) values (NULL, 2);
--error ER_BAD_NULL_ERROR
insert t1 (a,c) values (NULL, 20);
--error ER_BAD_NULL_ERROR
insert t1 (a,b) values (1, 2);
select * from t1;
delete from t1;
# insert select
insert t1 select 10, NULL, 1;
insert t1 select NULL, 2, NULL;
--error ER_BAD_NULL_ERROR
insert t1 select NULL, NULL, 20;
--error ER_BAD_NULL_ERROR
insert t1 select 1, 2, NULL;
insert ignore t1 select NULL, NULL, 30;
insert ignore t1 select 1, 3, NULL;
select * from t1;
delete from t1;
# insert delayed
insert delayed t1 values (10, NULL, 1);
insert delayed t1 values (NULL, 2, NULL);
--error ER_BAD_NULL_ERROR
insert delayed t1 values (NULL, NULL, 20);
--error ER_BAD_NULL_ERROR
insert delayed t1 values (1, 2, NULL);
select * from t1;
insert delayed ignore t1 values (NULL, NULL, 30);
insert delayed ignore t1 values (1, 3, NULL);
flush table t1; # wait for inserts to finish
select * from t1;
delete from t1;
# insert on dup key update
alter table t1 add primary key (a);
create trigger trgu before update on t1 for each row set new.a=if(new.a is null,new.b,new.c);
insert t1 values (100,100,100), (200,200,200), (300,300,300);
insert t1 values (100,100,100) on duplicate key update a=10, b=NULL, c=1;
insert t1 values (200,200,200) on duplicate key update a=NULL, b=2, c=NULL;
--error ER_BAD_NULL_ERROR
insert t1 values (300,300,300) on duplicate key update a=NULL, b=NULL, c=20;
--error ER_BAD_NULL_ERROR
insert t1 values (300,300,300) on duplicate key update a=1, b=2, c=NULL;
select * from t1;
delete from t1;
# replace
insert t1 values (1,100,1), (2,200,2);
replace t1 values (10, NULL, 1);
replace t1 values (NULL, 2, NULL);
--error ER_BAD_NULL_ERROR
replace t1 values (NULL, NULL, 30);
--error ER_BAD_NULL_ERROR
replace t1 values (1, 3, NULL);
select * from t1;
delete from t1;
# update
insert t1 values (100,100,100), (200,200,200), (300,300,300);
update t1 set a=10, b=NULL, c=1 where a=100;
update t1 set a=NULL, b=2, c=NULL where a=200;
--error ER_BAD_NULL_ERROR
update t1 set a=NULL, b=NULL, c=20 where a=300;
--error ER_BAD_NULL_ERROR
update t1 set a=1, b=2, c=NULL where a=300;
select * from t1;
# Test UPDATE with NULL violation in many rows
--error ER_DUP_ENTRY
set statement sql_mode='' for update t1 set a=1, b=2, c=NULL where a > 1;
select * from t1;
# fields in the value list
update t1 set a=NULL, b=4, c=a where a=300;
select * from t1;
delete from t1;
# multi-update
create table t2 (d int, e int);
insert t1 values (100,100,100), (200,200,200), (300,300,300);
insert t2 select a,b from t1;
update t1,t2 set a=10, b=NULL, c=1 where b=d and e=100;
update t1,t2 set a=NULL, b=2, c=NULL where b=d and e=200;
--error ER_BAD_NULL_ERROR
update t1,t2 set a=NULL, b=NULL, c=20 where b=d and e=300;
--error ER_BAD_NULL_ERROR
update t1,t2 set a=1, b=2, c=NULL where b=d and e=300;
select * from t1;
# fields in the value list
update t1,t2 set a=NULL, b=4, c=a where b=d and e=300;
select * from t1;
delete from t1;
# view
insert t2 values (2,2);
create view v1 as select * from t1, t2 where d=2;
insert v1 (a,c) values (10, 1);
insert v1 (a,b) values (NULL, 2);
--error ER_BAD_NULL_ERROR
insert v1 (a,c) values (NULL, 20);
--error ER_BAD_NULL_ERROR
insert v1 (a,b) values (1, 2);
select * from v1;
delete from t1;
drop view v1;
drop table t2;
# load data
let $datadir=`select @@datadir`;
--write_file $datadir/test/mdev8605.txt
10,\N,1
\N,2,\N
\N,\N,20
EOF
--error ER_BAD_NULL_ERROR
load data infile 'mdev8605.txt' into table t1 fields terminated by ',';
select * from t1;
drop table t1;
# timestamps (on NULL = NOW())
create table t1 (a timestamp, b int auto_increment primary key);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null, '2000-10-20 10:20:30', NULL);
set statement timestamp=777777777 for insert t1 (a) values (NULL);
set statement timestamp=888888888 for insert t1 (a) values ('1999-12-11 10:9:8');
select b, a, unix_timestamp(a) from t1;
set statement timestamp=999999999 for update t1 set b=3 where b=2;
select b, a, unix_timestamp(a) from t1;
create trigger trgu before update on t1 for each row set new.a='2011-11-11 11:11:11';
update t1 set b=4 where b=3;
select b, a, unix_timestamp(a) from t1;
drop table t1;
# auto-increment (on NULL = int)
create table t1 (a int auto_increment primary key);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null, 5, NULL);
insert t1 values (NULL);
insert t1 values (10);
select a from t1;
drop table t1;
# insert, all columns NULLable
create table t1 (a int, b int, c int);
create trigger trgi before insert on t1 for each row set new.a=if(new.a is null,new.b,new.c);
insert t1 values (10, NULL, 1);
insert t1 values (NULL, 2, NULL);
insert t1 values (NULL, NULL, 20);
insert t1 values (1, 2, NULL);
select * from t1;
drop table t1;
# more load data: autoinc and timestamp, different load formats
create table t1 (a1 tinyint not null, a2 timestamp not null,
a3 tinyint not null auto_increment primary key,
b tinyint, c int not null);
delimiter |;
create trigger trgi before insert on t1 for each row
begin
if new.b=1 then set new.a1=if(new.c,new.c,null); end if;
if new.b=2 then set new.a2=if(new.c,new.c,null); end if;
if new.b=3 then set new.a3=if(new.c,new.c,null); end if;
end|
delimiter ;|
--write_file $datadir/test/sep8605.txt
1,2010-11-12 1:2:3,10,0,0
\N,2010-11-12 1:2:3,11,1,2
3,\N,12,0,0
4,\N,13,2,908070605
5,2010-11-12 1:2:3,14,2,0
6,2010-11-12 1:2:3,\N,0,0
7,2010-11-12 1:2:3,\N,3,20
8,2010-11-12 1:2:3,30,3,0
99,2010-11-12 1:2:3,0,1,0
EOF
--error ER_BAD_NULL_ERROR
set statement timestamp=777777777 for
load data infile 'sep8605.txt' into table t1 fields terminated by ',';
select * from t1;
delete from t1;
--error ER_BAD_NULL_ERROR
set statement timestamp=777777777 for
load data infile 'sep8605.txt' into table t1 fields terminated by ','
(@a,a2,a3,b,c) set a1=100-@a;
select 100-a1,a2,a3,b,c from t1;
delete from t1;
--write_file $datadir/test/fix8605.txt
00012010-11-12 01:02:030010000000000000000
00052010-11-12 01:02:030014000200000000000
00082010-11-12 01:02:030030000300000000000
00992010-11-12 01:02:030000000100000000000
EOF
--error ER_BAD_NULL_ERROR
set statement timestamp=777777777 for
load data infile 'fix8605.txt' into table t1 fields terminated by '';
select * from t1;
delete from t1;
--write_file $datadir/test/xml8605.txt
<data>
<row>
<field name="a1">1</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="a3">10</field>
<field name="b">0</field>
<field name="c">0</field>
</row>
<row>
<field name="a2">2010-11-12 1:2:3</field>
<field name="a3">11</field>
<field name="b">1</field>
<field name="c">2</field>
</row>
<row>
<field name="a1">3</field>
<field name="a3">12</field>
<field name="b">0</field>
<field name="c">0</field>
</row>
<row>
<field name="a1">4</field>
<field name="a3">13</field>
<field name="b">2</field>
<field name="c">908070605</field>
</row>
<row>
<field name="a1">5</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="a3">14</field>
<field name="b">2</field>
<field name="c">0</field>
</row>
<row>
<field name="a1">6</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="b">0</field>
<field name="c">0</field>
</row>
<row>
<field name="a1">7</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="b">3</field>
<field name="c">20</field>
</row>
<row>
<field name="a1">8</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="a3">30</field>
<field name="b">3</field>
<field name="c">0</field>
</row>
<row>
<field name="a1">99</field>
<field name="a2">2010-11-12 1:2:3</field>
<field name="a3">0</field>
<field name="b">1</field>
<field name="c">0</field>
</row>
</data>
EOF
--error ER_BAD_NULL_ERROR
set statement timestamp=777777777 for
load xml infile 'xml8605.txt' into table t1 rows identified by '<row>';
select * from t1;
drop table t1;
# explicit DEFAULT
create table t1 (a int not null default 5, b int, c int);
create trigger trgi before insert on t1 for each row set new.b=new.c;
insert t1 values (DEFAULT,2,1);
select * from t1;
drop table t1;
# Two statements, first fails, second uses an implicit default
create table t1 (a int not null, b int not null default 5, c int);
delimiter |;
create trigger trgi before insert on t1 for each row
begin
if new.c=1 then set new.a=1, new.b=1; end if;
if new.c=2 then set new.a=NULL, new.b=NULL; end if;
if new.c=3 then set new.a=2; end if;
end|
delimiter ;|
insert t1 values (9, 9, 1);
--error ER_BAD_NULL_ERROR
insert t1 values (9, 9, 2);
insert t1 (a,c) values (9, 3);
select * from t1;
drop table t1;
...@@ -844,7 +844,7 @@ class Field: public Value_source ...@@ -844,7 +844,7 @@ class Field: public Value_source
my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values - my_ptrdiff_t l_offset= (my_ptrdiff_t) (table->s->default_values -
table->record[0]); table->record[0]);
memcpy(ptr, ptr + l_offset, pack_length()); memcpy(ptr, ptr + l_offset, pack_length());
if (null_ptr) if (maybe_null_in_table())
*null_ptr= ((*null_ptr & (uchar) ~null_bit) | *null_ptr= ((*null_ptr & (uchar) ~null_bit) |
(null_ptr[l_offset] & null_bit)); (null_ptr[l_offset] & null_bit));
} }
...@@ -1024,9 +1024,9 @@ class Field: public Value_source ...@@ -1024,9 +1024,9 @@ class Field: public Value_source
{ return null_ptr && (null_ptr[row_offset] & null_bit); } { return null_ptr && (null_ptr[row_offset] & null_bit); }
inline bool is_null_in_record(const uchar *record) const inline bool is_null_in_record(const uchar *record) const
{ {
if (!null_ptr) if (maybe_null_in_table())
return 0; return record[(uint) (null_ptr - table->record[0])] & null_bit;
return record[(uint) (null_ptr - table->record[0])] & null_bit; return 0;
} }
inline void set_null(my_ptrdiff_t row_offset= 0) inline void set_null(my_ptrdiff_t row_offset= 0)
{ if (null_ptr) null_ptr[row_offset]|= null_bit; } { if (null_ptr) null_ptr[row_offset]|= null_bit; }
...@@ -1035,10 +1035,19 @@ class Field: public Value_source ...@@ -1035,10 +1035,19 @@ class Field: public Value_source
inline bool maybe_null(void) const inline bool maybe_null(void) const
{ return null_ptr != 0 || table->maybe_null; } { return null_ptr != 0 || table->maybe_null; }
/* @return true if this field is NULL-able, false otherwise. */ /* @return true if this field is NULL-able (even if temporarily) */
inline bool real_maybe_null(void) const { return null_ptr != 0; } inline bool real_maybe_null(void) const { return null_ptr != 0; }
uint null_offset(const uchar *record) const uint null_offset(const uchar *record) const
{ return (uint) (null_ptr - record); } { return (uint) (null_ptr - record); }
/*
For a NULL-able field (that can actually store a NULL value in a table)
null_ptr points to the "null bitmap" in the table->record[0] header. For
NOT NULL fields it is either 0 or points outside table->record[0] into the
table->triggers->extra_null_bitmap (so that the field can store a NULL
value temporarily, only in memory)
*/
bool maybe_null_in_table() const
{ return null_ptr >= table->record[0] && null_ptr <= ptr; }
uint null_offset() const uint null_offset() const
{ return null_offset(table->record[0]); } { return null_offset(table->record[0]); }
...@@ -3600,6 +3609,7 @@ enum_field_types get_blob_type_from_length(ulong length); ...@@ -3600,6 +3609,7 @@ enum_field_types get_blob_type_from_length(ulong length);
uint32 calc_pack_length(enum_field_types type,uint32 length); uint32 calc_pack_length(enum_field_types type,uint32 length);
int set_field_to_null(Field *field); int set_field_to_null(Field *field);
int set_field_to_null_with_conversions(Field *field, bool no_conversions); int set_field_to_null_with_conversions(Field *field, bool no_conversions);
int convert_null_to_field_value_or_error(Field *field);
/* /*
The following are for the interface with the .frm file The following are for the interface with the .frm file
......
...@@ -152,6 +152,36 @@ int set_field_to_null(Field *field) ...@@ -152,6 +152,36 @@ int set_field_to_null(Field *field)
} }
/**
Set TIMESTAMP to NOW(), AUTO_INCREMENT to the next number, or report an error
@param field Field to update
@retval
0 Field could take 0 or an automatic conversion was used
@retval
-1 Field could not take NULL and no conversion was used.
If no_conversion was not set, an error message is printed
*/
int convert_null_to_field_value_or_error(Field *field)
{
if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
((Field_timestamp*) field)->set_time();
return 0;
}
field->reset(); // Note: we ignore any potential failure of reset() here.
if (field == field->table->next_number_field)
{
field->table->auto_increment_field_not_null= FALSE;
return 0; // field is set in fill_record()
}
return set_bad_null_error(field, ER_BAD_NULL_ERROR);
}
/** /**
Set field to NULL or TIMESTAMP or to next auto_increment number. Set field to NULL or TIMESTAMP or to next auto_increment number.
...@@ -186,26 +216,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions) ...@@ -186,26 +216,7 @@ set_field_to_null_with_conversions(Field *field, bool no_conversions)
if (no_conversions) if (no_conversions)
return -1; return -1;
/* return convert_null_to_field_value_or_error(field);
Check if this is a special type, which will get a special walue
when set to NULL (TIMESTAMP fields which allow setting to NULL
are handled by first check).
*/
if (field->type() == MYSQL_TYPE_TIMESTAMP)
{
((Field_timestamp*) field)->set_time();
return 0; // Ok to set time to NULL
}
// Note: we ignore any potential failure of reset() here.
field->reset();
if (field == field->table->next_number_field)
{
field->table->auto_increment_field_not_null= FALSE;
return 0; // field is set in fill_record()
}
return set_bad_null_error(field, ER_BAD_NULL_ERROR);
} }
......
...@@ -2379,6 +2379,24 @@ bool Item_field::update_table_bitmaps_processor(uchar *arg) ...@@ -2379,6 +2379,24 @@ bool Item_field::update_table_bitmaps_processor(uchar *arg)
return FALSE; return FALSE;
} }
static inline void set_field_to_new_field(Field **field, Field **new_field)
{
if (*field)
{
Field *newf= new_field[(*field)->field_index];
if ((*field)->ptr == newf->ptr)
*field= newf;
}
}
bool Item_field::switch_to_nullable_fields_processor(uchar *arg)
{
Field **new_fields= (Field **)arg;
set_field_to_new_field(&field, new_fields);
set_field_to_new_field(&result_field, new_fields);
return 0;
}
const char *Item_ident::full_name() const const char *Item_ident::full_name() const
{ {
char *tmp; char *tmp;
...@@ -8191,9 +8209,8 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions) ...@@ -8191,9 +8209,8 @@ int Item_default_value::save_in_field(Field *field_arg, bool no_conversions)
} }
field_arg->set_default(); field_arg->set_default();
return return
!field_arg->is_null_in_record(field_arg->table->s->default_values) && !field_arg->is_null() &&
field_arg->validate_value_in_record_with_warn(thd, field_arg->validate_value_in_record_with_warn(thd, table->record[0]) &&
field_arg->table->s->default_values) &&
thd->is_error() ? -1 : 0; thd->is_error() ? -1 : 0;
} }
return Item_field::save_in_field(field_arg, no_conversions); return Item_field::save_in_field(field_arg, no_conversions);
...@@ -8387,6 +8404,7 @@ bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it) ...@@ -8387,6 +8404,7 @@ bool Item_trigger_field::set_value(THD *thd, sp_rcontext * /*ctx*/, Item **it)
int err_code= item->save_in_field(field, 0); int err_code= item->save_in_field(field, 0);
field->table->copy_blobs= copy_blobs_saved; field->table->copy_blobs= copy_blobs_saved;
field->set_explicit_default(item);
return err_code < 0; return err_code < 0;
} }
......
...@@ -1605,6 +1605,8 @@ class Item: public Value_source, ...@@ -1605,6 +1605,8 @@ class Item: public Value_source,
virtual bool check_inner_refs_processor(uchar *arg) { return FALSE; } virtual bool check_inner_refs_processor(uchar *arg) { return FALSE; }
virtual bool switch_to_nullable_fields_processor(uchar *arg) { return FALSE; }
/* /*
For SP local variable returns pointer to Item representing its For SP local variable returns pointer to Item representing its
current value and pointer to current Item otherwise. current value and pointer to current Item otherwise.
...@@ -2464,6 +2466,7 @@ class Item_field :public Item_ident ...@@ -2464,6 +2466,7 @@ class Item_field :public Item_ident
bool vcol_in_partition_func_processor(uchar *bool_arg); bool vcol_in_partition_func_processor(uchar *bool_arg);
bool enumerate_field_refs_processor(uchar *arg); bool enumerate_field_refs_processor(uchar *arg);
bool update_table_bitmaps_processor(uchar *arg); bool update_table_bitmaps_processor(uchar *arg);
bool switch_to_nullable_fields_processor(uchar *arg);
void cleanup(); void cleanup();
Item_equal *get_item_equal() { return item_equal; } Item_equal *get_item_equal() { return item_equal; }
void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; } void set_item_equal(Item_equal *item_eq) { item_equal= item_eq; }
......
...@@ -8818,7 +8818,61 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values, ...@@ -8818,7 +8818,61 @@ fill_record(THD *thd, TABLE *table_arg, List<Item> &fields, List<Item> &values,
} }
/* /**
Prepare Item_field's for fill_record_n_invoke_before_triggers()
This means redirecting from table->field to
table->field_to_fill(), if needed.
*/
void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *table)
{
Field** field= table->field_to_fill();
if (field != table->field)
{
List_iterator_fast<Item> it(items);
Item *item;
while ((item= it++))
item->walk(&Item::switch_to_nullable_fields_processor, 1, (uchar*)field);
table->triggers->reset_extra_null_bitmap();
}
}
/**
Test NOT NULL constraint after BEFORE triggers
*/
static bool not_null_fields_have_null_values(TABLE *table)
{
Field **orig_field= table->field;
Field **filled_field= table->field_to_fill();
if (filled_field != orig_field)
{
THD *thd=table->in_use;
for (uint i=0; i < table->s->fields; i++)
{
Field *of= orig_field[i];
Field *ff= filled_field[i];
if (ff != of)
{
// copy after-update flags to of, copy before-update flags to ff
swap_variables(uint32, of->flags, ff->flags);
if (ff->is_real_null())
{
ff->set_notnull(); // for next row WHERE condition in UPDATE
if (convert_null_to_field_value_or_error(of) || thd->is_error())
return true;
}
}
}
}
return false;
}
/**
Fill fields in list with values from the list of items and invoke Fill fields in list with values from the list of items and invoke
before triggers. before triggers.
...@@ -8846,9 +8900,13 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields, ...@@ -8846,9 +8900,13 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, List<Item> &fields,
{ {
bool result; bool result;
Table_triggers_list *triggers= table->triggers; Table_triggers_list *triggers= table->triggers;
result= (fill_record(thd, table, fields, values, ignore_errors) ||
(triggers && triggers->process_triggers(thd, event, result= fill_record(thd, table, fields, values, ignore_errors);
TRG_ACTION_BEFORE, TRUE)));
if (!result && triggers)
result= triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE) ||
not_null_fields_have_null_values(table);
/* /*
Re-calculate virtual fields to cater for cases when base columns are Re-calculate virtual fields to cater for cases when base columns are
updated by the triggers. updated by the triggers.
...@@ -8994,9 +9052,12 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr, ...@@ -8994,9 +9052,12 @@ fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, Field **ptr,
{ {
bool result; bool result;
Table_triggers_list *triggers= table->triggers; Table_triggers_list *triggers= table->triggers;
result= (fill_record(thd, table, ptr, values, ignore_errors, FALSE) ||
(triggers && triggers->process_triggers(thd, event, result= fill_record(thd, table, ptr, values, ignore_errors, FALSE);
TRG_ACTION_BEFORE, TRUE)));
if (!result && triggers && *ptr)
result= triggers->process_triggers(thd, event, TRG_ACTION_BEFORE, TRUE) ||
not_null_fields_have_null_values(table);
/* /*
Re-calculate virtual fields to cater for cases when base columns are Re-calculate virtual fields to cater for cases when base columns are
updated by the triggers. updated by the triggers.
......
...@@ -150,6 +150,7 @@ bool find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl, ...@@ -150,6 +150,7 @@ bool find_and_use_temporary_table(THD *thd, const TABLE_LIST *tl,
TABLE *find_temporary_table(THD *thd, const char *table_key, TABLE *find_temporary_table(THD *thd, const char *table_key,
uint table_key_length); uint table_key_length);
void close_thread_tables(THD *thd); void close_thread_tables(THD *thd);
void switch_to_nullable_trigger_fields(List<Item> &items, TABLE *);
bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table, bool fill_record_n_invoke_before_triggers(THD *thd, TABLE *table,
List<Item> &fields, List<Item> &fields,
List<Item> &values, List<Item> &values,
......
...@@ -761,6 +761,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -761,6 +761,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
*/ */
table_list->next_local= 0; table_list->next_local= 0;
context->resolve_in_table_list_only(table_list); context->resolve_in_table_list_only(table_list);
switch_to_nullable_trigger_fields(*values, table);
while ((values= its++)) while ((values= its++))
{ {
...@@ -772,6 +773,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -772,6 +773,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
} }
if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0)) if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, 0))
goto abort; goto abort;
switch_to_nullable_trigger_fields(*values, table);
} }
its.rewind (); its.rewind ();
...@@ -870,6 +872,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -870,6 +872,9 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
error= 1; error= 1;
table->reset_default_fields(); table->reset_default_fields();
switch_to_nullable_trigger_fields(fields, table);
switch_to_nullable_trigger_fields(update_fields, table);
switch_to_nullable_trigger_fields(update_values, table);
if (fields.elements || !value_count) if (fields.elements || !value_count)
{ {
...@@ -943,8 +948,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, ...@@ -943,8 +948,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
share->default_values[share->null_bytes - 1]; share->default_values[share->null_bytes - 1];
} }
} }
if (fill_record_n_invoke_before_triggers(thd, table, table->field, *values, 0, if (fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(),
TRG_EVENT_INSERT)) *values, 0, TRG_EVENT_INSERT))
{ {
if (values_list.elements != 1 && ! thd->is_error()) if (values_list.elements != 1 && ! thd->is_error())
{ {
...@@ -3704,8 +3709,8 @@ void select_insert::store_values(List<Item> &values) ...@@ -3704,8 +3709,8 @@ void select_insert::store_values(List<Item> &values)
fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1, fill_record_n_invoke_before_triggers(thd, table, *fields, values, 1,
TRG_EVENT_INSERT); TRG_EVENT_INSERT);
else else
fill_record_n_invoke_before_triggers(thd, table, table->field, values, 1, fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(),
TRG_EVENT_INSERT); values, 1, TRG_EVENT_INSERT);
} }
bool select_insert::prepare_eof() bool select_insert::prepare_eof()
......
...@@ -295,6 +295,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list, ...@@ -295,6 +295,9 @@ int mysql_load(THD *thd,sql_exchange *ex,TABLE_LIST *table_list,
if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0)) if (setup_fields(thd, 0, set_values, MARK_COLUMNS_READ, 0, 0))
DBUG_RETURN(TRUE); DBUG_RETURN(TRUE);
} }
switch_to_nullable_trigger_fields(fields_vars, table);
switch_to_nullable_trigger_fields(set_fields, table);
switch_to_nullable_trigger_fields(set_values, table);
table->prepare_triggers_for_insert_stmt_or_event(); table->prepare_triggers_for_insert_stmt_or_event();
table->mark_columns_needed_for_insert(); table->mark_columns_needed_for_insert();
......
...@@ -1073,29 +1073,71 @@ Table_triggers_list::~Table_triggers_list() ...@@ -1073,29 +1073,71 @@ Table_triggers_list::~Table_triggers_list()
@retval @retval
True error True error
*/ */
bool Table_triggers_list::prepare_record1_accessors(TABLE *table) bool Table_triggers_list::prepare_record_accessors(TABLE *table)
{ {
Field **fld, **old_fld; Field **fld, **trg_fld;
if (!(record1_field= (Field **)alloc_root(&table->mem_root, if ((bodies[TRG_EVENT_INSERT][TRG_ACTION_BEFORE] ||
(table->s->fields + 1) * bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE])
sizeof(Field*)))) && (table->s->stored_fields != table->s->null_fields))
return 1;
for (fld= table->field, old_fld= record1_field; *fld; fld++, old_fld++)
{ {
/* int null_bytes= (table->s->stored_fields - table->s->null_fields + 7)/8;
QQ: it is supposed that it is ok to use this function for field if (!(extra_null_bitmap= (uchar*)alloc_root(&table->mem_root, null_bytes)))
cloning...
*/
if (!(*old_fld= (*fld)->make_new_field(&table->mem_root, table,
table == (*fld)->table)))
return 1; return 1;
(*old_fld)->move_field_offset((my_ptrdiff_t)(table->record[1] - if (!(record0_field= (Field **)alloc_root(&table->mem_root,
table->record[0])); (table->s->fields + 1) *
sizeof(Field*))))
return 1;
uchar *null_ptr= extra_null_bitmap;
uchar null_bit= 1;
for (fld= table->field, trg_fld= record0_field; *fld; fld++, trg_fld++)
{
if (!(*fld)->null_ptr && !(*fld)->vcol_info)
{
Field *f;
if (!(f= *trg_fld= (*fld)->make_new_field(&table->mem_root, table,
table == (*fld)->table)))
return 1;
f->null_ptr= null_ptr;
f->null_bit= null_bit;
if (null_bit == 128)
null_ptr++, null_bit= 1;
else
null_bit*= 2;
}
else
*trg_fld= *fld;
}
*trg_fld= 0;
DBUG_ASSERT(null_ptr <= extra_null_bitmap + null_bytes);
bzero(extra_null_bitmap, null_bytes);
} }
*old_fld= 0; else
record0_field= table->field;
if (bodies[TRG_EVENT_UPDATE][TRG_ACTION_BEFORE] ||
bodies[TRG_EVENT_UPDATE][TRG_ACTION_AFTER] ||
bodies[TRG_EVENT_DELETE][TRG_ACTION_BEFORE] ||
bodies[TRG_EVENT_DELETE][TRG_ACTION_AFTER])
{
if (!(record1_field= (Field **)alloc_root(&table->mem_root,
(table->s->fields + 1) *
sizeof(Field*))))
return 1;
for (fld= table->field, trg_fld= record1_field; *fld; fld++, trg_fld++)
{
if (!(*trg_fld= (*fld)->make_new_field(&table->mem_root, table,
table == (*fld)->table)))
return 1;
(*trg_fld)->move_field_offset((my_ptrdiff_t)(table->record[1] -
table->record[0]));
}
*trg_fld= 0;
}
return 0; return 0;
} }
...@@ -1320,13 +1362,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -1320,13 +1362,6 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
table->triggers= triggers; table->triggers= triggers;
status_var_increment(thd->status_var.feature_trigger); status_var_increment(thd->status_var.feature_trigger);
/*
TODO: This could be avoided if there is no triggers
for UPDATE and DELETE.
*/
if (!names_only && triggers->prepare_record1_accessors(table))
DBUG_RETURN(1);
List_iterator_fast<ulonglong> itm(triggers->definition_modes_list); List_iterator_fast<ulonglong> itm(triggers->definition_modes_list);
List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list); List_iterator_fast<LEX_STRING> it_definer(triggers->definers_list);
List_iterator_fast<LEX_STRING> it_client_cs_name(triggers->client_cs_names); List_iterator_fast<LEX_STRING> it_client_cs_name(triggers->client_cs_names);
...@@ -1539,6 +1574,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db, ...@@ -1539,6 +1574,9 @@ bool Table_triggers_list::check_n_load(THD *thd, const char *db,
thd->spcont= save_spcont; thd->spcont= save_spcont;
thd->variables.sql_mode= save_sql_mode; thd->variables.sql_mode= save_sql_mode;
if (!names_only && triggers->prepare_record_accessors(table))
DBUG_RETURN(1);
DBUG_RETURN(0); DBUG_RETURN(0);
err_with_lex_cleanup: err_with_lex_cleanup:
...@@ -2107,13 +2145,13 @@ bool Table_triggers_list::process_triggers(THD *thd, ...@@ -2107,13 +2145,13 @@ bool Table_triggers_list::process_triggers(THD *thd,
if (old_row_is_record1) if (old_row_is_record1)
{ {
old_field= record1_field; old_field= record1_field;
new_field= trigger_table->field; new_field= record0_field;
} }
else else
{ {
DBUG_ASSERT(event == TRG_EVENT_DELETE); DBUG_ASSERT(event == TRG_EVENT_DELETE);
new_field= record1_field; new_field= record1_field;
old_field= trigger_table->field; old_field= record0_field;
} }
/* /*
This trigger must have been processed by the pre-locking This trigger must have been processed by the pre-locking
......
...@@ -67,6 +67,13 @@ class Table_triggers_list: public Sql_alloc ...@@ -67,6 +67,13 @@ class Table_triggers_list: public Sql_alloc
grouped by event and action_time. grouped by event and action_time.
*/ */
Item_trigger_field *trigger_fields[TRG_EVENT_MAX][TRG_ACTION_MAX]; Item_trigger_field *trigger_fields[TRG_EVENT_MAX][TRG_ACTION_MAX];
/**
Copy of TABLE::Field array which all fields made nullable
(using extra_null_bitmap, if needed). Used for NEW values in
BEFORE INSERT/UPDATE triggers.
*/
Field **record0_field;
uchar *extra_null_bitmap;
/** /**
Copy of TABLE::Field array with field pointers set to TABLE::record[1] Copy of TABLE::Field array with field pointers set to TABLE::record[1]
buffer instead of TABLE::record[0] (used for OLD values in on UPDATE buffer instead of TABLE::record[0] (used for OLD values in on UPDATE
...@@ -143,7 +150,8 @@ class Table_triggers_list: public Sql_alloc ...@@ -143,7 +150,8 @@ class Table_triggers_list: public Sql_alloc
/* End of character ser context. */ /* End of character ser context. */
Table_triggers_list(TABLE *table_arg) Table_triggers_list(TABLE *table_arg)
:record1_field(0), trigger_table(table_arg), :record0_field(0), extra_null_bitmap(0), record1_field(0),
trigger_table(table_arg),
m_has_unparseable_trigger(false) m_has_unparseable_trigger(false)
{ {
bzero((char *)bodies, sizeof(bodies)); bzero((char *)bodies, sizeof(bodies));
...@@ -211,8 +219,16 @@ class Table_triggers_list: public Sql_alloc ...@@ -211,8 +219,16 @@ class Table_triggers_list: public Sql_alloc
trg_event_type event_type, trg_event_type event_type,
trg_action_time_type action_time); trg_action_time_type action_time);
Field **nullable_fields() { return record0_field; }
void reset_extra_null_bitmap()
{
int null_bytes= (trigger_table->s->stored_fields -
trigger_table->s->null_fields + 7)/8;
bzero(extra_null_bitmap, null_bytes);
}
private: private:
bool prepare_record1_accessors(TABLE *table); bool prepare_record_accessors(TABLE *table);
LEX_STRING* change_table_name_in_trignames(const char *old_db_name, LEX_STRING* change_table_name_in_trignames(const char *old_db_name,
const char *new_db_name, const char *new_db_name,
LEX_STRING *new_table_name, LEX_STRING *new_table_name,
...@@ -234,6 +250,13 @@ class Table_triggers_list: public Sql_alloc ...@@ -234,6 +250,13 @@ class Table_triggers_list: public Sql_alloc
} }
}; };
inline Field **TABLE::field_to_fill()
{
return triggers && triggers->nullable_fields() ? triggers->nullable_fields()
: field;
}
extern const LEX_STRING trg_action_time_type_names[]; extern const LEX_STRING trg_action_time_type_names[];
extern const LEX_STRING trg_event_type_names[]; extern const LEX_STRING trg_event_type_names[];
......
...@@ -455,6 +455,8 @@ int mysql_update(THD *thd, ...@@ -455,6 +455,8 @@ int mysql_update(THD *thd,
} }
init_ftfuncs(thd, select_lex, 1); init_ftfuncs(thd, select_lex, 1);
switch_to_nullable_trigger_fields(fields, table);
switch_to_nullable_trigger_fields(values, table);
table->mark_columns_needed_for_update(); table->mark_columns_needed_for_update();
table->update_const_key_parts(conds); table->update_const_key_parts(conds);
...@@ -1766,7 +1768,6 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1766,7 +1768,6 @@ int multi_update::prepare(List<Item> &not_used_values,
} }
} }
table_count= update.elements; table_count= update.elements;
update_tables= update.first; update_tables= update.first;
...@@ -1802,7 +1803,15 @@ int multi_update::prepare(List<Item> &not_used_values, ...@@ -1802,7 +1803,15 @@ int multi_update::prepare(List<Item> &not_used_values,
/* Allocate copy fields */ /* Allocate copy fields */
max_fields=0; max_fields=0;
for (i=0 ; i < table_count ; i++) for (i=0 ; i < table_count ; i++)
{
set_if_bigger(max_fields, fields_for_table[i]->elements + leaf_table_count); set_if_bigger(max_fields, fields_for_table[i]->elements + leaf_table_count);
if (fields_for_table[i]->elements)
{
TABLE *table= ((Item_field*)(fields_for_table[i]->head()))->field->table;
switch_to_nullable_trigger_fields(*fields_for_table[i], table);
switch_to_nullable_trigger_fields(*values_for_table[i], table);
}
}
copy_field= new Copy_field[max_fields]; copy_field= new Copy_field[max_fields];
DBUG_RETURN(thd->is_fatal_error != 0); DBUG_RETURN(thd->is_fatal_error != 0);
} }
......
...@@ -1386,6 +1386,7 @@ struct TABLE ...@@ -1386,6 +1386,7 @@ struct TABLE
bool prepare_triggers_for_delete_stmt_or_event(); bool prepare_triggers_for_delete_stmt_or_event();
bool prepare_triggers_for_update_stmt_or_event(); bool prepare_triggers_for_update_stmt_or_event();
inline Field **field_to_fill();
bool validate_default_values_of_unset_fields(THD *thd) const; bool validate_default_values_of_unset_fields(THD *thd) const;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment