Skip to content

Commit ea1b250

Browse files
committed
New simpler bugfix for UPDATE and virtual BLOBs
When updating a table with virtual BLOB columns, the following might happen: - an old record is read from the table, it has no virtual blob values - update_virtual_fields() is run, vcol blob gets its value into the record. But only a pointer to the value is in the table->record[0], the value is in Field_blob::value String (but it doesn't have to be! it can be in the record, if the column is just a copy of another columns: ... b VARCHAR, c BLOB AS (b) ...) - store_record(table,record[1]), old record now is in record[1] - fill_record() prepares new values in record[0], vcol blob is updated, new value replaces the old one in the Field_blob::value - now both record[1] and record[0] have a pointer that points to the *new* vcol blob value. Or record[1] has a pointer to nowhere if Field_blob::value had to realloc. To fix this I have introduced a new String object 'read_value' in Field_blob. When updating virtual columns when a row has been read, the allocated value is stored in 'read_value' instead of 'value'. The allocated blobs for the new row is stored in 'value' as before. I also made, as a safety precaution, the insert delayed handling of blobs more general by using value to store strings instead of the record. This ensures that virtual functions on delayed insert should work in as in the case of normal insert. Triggers are now properly updating the read, write and vcol maps for used fields. This means that we don't need VCOL_UPDATE_FOR_READ_WRITE anymore and there is no need for any other special handling of triggers in update_virtual_fields(). To be able to test how many times virtual fields are invoked, I also relaxed rules that one can use local (@) variables in DEFAULT and non persistent virtual field expressions.
1 parent 7454087 commit ea1b250

17 files changed

+643
-52
lines changed

mysql-test/r/default.result

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,10 @@ a b
480480
2 2
481481
3 4
482482
drop table t1;
483+
CREATE OR REPLACE TABLE t1 (a INT DEFAULT @v);
484+
drop table t1;
485+
CREATE TABLE t1 (a INT DEFAULT @v:=1);
486+
drop table t1;
483487
#
484488
# Error handling
485489
#
@@ -516,10 +520,6 @@ CREATE TABLE t1 (a INT DEFAULT(?));
516520
Got one of the listed errors
517521
CREATE TABLE t1 (a INT DEFAULT (b), b INT DEFAULT(a));
518522
ERROR 01000: Expression for field `a` is refering to uninitialized field `b`
519-
CREATE TABLE t1 (a INT DEFAULT @v);
520-
ERROR HY000: Function or expression '@v' cannot be used in the DEFAULT clause of `a`
521-
CREATE TABLE t1 (a INT DEFAULT @v:=1);
522-
ERROR HY000: Function or expression '@v' cannot be used in the DEFAULT clause of `a`
523523
CREATE TABLE t1 (a INT DEFAULT(NAME_CONST('xxx', 'yyy'));
524524
ERROR HY000: Function or expression 'name_const()' cannot be used in the DEFAULT clause of `a`
525525
CREATE TABLE t1 (a INT DEFAULT COUNT(*));

mysql-test/suite/vcol/inc/vcol_trigger_sp.inc

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,135 @@ DROP TRIGGER t1_ins_aft;
149149
DROP TRIGGER t1_del_bef;
150150
DROP TABLE t1,t2;
151151

152+
--echo #
153+
--echo # Examine the number of times triggers are recalculated for updates
154+
--echo #
155+
156+
CREATE TABLE t1 (
157+
a INTEGER UNSIGNED NULL DEFAULT NULL,
158+
b CHAR(10) NULL DEFAULT NULL,
159+
c blob NULL DEFAULT NULL,
160+
blob_a blob GENERATED ALWAYS AS (last_value(@a:=@a+1,a)) VIRTUAL,
161+
blob_b blob GENERATED ALWAYS AS (last_value(@b:=@b+1,b)) VIRTUAL,
162+
blob_c blob GENERATED ALWAYS AS (last_value(@c:=@c+1,c)) VIRTUAL
163+
);
164+
165+
DELIMITER |;
166+
CREATE TRIGGER t1_ins
167+
BEFORE INSERT
168+
ON t1
169+
FOR EACH ROW
170+
BEGIN
171+
IF NEW.b IS NULL THEN
172+
SET NEW.b="generated before insert";
173+
END IF;
174+
END |
175+
176+
CREATE TRIGGER t1_update
177+
BEFORE UPDATE
178+
ON t1
179+
FOR EACH ROW
180+
BEGIN
181+
IF NEW.b IS NULL or NEW.c IS NULL THEN
182+
SET NEW.b="generated before update";
183+
SET NEW.c="generated before update";
184+
END IF;
185+
END |
186+
187+
DELIMITER ;|
188+
189+
--echo # Inserts
190+
set @a=0,@b=0,@c=0;
191+
192+
insert into t1 (a) values(1);
193+
insert into t1 (a,b) values(2, "*2*");
194+
insert into t1 (a,b,c) values(3, "*3*", "**3**");
195+
insert into t1 (a,c) values(4, "**4**");
196+
select * from t1;
197+
select @a,@b,@c;
198+
select * from t1;
199+
select @a,@b,@c;
200+
select a,b,c from t1;
201+
select @a,@b,@c;
202+
select a,b,c,blob_a from t1;
203+
select @a,@b,@c;
204+
205+
--echo # updates
206+
set @a=0,@b=0,@c=0;
207+
208+
update t1 set a=a+100 where a=1;
209+
update t1 set a=a+100, b="*102*" where a=2;
210+
update t1 set a=a+100, b=NULL where a=3;
211+
update t1 set a=a+100, b="invisible", c=NULL where a=4;
212+
select @a,@b,@c;
213+
select * from t1;
214+
215+
drop trigger t1_ins;
216+
drop trigger t1_update;
217+
drop table t1;
218+
219+
--echo #
220+
--echo # Same test, but with virtual keys
221+
--echo #
222+
223+
CREATE TABLE t1 (
224+
a INTEGER UNSIGNED NULL DEFAULT NULL,
225+
b CHAR(10) NULL DEFAULT NULL,
226+
c blob NULL DEFAULT NULL,
227+
blob_a blob GENERATED ALWAYS AS (a) VIRTUAL,
228+
blob_b blob GENERATED ALWAYS AS (b) VIRTUAL,
229+
blob_c blob GENERATED ALWAYS AS (c) VIRTUAL,
230+
key (a),
231+
key (blob_a(10)),
232+
key (blob_b(10)),
233+
key (blob_c(10))
234+
);
235+
236+
DELIMITER |;
237+
CREATE TRIGGER t1_ins
238+
BEFORE INSERT
239+
ON t1
240+
FOR EACH ROW
241+
BEGIN
242+
IF NEW.b IS NULL THEN
243+
SET NEW.b="generated before insert";
244+
END IF;
245+
END |
246+
247+
CREATE TRIGGER t1_update
248+
BEFORE UPDATE
249+
ON t1
250+
FOR EACH ROW
251+
BEGIN
252+
IF NEW.b IS NULL or NEW.c IS NULL THEN
253+
SET NEW.b="generated before update";
254+
SET NEW.c="generated before update";
255+
END IF;
256+
END |
257+
258+
DELIMITER ;|
259+
260+
--echo # Inserts
261+
insert into t1 (a) values(1);
262+
insert into t1 (a,b) values(2, "*2*");
263+
insert into t1 (a,b,c) values(3, "*3*", "**3**");
264+
insert into t1 (a,c) values(4, "**4**");
265+
select * from t1;
266+
select @a,@b,@c;
267+
select * from t1;
268+
select @a,@b,@c;
269+
select a,b,c from t1;
270+
select @a,@b,@c;
271+
select a,b,c,blob_a from t1;
272+
select @a,@b,@c;
273+
274+
--echo # updates
275+
update t1 set a=a+100 where a=1;
276+
update t1 set a=a+100, b="*102*" where a=2;
277+
update t1 set a=a+100, b=NULL where a=3;
278+
update t1 set a=a+100, b="invisible", c=NULL where a=4;
279+
select * from t1;
280+
281+
drop trigger t1_ins;
282+
drop trigger t1_update;
283+
drop table t1;

mysql-test/suite/vcol/r/not_supported.result

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ set time_zone='+10:00';
44
set div_precision_increment=20;
55
create table t1 (a int, b int, v decimal(20,19) as (a/3));
66
create table t2 (a int, b int, v int as (a+@a));
7-
ERROR HY000: Function or expression '@a' cannot be used in the GENERATED ALWAYS AS clause of `v`
7+
drop table t2;
88
create table t2 (a int, b int, v int as (a+@a) PERSISTENT);
99
ERROR HY000: Function or expression '@a' cannot be used in the GENERATED ALWAYS AS clause of `v`
1010
create table t3_ok (a int, b int, v int as (a+@@error_count));
1111
create table t3 (a int, b int, v int as (a+@@error_count) PERSISTENT);
1212
ERROR HY000: Function or expression '@@error_count' cannot be used in the GENERATED ALWAYS AS clause of `v`
1313
create table t4 (a int, b int, v int as (@a:=a));
14-
ERROR HY000: Function or expression '@a' cannot be used in the GENERATED ALWAYS AS clause of `v`
14+
drop table t4;
1515
create table t4 (a int, b int, v int as (@a:=a) PERSISTENT);
1616
ERROR HY000: Function or expression '@a' cannot be used in the GENERATED ALWAYS AS clause of `v`
1717
create table t8 (a int, b int, v varchar(100) as (from_unixtime(a)));

mysql-test/suite/vcol/r/vcol_trigger_sp_innodb.result

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,182 @@ c
125125
DROP TRIGGER t1_ins_aft;
126126
DROP TRIGGER t1_del_bef;
127127
DROP TABLE t1,t2;
128+
#
129+
# Examine the number of times triggers are recalculated for updates
130+
#
131+
CREATE TABLE t1 (
132+
a INTEGER UNSIGNED NULL DEFAULT NULL,
133+
b CHAR(10) NULL DEFAULT NULL,
134+
c blob NULL DEFAULT NULL,
135+
blob_a blob GENERATED ALWAYS AS (last_value(@a:=@a+1,a)) VIRTUAL,
136+
blob_b blob GENERATED ALWAYS AS (last_value(@b:=@b+1,b)) VIRTUAL,
137+
blob_c blob GENERATED ALWAYS AS (last_value(@c:=@c+1,c)) VIRTUAL
138+
);
139+
CREATE TRIGGER t1_ins
140+
BEFORE INSERT
141+
ON t1
142+
FOR EACH ROW
143+
BEGIN
144+
IF NEW.b IS NULL THEN
145+
SET NEW.b="generated before insert";
146+
END IF;
147+
END |
148+
CREATE TRIGGER t1_update
149+
BEFORE UPDATE
150+
ON t1
151+
FOR EACH ROW
152+
BEGIN
153+
IF NEW.b IS NULL or NEW.c IS NULL THEN
154+
SET NEW.b="generated before update";
155+
SET NEW.c="generated before update";
156+
END IF;
157+
END |
158+
# Inserts
159+
set @a=0,@b=0,@c=0;
160+
insert into t1 (a) values(1);
161+
insert into t1 (a,b) values(2, "*2*");
162+
insert into t1 (a,b,c) values(3, "*3*", "**3**");
163+
insert into t1 (a,c) values(4, "**4**");
164+
select * from t1;
165+
a b c blob_a blob_b blob_c
166+
1 generated NULL 1 generated NULL
167+
2 *2* NULL 2 *2* NULL
168+
3 *3* **3** 3 *3* **3**
169+
4 generated **4** 4 generated **4**
170+
select @a,@b,@c;
171+
@a @b @c
172+
4 4 4
173+
select * from t1;
174+
a b c blob_a blob_b blob_c
175+
1 generated NULL 1 generated NULL
176+
2 *2* NULL 2 *2* NULL
177+
3 *3* **3** 3 *3* **3**
178+
4 generated **4** 4 generated **4**
179+
select @a,@b,@c;
180+
@a @b @c
181+
8 8 8
182+
select a,b,c from t1;
183+
a b c
184+
1 generated NULL
185+
2 *2* NULL
186+
3 *3* **3**
187+
4 generated **4**
188+
select @a,@b,@c;
189+
@a @b @c
190+
8 8 8
191+
select a,b,c,blob_a from t1;
192+
a b c blob_a
193+
1 generated NULL 1
194+
2 *2* NULL 2
195+
3 *3* **3** 3
196+
4 generated **4** 4
197+
select @a,@b,@c;
198+
@a @b @c
199+
12 8 8
200+
# updates
201+
set @a=0,@b=0,@c=0;
202+
update t1 set a=a+100 where a=1;
203+
update t1 set a=a+100, b="*102*" where a=2;
204+
update t1 set a=a+100, b=NULL where a=3;
205+
update t1 set a=a+100, b="invisible", c=NULL where a=4;
206+
select @a,@b,@c;
207+
@a @b @c
208+
0 0 0
209+
select * from t1;
210+
a b c blob_a blob_b blob_c
211+
101 generated generated before update 101 generated generated before update
212+
102 generated generated before update 102 generated generated before update
213+
103 generated generated before update 103 generated generated before update
214+
104 generated generated before update 104 generated generated before update
215+
drop trigger t1_ins;
216+
drop trigger t1_update;
217+
drop table t1;
218+
#
219+
# Same test, but with virtual keys
220+
#
221+
CREATE TABLE t1 (
222+
a INTEGER UNSIGNED NULL DEFAULT NULL,
223+
b CHAR(10) NULL DEFAULT NULL,
224+
c blob NULL DEFAULT NULL,
225+
blob_a blob GENERATED ALWAYS AS (a) VIRTUAL,
226+
blob_b blob GENERATED ALWAYS AS (b) VIRTUAL,
227+
blob_c blob GENERATED ALWAYS AS (c) VIRTUAL,
228+
key (a),
229+
key (blob_a(10)),
230+
key (blob_b(10)),
231+
key (blob_c(10))
232+
);
233+
CREATE TRIGGER t1_ins
234+
BEFORE INSERT
235+
ON t1
236+
FOR EACH ROW
237+
BEGIN
238+
IF NEW.b IS NULL THEN
239+
SET NEW.b="generated before insert";
240+
END IF;
241+
END |
242+
CREATE TRIGGER t1_update
243+
BEFORE UPDATE
244+
ON t1
245+
FOR EACH ROW
246+
BEGIN
247+
IF NEW.b IS NULL or NEW.c IS NULL THEN
248+
SET NEW.b="generated before update";
249+
SET NEW.c="generated before update";
250+
END IF;
251+
END |
252+
# Inserts
253+
insert into t1 (a) values(1);
254+
insert into t1 (a,b) values(2, "*2*");
255+
insert into t1 (a,b,c) values(3, "*3*", "**3**");
256+
insert into t1 (a,c) values(4, "**4**");
257+
select * from t1;
258+
a b c blob_a blob_b blob_c
259+
1 generated NULL 1 generated NULL
260+
2 *2* NULL 2 *2* NULL
261+
3 *3* **3** 3 *3* **3**
262+
4 generated **4** 4 generated **4**
263+
select @a,@b,@c;
264+
@a @b @c
265+
4 4 4
266+
select * from t1;
267+
a b c blob_a blob_b blob_c
268+
1 generated NULL 1 generated NULL
269+
2 *2* NULL 2 *2* NULL
270+
3 *3* **3** 3 *3* **3**
271+
4 generated **4** 4 generated **4**
272+
select @a,@b,@c;
273+
@a @b @c
274+
4 4 4
275+
select a,b,c from t1;
276+
a b c
277+
1 generated NULL
278+
2 *2* NULL
279+
3 *3* **3**
280+
4 generated **4**
281+
select @a,@b,@c;
282+
@a @b @c
283+
4 4 4
284+
select a,b,c,blob_a from t1;
285+
a b c blob_a
286+
1 generated NULL 1
287+
2 *2* NULL 2
288+
3 *3* **3** 3
289+
4 generated **4** 4
290+
select @a,@b,@c;
291+
@a @b @c
292+
4 4 4
293+
# updates
294+
update t1 set a=a+100 where a=1;
295+
update t1 set a=a+100, b="*102*" where a=2;
296+
update t1 set a=a+100, b=NULL where a=3;
297+
update t1 set a=a+100, b="invisible", c=NULL where a=4;
298+
select * from t1;
299+
a b c blob_a blob_b blob_c
300+
101 generated generated before update 101 generated generated before update
301+
102 generated generated before update 102 generated generated before update
302+
103 generated generated before update 103 generated generated before update
303+
104 generated generated before update 104 generated generated before update
304+
drop trigger t1_ins;
305+
drop trigger t1_update;
306+
drop table t1;

0 commit comments

Comments
 (0)