1
0
mirror of https://github.com/MariaDB/server.git synced 2025-07-30 16:24:05 +03:00

MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations

This commit is contained in:
Alexander Barkov
2017-03-03 15:02:08 +04:00
parent 400de20279
commit 1b8a0c879d
24 changed files with 2106 additions and 58 deletions

View File

@ -177,3 +177,154 @@ master-bin.000002 # Query # # use `test`; DROP PROCEDURE p1
#
# End of MDEV-10914 ROW data type for stored routine variables
#
#
# MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
#
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE;
BEGIN
rec.a:=100;
rec.b:=200;
INSERT INTO t1 VALUES (rec.a,rec.b);
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.a:=NULL;
INSERT INTO t1 VALUES (11, rec=ROW(100,200));
INSERT INTO t1 VALUES (11, rec=ROW(100,201));
INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.b:=NULL;
INSERT INTO t1 VALUES (12, rec=ROW(100,200));
INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
END;
$$
CALL p1();
SELECT * FROM t1;
a b
100 200
10 1
10 1
10 20
10 21
11 NULL
11 0
11 NULL
11 0
12 NULL
12 NULL
DROP TABLE t1;
DROP PROCEDURE p1;
include/show_binlog_events.inc
Log_name Pos Event_type Server_id End_log_pos Info
master-bin.000002 # Binlog_checkpoint # # master-bin.000002
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; CREATE TABLE t1 (a INT)
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (10),(10)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; CREATE TABLE t2 (a INT)
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
AS
a INT:= 1;
rec ROW(a INT);
BEGIN
rec.a:= 1;
INSERT INTO t2 SELECT 1 FROM t1 LIMIT a;
INSERT INTO t2 SELECT 2 FROM t1 LIMIT rec.a;
END
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t2 SELECT 1 FROM t1 LIMIT 1
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t2 SELECT 2 FROM t1 LIMIT 1
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; DROP TABLE "t1","t2" /* generated by server */
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; DROP PROCEDURE p1
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; CREATE TABLE t1 (a INT, b INT)
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
AS
rec t1%ROWTYPE;
BEGIN
rec.a:=100;
rec.b:=200;
INSERT INTO t1 VALUES (rec.a,rec.b);
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.a:=NULL;
INSERT INTO t1 VALUES (11, rec=ROW(100,200));
INSERT INTO t1 VALUES (11, rec=ROW(100,201));
INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.b:=NULL;
INSERT INTO t1 VALUES (12, rec=ROW(100,200));
INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
END
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('rec.a',100), NAME_CONST('rec.b',200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,201))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,200)=ROW(NULL,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,201)=ROW(NULL,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE ROW(NULL,200)=ROW(100,200)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,200)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(NULL,NULL)=ROW(100,200))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(100,200)=ROW(NULL,NULL))
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE ROW(NULL,NULL)=ROW(100,200)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # BEGIN GTID #-#-#
master-bin.000002 # Query # # use `test`; INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,NULL)
master-bin.000002 # Query # # COMMIT
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; DROP TABLE "t1" /* generated by server */
master-bin.000002 # Gtid # # GTID #-#-#
master-bin.000002 # Query # # use `test`; DROP PROCEDURE p1

View File

@ -982,3 +982,28 @@ DROP PROCEDURE p1;
#
# End of MDEV-10914 ROW data type for stored routine variables
#
#
# MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
#
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
rec1.c:= 10e2;
rec1.d:= 10.12;
rec1.c:= rec1.d;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set rec1@0 NULL
1 set rec1.a@0["a"] 10
2 set rec1.b@0["b"] 'bbb'
3 set rec1.c@0["c"] 10e2
4 set rec1.d@0["d"] 10.12
5 set rec1.c@0["c"] rec1.d@0["d"]
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@ -2198,3 +2198,624 @@ DROP PROCEDURE p1;
#
# End of MDEV-10914 ROW data type for stored routine variables
#
#
# MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
#
#
# Referring to a table in a non-existing database
#
CREATE PROCEDURE p1()
AS
rec test2.t1%ROWTYPE;
BEGIN
NULL;
END;
$$
CALL p1();
ERROR 42S02: Table 'test2.t1' doesn't exist
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CALL p1();
ERROR 42S02: Table 'test2.t1' doesn't exist
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Referring to a table in the current database
#
CREATE PROCEDURE p1()
AS
rec t1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
ERROR 42S02: Table 'test.t1' doesn't exist
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CALL p1();
Table Create Table
t2 CREATE TABLE "t2" (
"rec.a" bigint(11) DEFAULT NULL,
"rec.b" varchar(10) DEFAULT NULL,
"rec.c" double DEFAULT NULL,
"rec.d" decimal(10,0) DEFAULT NULL
)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Referring to a table in an explicitly specified database
#
CREATE PROCEDURE p1()
AS
rec test.t1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
ERROR 42S02: Table 'test.t1' doesn't exist
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CALL p1();
Table Create Table
t2 CREATE TABLE "t2" (
"rec.a" bigint(11) DEFAULT NULL,
"rec.b" varchar(10) DEFAULT NULL,
"rec.c" double DEFAULT NULL,
"rec.d" decimal(10,0) DEFAULT NULL
)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Referring to a view in the current database
#
CREATE PROCEDURE p1()
AS
rec v1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
ERROR 42S02: Table 'test.v1' doesn't exist
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CREATE VIEW v1 AS SELECT * FROM t1;
CALL p1();
Table Create Table
t2 CREATE TABLE "t2" (
"rec.a" bigint(11) DEFAULT NULL,
"rec.b" varchar(10) DEFAULT NULL,
"rec.c" double DEFAULT NULL,
"rec.d" decimal(10,0) DEFAULT NULL
)
DROP VIEW v1;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Referring to a view in an explicitly specified database
#
CREATE PROCEDURE p1()
AS
rec test.v1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
ERROR 42S02: Table 'test.v1' doesn't exist
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CREATE VIEW v1 AS SELECT * FROM t1;
CALL p1();
Table Create Table
t2 CREATE TABLE "t2" (
"rec.a" bigint(11) DEFAULT NULL,
"rec.b" varchar(10) DEFAULT NULL,
"rec.c" double DEFAULT NULL,
"rec.d" decimal(10,0) DEFAULT NULL
)
DROP VIEW v1;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Checking that all table%ROWTYPE fields are NULL by default
#
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
SELECT rec1.a, rec1.b, rec1.c, rec1.d;
END;
$$
CALL p1();
rec1.a rec1.b rec1.c rec1.d
NULL NULL NULL NULL
DROP TABLE t1;
DROP PROCEDURE p1;
#
# A table%ROWTYPE variable with a ROW expression as a default
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb');
BEGIN
SELECT rec1.a, rec1.b;
END;
$$
CALL p1();
rec1.a rec1.b
10 bbb
DROP TABLE t1;
DROP PROCEDURE p1;
#
# A table%ROWTYPE variable with an incompatible ROW expression as a default
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb','ccc');
BEGIN
SELECT rec1.a, rec1.b;
END;
$$
CALL p1();
ERROR 21000: Operand should contain 2 column(s)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# A table%ROWTYPE variable with a ROW variable as a default
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
rec2 t1%ROWTYPE DEFAULT rec1;
BEGIN
SELECT rec2.a, rec2.b;
END;
$$
CALL p1();
rec2.a rec2.b
10 bbb
DROP TABLE t1;
DROP PROCEDURE p1;
#
# A ROW variable using a table%ROWTYPE variable as a default
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE := ROW(10,'bbb');
rec2 ROW(a INT, b VARCHAR(10)):= rec1;
BEGIN
SELECT rec2.a, rec2.b;
END;
$$
CALL p1();
rec2.a rec2.b
10 bbb
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Assigning table%ROWTYPE variables with a different column count
#
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE);
CREATE TABLE t2 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec2:=rec1;
END;
$$
CALL p1();
ERROR 21000: Operand should contain 2 column(s)
DROP PROCEDURE p1;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec1:=rec2;
END;
$$
CALL p1();
ERROR 21000: Operand should contain 3 column(s)
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Assigning compatible table%ROWTYPE variables (equal number of fields)
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (x INT, y VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
rec2:=rec1;
SELECT rec2.x, rec2.y;
END;
$$
CALL p1();
rec2.x rec2.y
10 bbb
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Assigning between incompatible table%ROWTYPE and explicit ROW variables
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 ROW(x INT,y INT,z INT);
BEGIN
rec2.x:= 10;
rec2.y:= 20;
rec2.z:= 30;
rec1:= rec2;
END;
$$
CALL p1();
ERROR 21000: Operand should contain 2 column(s)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Assigning between compatible table%ROWTYPE and explicit ROW variables
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 ROW(x INT,y INT);
BEGIN
rec2.x:= 10;
rec2.y:= 20;
rec1:= rec2;
SELECT rec1.a, rec1.b;
rec1.a:= 11;
rec1.b:= 21;
rec2:= rec1;
SELECT rec2.x, rec2.y;
END;
$$
CALL p1();
rec1.a rec1.b
10 20
rec2.x rec2.y
11 21
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Assigning table%ROWTYPE from a ROW expression
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
rec1:= ROW(10,20);
SELECT rec1.a, rec1.b;
END;
$$
CALL p1();
rec1.a rec1.b
10 20
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Fetching a cursor into a table%ROWTYPE variable with a wrong field count
#
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE TABLE t2 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
CLOSE cur1;
END;
$$
CALL p1();
ERROR HY000: Incorrect number of FETCH variables
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Fetching a cursor into a table%ROWTYPE variable
#
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32);
INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33);
CREATE PROCEDURE p1()
AS
rec t1%ROWTYPE;
CURSOR cur IS SELECT * FROM t1;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN cur%NOTFOUND;
SELECT rec.a, rec.b, rec.c, rec.d;
INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d);
END LOOP;
CLOSE cur;
END;
$$
CALL p1();
rec.a rec.b rec.c rec.d
10 bb1 11111.1 12.31
rec.a rec.b rec.c rec.d
20 bb2 22222.2 12.32
rec.a rec.b rec.c rec.d
30 bb3 33333.3 12.33
SELECT * FROM t2;
a b c d
10 bb1 11111.1 12.31
20 bb2 22222.2 12.32
30 bb3 33333.3 12.33
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Fetching a cursor into a table%ROWTYPE variable with different column names
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (x INT, y VARCHAR(10));
INSERT INTO t1 VALUES (10,'bbb');
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
SELECT rec2.x, rec2.y;
CLOSE cur1;
END;
$$
CALL p1();
rec2.x rec2.y
10 bbb
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# Fetching a cursor into a table%ROWTYPE variable, with truncation
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (a INT, b INT);
INSERT INTO t1 VALUES (10,'11x');
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
SELECT rec2.a, rec2.b;
CLOSE cur1;
END;
$$
CALL p1();
rec2.a rec2.b
10 11
Warnings:
Warning 1265 Data truncated for column 'b' at row 1
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
#
# table%ROWTYPE variables are not allowed in LIMIT
#
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,2);
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE:=(1,2);
BEGIN
SELECT * FROM t1 LIMIT rec1.a;
END;
$$
ERROR HY000: A variable of a non-integer based type in LIMIT clause
DROP TABLE t1;
#
# table%ROWTYPE variable fields as OUT parameters
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
AS
BEGIN
a:=10;
b:='bb';
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE;
BEGIN
CALL p1(rec1.a, rec1.b);
SELECT rec1.a, rec1.b;
END;
$$
CALL p2();
rec1.a rec1.b
10 bb
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Passing the entire table%ROWTYPE variable
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
AS
BEGIN
SELECT a.a, a.b;
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE:= ROW(10,'bb');
BEGIN
CALL p1(rec1);
END;
$$
CALL p2();
a.a a.b
10 bb
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Passing the entire table%ROWTYPE variable as an OUT parameter
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
AS
BEGIN
a:= ROW(10,'bb');
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE;
BEGIN
CALL p1(rec1);
SELECT rec1.a, rec1.b;
END;
$$
CALL p2();
rec1.a rec1.b
10 bb
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Assigning a table%ROWTYPE field to an OUT parameter
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1 (res IN OUT INTEGER)
AS
rec1 t1%ROWTYPE:=ROW(10,'b0');
BEGIN
res:=rec1.a;
END;
$$
CALL p1(@res);
SELECT @res;
@res
10
SET @res=NULL;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Testing Item_splocal_row_field_by_name::print
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
EXPLAIN EXTENDED SELECT rec.a, rec.b;
END;
$$
CALL p1();
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select rec.a@0["a"] AS "rec.a",rec.b@0["b"] AS "rec.b"
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Non-existing field
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE;
BEGIN
SELECT rec.c;
END;
$$
CALL p1();
ERROR HY000: Row variable 'rec' does not have a field 'c'
ALTER TABLE t1 ADD c INT;
CALL p1();
rec.c
NULL
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Testing that field names are case insensitive
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
SELECT rec.A, rec.B;
END;
$$
CALL p1();
rec.A rec.B
10 bb
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Testing that table%ROWTYPE uses temporary tables vs shadowed real tables
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10));
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
SELECT rec.A, rec.B;
END;
$$
CALL p1();
ERROR HY000: Row variable 'rec' does not have a field 'A'
DROP TEMPORARY TABLE t1;
CALL p1();
rec.A rec.B
10 bb
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Testing that the structure of table%ROWTYPE variables is determined at the very beginning and is not changed after ALTER
#
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10,'b10');
CREATE PROCEDURE p1 AS
BEGIN
ALTER TABLE t1 ADD c INT;
DECLARE
rec t1%ROWTYPE; -- this will not have column "c"
BEGIN
rec.c:=10;
END;
END;
$$
CALL p1();
ERROR HY000: Row variable 'rec' does not have a field 'c'
DROP TABLE t1;
DROP PROCEDURE p1;

View File

@ -63,6 +63,16 @@ $$
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP PROCEDURE p1;
CREATE PROCEDURE p1()
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP PROCEDURE p1;
#
# Stored procedure: Using %TYPE for with a table that we don't have access to
# DEFINER root, SQL SECURITY INVOKER
@ -80,6 +90,19 @@ connection conn1;
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP PROCEDURE p1;
connection default;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
connection conn1;
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP PROCEDURE p1;
#
# Stored procedure: Using %TYPE for with a table that we don't have access to
# DEFINER root, SQL SECURITY DEFINER
@ -98,6 +121,21 @@ CALL p1;
a
10
DROP PROCEDURE p1;
connection default;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1%ROWTYPE;
BEGIN
a.a:= 10;
SELECT a.a;
END;
$$
connection conn1;
CALL p1;
a.a
10
DROP PROCEDURE p1;
#
# Stored function: Using %TYPE for with a table that we don't have access to
# DEFINER user1, SQL SECURITY DEFAULT
@ -227,6 +265,17 @@ $$
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1'
DROP PROCEDURE p1;
CREATE PROCEDURE p1()
AS
b db1.t1%ROWTYPE;
BEGIN
b.b:=10;
SELECT b.b;
END;
$$
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1'
DROP PROCEDURE p1;
#
# Clean up
#

View File

@ -79,3 +79,43 @@ source include/show_binlog_events.inc;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #
--echo #
--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE;
BEGIN
rec.a:=100;
rec.b:=200;
INSERT INTO t1 VALUES (rec.a,rec.b);
INSERT INTO t1 VALUES (10, rec=ROW(100,200));
INSERT INTO t1 VALUES (10, ROW(100,200)=rec);
INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.a:=NULL;
INSERT INTO t1 VALUES (11, rec=ROW(100,200));
INSERT INTO t1 VALUES (11, rec=ROW(100,201));
INSERT INTO t1 VALUES (11, ROW(100,200)=rec);
INSERT INTO t1 VALUES (11, ROW(100,201)=rec);
INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=rec;
rec.b:=NULL;
INSERT INTO t1 VALUES (12, rec=ROW(100,200));
INSERT INTO t1 VALUES (12, ROW(100,200)=rec);
INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE rec=ROW(100,200);
INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=rec;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;

View File

@ -742,3 +742,25 @@ DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #
--echo #
--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
rec1.c:= 10e2;
rec1.d:= 10.12;
rec1.c:= rec1.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@ -598,7 +598,7 @@ DELIMITER ;$$
--echo # Referencing a non-existing row field
--echo #
DELIMITER $$;
--error ER_UNKNOWN_ERROR
--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
CREATE PROCEDURE p1()
AS
a ROW(a INT,b INT);
@ -1396,3 +1396,682 @@ DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #
--echo #
--echo # MDEV-12133 sql_mode=ORACLE: table%ROWTYPE in variable declarations
--echo #
--echo #
--echo # Referring to a table in a non-existing database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec test2.t1%ROWTYPE;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
--error ER_NO_SUCH_TABLE
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Referring to a table in the current database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec t1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Referring to a table in an explicitly specified database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec test.t1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Referring to a view in the current database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec v1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CREATE VIEW v1 AS SELECT * FROM t1;
CALL p1();
DROP VIEW v1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Referring to a view in an explicitly specified database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec test.v1%ROWTYPE;
BEGIN
CREATE TABLE t2 AS SELECT rec.a, rec.b, rec.c, rec.d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10));
CREATE VIEW v1 AS SELECT * FROM t1;
CALL p1();
DROP VIEW v1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Checking that all table%ROWTYPE fields are NULL by default
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
SELECT rec1.a, rec1.b, rec1.c, rec1.d;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # A table%ROWTYPE variable with a ROW expression as a default
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb');
BEGIN
SELECT rec1.a, rec1.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # A table%ROWTYPE variable with an incompatible ROW expression as a default
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE DEFAULT ROW(10,'bbb','ccc');
BEGIN
SELECT rec1.a, rec1.b;
END;
$$
DELIMITER ;$$
--error ER_OPERAND_COLUMNS
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # A table%ROWTYPE variable with a ROW variable as a default
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 ROW(a INT, b VARCHAR(10)):= ROW(10,'bbb');
rec2 t1%ROWTYPE DEFAULT rec1;
BEGIN
SELECT rec2.a, rec2.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # A ROW variable using a table%ROWTYPE variable as a default
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE := ROW(10,'bbb');
rec2 ROW(a INT, b VARCHAR(10)):= rec1;
BEGIN
SELECT rec2.a, rec2.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning table%ROWTYPE variables with a different column count
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE);
CREATE TABLE t2 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec2:=rec1;
END;
$$
DELIMITER ;$$
--error ER_OPERAND_COLUMNS
CALL p1();
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec1:=rec2;
END;
$$
DELIMITER ;$$
--error ER_OPERAND_COLUMNS
CALL p1();
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning compatible table%ROWTYPE variables (equal number of fields)
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (x INT, y VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 t2%ROWTYPE;
BEGIN
rec1.a:= 10;
rec1.b:= 'bbb';
rec2:=rec1;
SELECT rec2.x, rec2.y;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning between incompatible table%ROWTYPE and explicit ROW variables
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 ROW(x INT,y INT,z INT);
BEGIN
rec2.x:= 10;
rec2.y:= 20;
rec2.z:= 30;
rec1:= rec2;
END;
$$
DELIMITER ;$$
--error ER_OPERAND_COLUMNS
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning between compatible table%ROWTYPE and explicit ROW variables
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
rec2 ROW(x INT,y INT);
BEGIN
rec2.x:= 10;
rec2.y:= 20;
rec1:= rec2;
SELECT rec1.a, rec1.b;
rec1.a:= 11;
rec1.b:= 21;
rec2:= rec1;
SELECT rec2.x, rec2.y;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Assigning table%ROWTYPE from a ROW expression
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE;
BEGIN
rec1:= ROW(10,20);
SELECT rec1.a, rec1.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Fetching a cursor into a table%ROWTYPE variable with a wrong field count
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE TABLE t2 (a INT, b VARCHAR(10));
INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
CLOSE cur1;
END;
$$
DELIMITER ;$$
--error ER_SP_WRONG_NO_OF_FETCH_ARGS
CALL p1();
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Fetching a cursor into a table%ROWTYPE variable
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10), c DOUBLE, d DECIMAL(10,2));
CREATE TABLE t2 LIKE t1;
INSERT INTO t1 VALUES (10,'bb1',111.111e2, 12.31);
INSERT INTO t1 VALUES (20,'bb2',222.222e2, 12.32);
INSERT INTO t1 VALUES (30,'bb3',333.333e2, 12.33);
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec t1%ROWTYPE;
CURSOR cur IS SELECT * FROM t1;
BEGIN
OPEN cur;
LOOP
FETCH cur INTO rec;
EXIT WHEN cur%NOTFOUND;
SELECT rec.a, rec.b, rec.c, rec.d;
INSERT INTO t2 VALUES (rec.a, rec.b, rec.c, rec.d);
END LOOP;
CLOSE cur;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t2;
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Fetching a cursor into a table%ROWTYPE variable with different column names
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (x INT, y VARCHAR(10));
INSERT INTO t1 VALUES (10,'bbb');
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
SELECT rec2.x, rec2.y;
CLOSE cur1;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # Fetching a cursor into a table%ROWTYPE variable, with truncation
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TABLE t2 (a INT, b INT);
INSERT INTO t1 VALUES (10,'11x');
DELIMITER $$;
CREATE PROCEDURE p1()
AS
rec2 t2%ROWTYPE;
CURSOR cur1 IS SELECT * FROM t1;
BEGIN
OPEN cur1;
FETCH cur1 INTO rec2;
SELECT rec2.a, rec2.b;
CLOSE cur1;
END;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t2;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # table%ROWTYPE variables are not allowed in LIMIT
--echo #
CREATE TABLE t1 (a INT, b INT);
INSERT INTO t1 VALUES (1,2);
DELIMITER $$;
--error ER_WRONG_SPVAR_TYPE_IN_LIMIT
CREATE PROCEDURE p1()
AS
rec1 t1%ROWTYPE:=(1,2);
BEGIN
SELECT * FROM t1 LIMIT rec1.a;
END;
$$
DELIMITER ;$$
DROP TABLE t1;
--echo #
--echo # table%ROWTYPE variable fields as OUT parameters
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1(a OUT INT,b OUT VARCHAR(10))
AS
BEGIN
a:=10;
b:='bb';
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE;
BEGIN
CALL p1(rec1.a, rec1.b);
SELECT rec1.a, rec1.b;
END;
$$
DELIMITER ;$$
CALL p2();
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Passing the entire table%ROWTYPE variable
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1(a ROW(a INT, b VARCHAR(10)))
AS
BEGIN
SELECT a.a, a.b;
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE:= ROW(10,'bb');
BEGIN
CALL p1(rec1);
END;
$$
DELIMITER ;$$
CALL p2();
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Passing the entire table%ROWTYPE variable as an OUT parameter
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1(a OUT ROW(a INT, b VARCHAR(10)))
AS
BEGIN
a:= ROW(10,'bb');
END;
$$
CREATE PROCEDURE p2()
AS
rec1 t1%ROWTYPE;
BEGIN
CALL p1(rec1);
SELECT rec1.a, rec1.b;
END;
$$
DELIMITER ;$$
CALL p2();
DROP PROCEDURE p2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Assigning a table%ROWTYPE field to an OUT parameter
--echo #
DELIMITER $$;
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE PROCEDURE p1 (res IN OUT INTEGER)
AS
rec1 t1%ROWTYPE:=ROW(10,'b0');
BEGIN
res:=rec1.a;
END;
$$
DELIMITER ;$$
CALL p1(@res);
SELECT @res;
SET @res=NULL;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing Item_splocal_row_field_by_name::print
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
EXPLAIN EXTENDED SELECT rec.a, rec.b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Non-existing field
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE;
BEGIN
SELECT rec.c;
END;
$$
DELIMITER ;$$
--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
CALL p1();
ALTER TABLE t1 ADD c INT;
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing that field names are case insensitive
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
SELECT rec.A, rec.B;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing that table%ROWTYPE uses temporary tables vs shadowed real tables
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
CREATE TEMPORARY TABLE t1 (x INT, y VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec t1%ROWTYPE:=ROW(10,'bb');
BEGIN
SELECT rec.A, rec.B;
END;
$$
DELIMITER ;$$
--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
CALL p1();
DROP TEMPORARY TABLE t1;
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Testing that the structure of table%ROWTYPE variables is determined at the very beginning and is not changed after ALTER
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(32));
INSERT INTO t1 VALUES (10,'b10');
DELIMITER $$;
CREATE PROCEDURE p1 AS
BEGIN
ALTER TABLE t1 ADD c INT;
DECLARE
rec t1%ROWTYPE; -- this will not have column "c"
BEGIN
rec.c:=10;
END;
END;
$$
DELIMITER ;$$
--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
CALL p1();
DROP TABLE t1;
DROP PROCEDURE p1;

View File

@ -77,6 +77,19 @@ DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
@ -100,6 +113,23 @@ CALL p1;
DROP PROCEDURE p1;
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1%ROWTYPE;
BEGIN
SELECT a.a;
END;
$$
DELIMITER ;$$
connection conn1;
--error ER_TABLEACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY DEFINER
@ -120,6 +150,22 @@ connection conn1;
CALL p1;
DROP PROCEDURE p1;
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1%ROWTYPE;
BEGIN
a.a:= 10;
SELECT a.a;
END;
$$
DELIMITER ;$$
connection conn1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
@ -270,6 +316,20 @@ DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
b db1.t1%ROWTYPE;
BEGIN
b.b:=10;
SELECT b.b;
END;
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Clean up

View File

@ -548,7 +548,7 @@ DELIMITER ;$$
--echo # Referencing a non-existing row field
--echo #
DELIMITER $$;
--error ER_UNKNOWN_ERROR
--error ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
CREATE PROCEDURE p1()
BEGIN
DECLARE a ROW(a INT,b INT);

View File

@ -4009,12 +4009,21 @@ public:
class Spvar_definition: public Column_definition
{
class Qualified_column_ident *m_column_type_ref; // for %TYPE
class Table_ident *m_table_rowtype_ref; // for table%ROWTYPE
Row_definition_list *m_row_field_definitions; // for ROW
public:
Spvar_definition()
:m_column_type_ref(NULL),
m_table_rowtype_ref(NULL),
m_row_field_definitions(NULL) { }
Spvar_definition(THD *thd, Field *field)
:Column_definition(thd, field, NULL),
m_column_type_ref(NULL),
m_table_rowtype_ref(NULL),
m_row_field_definitions(NULL)
{ }
bool is_column_type_ref() const { return m_column_type_ref != 0; }
bool is_table_rowtype_ref() const { return m_table_rowtype_ref != 0; }
class Qualified_column_ident *column_type_ref() const
{
return m_column_type_ref;
@ -4024,6 +4033,15 @@ public:
m_column_type_ref= ref;
}
class Table_ident *table_rowtype_ref() const
{
return m_table_rowtype_ref;
}
void set_table_rowtype_ref(class Table_ident *ref)
{
m_table_rowtype_ref= ref;
}
/*
Find a ROW field by name.
See Row_field_list::find_row_field_by_name() for details.

View File

@ -1501,12 +1501,9 @@ Item_sp_variable::Item_sp_variable(THD *thd, char *sp_var_name_str,
}
bool Item_sp_variable::fix_fields(THD *thd, Item **)
bool Item_sp_variable::fix_fields_from_item(THD *thd, Item **, const Item *it)
{
Item *it;
m_thd= thd; /* NOTE: this must be set before any this_xxx() */
it= this_item();
DBUG_ASSERT(it->fixed);
@ -1612,11 +1609,17 @@ Item_splocal::Item_splocal(THD *thd, const LEX_STRING &sp_var_name,
}
bool Item_splocal::fix_fields(THD *thd, Item **ref)
{
return fix_fields_from_item(thd, ref, thd->spcont->get_item(m_var_idx));
}
Item *
Item_splocal::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
DBUG_ASSERT(fixed);
return m_thd->spcont->get_item(m_var_idx);
}
@ -1624,7 +1627,7 @@ const Item *
Item_splocal::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
DBUG_ASSERT(fixed);
return m_thd->spcont->get_item(m_var_idx);
}
@ -1633,7 +1636,7 @@ Item **
Item_splocal::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->sp);
DBUG_ASSERT(fixed);
return thd->spcont->get_item_addr(m_var_idx);
}
@ -1729,10 +1732,18 @@ bool Item_splocal::check_cols(uint n)
}
bool Item_splocal_row_field::fix_fields(THD *thd, Item **ref)
{
Item *item= thd->spcont->get_item(m_var_idx)->element_index(m_field_idx);
return fix_fields_from_item(thd, ref, item);
}
Item *
Item_splocal_row_field::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
DBUG_ASSERT(fixed);
return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx);
}
@ -1741,6 +1752,7 @@ const Item *
Item_splocal_row_field::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
DBUG_ASSERT(fixed);
return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx);
}
@ -1749,6 +1761,7 @@ Item **
Item_splocal_row_field::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->sp);
DBUG_ASSERT(fixed);
return thd->spcont->get_item(m_var_idx)->addr(m_field_idx);
}
@ -1773,6 +1786,43 @@ bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
}
bool Item_splocal_row_field_by_name::fix_fields(THD *thd, Item **it)
{
m_thd= thd;
Item *row= m_thd->spcont->get_item(m_var_idx);
if (row->element_index_by_name(&m_field_idx, m_field_name))
{
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
m_name.str, m_field_name.str);
return true;
}
Item *item= row->element_index(m_field_idx);
set_handler(item->type_handler());
return fix_fields_from_item(thd, it, item);
}
void Item_splocal_row_field_by_name::print(String *str, enum_query_type)
{
str->reserve(m_name.length + 2 * m_field_name.length + 8);
str->append(m_name.str, m_name.length);
str->append('.');
str->append(m_field_name.str, m_field_name.length);
str->append('@');
str->append_ulonglong(m_var_idx);
str->append("[\"", 2);
str->append(m_field_name.str, m_field_name.length);
str->append("\"]", 2);
}
bool Item_splocal_row_field_by_name::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
DBUG_ASSERT(fixed); // Make sure m_field_idx is already set
return Item_splocal_row_field::set_value(thd, ctx, it);
}
/*****************************************************************************
Item_case_expr methods
*****************************************************************************/
@ -1784,6 +1834,13 @@ Item_case_expr::Item_case_expr(THD *thd, uint case_expr_id):
}
bool Item_case_expr::fix_fields(THD *thd, Item **ref)
{
Item *item= thd->spcont->get_case_expr(m_case_expr_id);
return fix_fields_from_item(thd, ref, item);
}
Item *
Item_case_expr::this_item()
{
@ -7398,6 +7455,26 @@ void Item_field::print(String *str, enum_query_type query_type)
}
bool Item_field_row::element_index_by_name(uint *idx,
const LEX_STRING &name) const
{
Field *field;
for (uint i= 0; (field= get_row_field(i)); i++)
{
// Use the same comparison style with sp_context::find_variable()
if (!my_strnncoll(system_charset_info,
(const uchar *) field->field_name,
strlen(field->field_name),
(const uchar *) name.str, name.length))
{
*idx= i;
return false;
}
}
return true;
}
void Item_temptable_field::print(String *str, enum_query_type query_type)
{
/*

View File

@ -1622,6 +1622,10 @@ public:
// Row emulation
virtual uint cols() { return 1; }
virtual Item* element_index(uint i) { return this; }
virtual bool element_index_by_name(uint *idx, const LEX_STRING &name) const
{
return true;
}
virtual Item** addr(uint i) { return 0; }
virtual bool check_cols(uint c);
// It is not row => null inside is impossible
@ -2086,6 +2090,7 @@ protected:
*/
THD *m_thd;
bool fix_fields_from_item(THD *thd, Item **, const Item *);
public:
LEX_STRING m_name;
@ -2102,7 +2107,7 @@ public:
Item_sp_variable(THD *thd, char *sp_var_name_str, uint sp_var_name_length);
public:
bool fix_fields(THD *thd, Item **);
bool fix_fields(THD *thd, Item **)= 0;
double val_real();
longlong val_int();
@ -2175,6 +2180,7 @@ public:
enum_field_types sp_var_type,
uint pos_in_q= 0, uint len_in_q= 0);
bool fix_fields(THD *, Item **);
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
@ -2273,6 +2279,7 @@ public:
*/
class Item_splocal_row_field :public Item_splocal
{
protected:
LEX_STRING m_field_name;
uint m_field_idx;
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@ -2288,6 +2295,7 @@ public:
m_field_name(sp_field_name),
m_field_idx(sp_field_idx)
{ }
bool fix_fields(THD *thd, Item **);
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
@ -2296,6 +2304,25 @@ public:
};
class Item_splocal_row_field_by_name :public Item_splocal_row_field
{
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field_by_name(THD *thd,
const LEX_STRING &sp_var_name,
const LEX_STRING &sp_field_name,
uint sp_var_idx,
enum_field_types sp_var_type,
uint pos_in_q= 0, uint len_in_q= 0)
:Item_splocal_row_field(thd, sp_var_name, sp_field_name,
sp_var_idx, 0 /* field index will be set later */,
sp_var_type, pos_in_q, len_in_q)
{ }
bool fix_fields(THD *thd, Item **it);
void print(String *str, enum_query_type query_type);
};
/*****************************************************************************
Item_splocal inline implementation.
*****************************************************************************/
@ -2325,6 +2352,7 @@ public:
Item_case_expr(THD *thd, uint case_expr_id);
public:
bool fix_fields(THD *thd, Item **);
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
@ -2758,6 +2786,7 @@ public:
Item_result result_type() const{ return ROW_RESULT ; }
Item_result cmp_type() const { return ROW_RESULT; }
uint cols() { return arg_count; }
bool element_index_by_name(uint *idx, const LEX_STRING &name) const;
Item* element_index(uint i) { return arg_count ? args[i] : this; }
Item** addr(uint i) { return arg_count ? args + i : NULL; }
bool check_cols(uint c)

View File

@ -530,6 +530,7 @@ static SYMBOL symbols[] = {
{ "ROW", SYM(ROW_SYM)},
{ "ROWCOUNT", SYM(ROWCOUNT_SYM)}, /* Oracle-N */
{ "ROWS", SYM(ROWS_SYM)},
{ "ROWTYPE", SYM(ROWTYPE_SYM)},
{ "ROW_COUNT", SYM(ROW_COUNT_SYM)},
{ "ROW_FORMAT", SYM(ROW_FORMAT_SYM)},
{ "RTREE", SYM(RTREE_SYM)},

View File

@ -7462,3 +7462,5 @@ ER_WRONG_PARAMCOUNT_TO_CURSOR 42000
rus "Некорректное количество параметров для курсора '%-.192s'"
ER_UNKNOWN_STRUCTURED_VARIABLE
eng "Unknown structured system variable or ROW routine variable '%-.*s'"
ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD
eng "Row variable '%-.192s' does not have a field '%-.192s'"

View File

@ -343,6 +343,12 @@ Item *
sp_prepare_func_item(THD* thd, Item **it_addr, uint cols)
{
DBUG_ENTER("sp_prepare_func_item");
if (!(*it_addr)->fixed &&
(*it_addr)->fix_fields(thd, it_addr))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(NULL);
}
it_addr= (*it_addr)->this_item_addr(thd, it_addr);
if ((!(*it_addr)->fixed &&
@ -3412,6 +3418,63 @@ sp_instr_set_row_field::print(String *str)
}
/*
sp_instr_set_field_by_name class functions
*/
int
sp_instr_set_row_field_by_name::exec_core(THD *thd, uint *nextp)
{
int res;
uint idx;
Item_field_row *row= (Item_field_row*) thd->spcont->get_item(m_offset);
if ((res= row->element_index_by_name(&idx, m_field_name)))
{
sp_variable *var= m_ctx->find_variable(m_offset);
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
var->name.str, m_field_name.str);
goto error;
}
res= thd->spcont->set_variable_row_field(thd, m_offset, idx, &m_value);
if (res)
{
/* Failed to evaluate the value. Reset the variable to NULL. */
thd->spcont->set_variable_row_field_to_null(thd, m_offset, idx);
}
error:
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
}
void
sp_instr_set_row_field_by_name::print(String *str)
{
/* set name.field@offset["field"] ... */
int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3 + 2;
sp_variable *var= m_ctx->find_variable(m_offset);
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_table_rowtype_ref());
rsrv+= var->name.length + 2 * m_field_name.length;
if (str->reserve(rsrv))
return;
str->qs_append(STRING_WITH_LEN("set "));
str->qs_append(var->name.str, var->name.length);
str->qs_append('.');
str->qs_append(m_field_name.str);
str->qs_append('@');
str->qs_append(m_offset);
str->qs_append("[\"",2);
str->qs_append(m_field_name.str);
str->qs_append("\"]",2);
str->qs_append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
/*
sp_instr_set_trigger_field class functions
*/
@ -4461,8 +4524,7 @@ sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
sp_instr_set *sp_set= new (thd->mem_root)
sp_instr_set(instructions(), spcont,
spv->offset, val, spv->sql_type(),
lex, true);
spv->offset, val, lex, true);
return sp_set == NULL || add_instr(sp_set);
}
@ -4484,12 +4546,31 @@ sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
spcont,
spv->offset,
field_idx, val,
spv->sql_type(),
lex, true);
return sp_set == NULL || add_instr(sp_set);
}
bool
sp_head::set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
sp_variable *spv,
const LEX_STRING &field_name,
Item *val, LEX *lex)
{
if (!(val= adjust_assignment_source(thd, val, NULL)))
return true;
sp_instr_set_row_field_by_name *sp_set=
new (thd->mem_root) sp_instr_set_row_field_by_name(instructions(),
spcont,
spv->offset,
field_name,
val,
lex, true);
return sp_set == NULL || add_instr(sp_set);
}
bool sp_head::add_open_cursor(THD *thd, sp_pcontext *spcont, uint offset,
sp_pcontext *param_spcont,
List<sp_assignment_lex> *parameters)

View File

@ -366,6 +366,10 @@ public:
bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex);
bool set_local_variable_row_field_by_name(THD *thd, sp_pcontext *spcont,
sp_variable *spv,
const LEX_STRING &field_name,
Item *val, LEX *lex);
private:
/**
Generate a code to set a single cursor parameter variable.
@ -1002,9 +1006,9 @@ class sp_instr_set : public sp_instr
public:
sp_instr_set(uint ip, sp_pcontext *ctx,
uint offset, Item *val, enum enum_field_types type_arg,
uint offset, Item *val,
LEX *lex, bool lex_resp)
: sp_instr(ip, ctx), m_offset(offset), m_value(val), m_type(type_arg),
: sp_instr(ip, ctx), m_offset(offset), m_value(val),
m_lex_keeper(lex, lex_resp)
{}
@ -1021,7 +1025,6 @@ protected:
uint m_offset; ///< Frame offset
Item *m_value;
enum enum_field_types m_type; ///< The declared type
sp_lex_keeper m_lex_keeper;
}; // class sp_instr_set : public sp_instr
@ -1042,9 +1045,9 @@ public:
sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
uint offset, uint field_offset,
Item *val, enum enum_field_types type_arg,
Item *val,
LEX *lex, bool lex_resp)
: sp_instr_set(ip, ctx, offset, val, type_arg, lex, lex_resp),
: sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
m_field_offset(field_offset)
{}
@ -1057,6 +1060,32 @@ public:
}; // class sp_instr_set_field : public sp_instr_set
class sp_instr_set_row_field_by_name : public sp_instr_set
{
// Prevent use of this
sp_instr_set_row_field_by_name(const sp_instr_set_row_field &);
void operator=(sp_instr_set_row_field_by_name &);
const LEX_STRING m_field_name;
public:
sp_instr_set_row_field_by_name(uint ip, sp_pcontext *ctx,
uint offset, const LEX_STRING &field_name,
Item *val,
LEX *lex, bool lex_resp)
: sp_instr_set(ip, ctx, offset, val, lex, lex_resp),
m_field_name(field_name)
{}
virtual ~sp_instr_set_row_field_by_name()
{}
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str);
}; // class sp_instr_set_field_by_name : public sp_instr_set
/**
Set NEW/OLD row field value instruction. Used in triggers.
*/

View File

@ -634,8 +634,7 @@ sp_variable::find_row_field(const LEX_STRING &var_name,
const Spvar_definition *def;
if ((def= field_def.find_row_field_by_name(field_name.str, row_field_offset)))
return def;
my_printf_error(ER_UNKNOWN_ERROR,
"Row variable '%s' does not have a field '%s'",
MYF(0), var_name.str, field_name.str);
my_error(ER_ROW_VARIABLE_DOES_NOT_HAVE_FIELD, MYF(0),
var_name.str, field_name.str);
return NULL;
}

View File

@ -132,13 +132,11 @@ bool sp_rcontext::init_var_table(THD *thd,
*/
static inline bool
check_column_grant_for_type_ref(THD *thd, TABLE_LIST *table_list,
const Qualified_column_ident *col)
const char *str, size_t length)
{
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table_list->table->grant.want_privilege= SELECT_ACL;
return check_column_grant_in_table_ref(thd, table_list,
col->m_column.str,
col->m_column.length);
return check_column_grant_in_table_ref(thd, table_list, str, length);
#else
return false;
#endif
@ -175,7 +173,9 @@ bool sp_rcontext::resolve_type_ref(THD *thd, Column_definition *def,
{
if ((src= lex.query_tables->table->find_field_by_name(ref->m_column.str)))
{
if (!(rc= check_column_grant_for_type_ref(thd, table_list, ref)))
if (!(rc= check_column_grant_for_type_ref(thd, table_list,
ref->m_column.str,
ref->m_column.length)))
{
*def= Column_definition(thd, src, NULL/*No defaults,no constraints*/);
def->flags&= (uint) ~NOT_NULL_FLAG;
@ -195,6 +195,54 @@ bool sp_rcontext::resolve_type_ref(THD *thd, Column_definition *def,
}
bool sp_rcontext::resolve_table_rowtype_ref(THD *thd,
Row_definition_list &defs,
Table_ident *ref)
{
Open_tables_backup open_tables_state_backup;
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
TABLE_LIST *table_list;
LEX *save_lex= thd->lex;
bool rc= true;
sp_lex_local lex(thd, thd->lex);
thd->lex= &lex;
lex.context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
// Make %ROWTYPE variables see temporary tables that shadow permanent tables
thd->temporary_tables= open_tables_state_backup.temporary_tables;
if ((table_list= lex.select_lex.add_table_to_list(thd, ref, NULL, 0,
TL_READ_NO_INSERT,
MDL_SHARED_READ)) &&
!check_table_access(thd, SELECT_ACL, table_list, TRUE, UINT_MAX, FALSE) &&
!open_tables_only_view_structure(thd, table_list,
thd->mdl_context.has_locks()))
{
for (Field **src= lex.query_tables->table->field; *src; src++)
{
if ((rc= check_column_grant_for_type_ref(thd, table_list,
src[0]->field_name,
strlen(src[0]->field_name))))
break;
Spvar_definition *def= new (thd->mem_root) Spvar_definition(thd, *src);
def->flags&= (uint) ~NOT_NULL_FLAG;
if ((rc= def->sp_prepare_create_field(thd, thd->mem_root)))
break;
defs.push_back(def, thd->mem_root);
}
}
lex.unit.cleanup();
thd->temporary_tables= NULL; // Avoid closing temporary tables
close_thread_tables(thd);
thd->lex= save_lex;
thd->restore_backup_open_tables_state(&open_tables_state_backup);
return rc;
}
bool sp_rcontext::resolve_type_refs(THD *thd, List<Spvar_definition> &defs)
{
List_iterator<Spvar_definition> it(defs);
@ -229,7 +277,16 @@ bool sp_rcontext::init_var_items(THD *thd,
for (uint idx= 0; idx < num_vars; ++idx, def= it++)
{
Field *field= m_var_table->field[idx];
if (def->is_row())
if (def->is_table_rowtype_ref())
{
Row_definition_list defs;
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
if (!(m_var_items[idx]= item) ||
resolve_table_rowtype_ref(thd, defs, def->table_rowtype_ref()) ||
item->row_create_items(thd, &defs))
return true;
}
else if (def->is_row())
{
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
if (!(m_var_items[idx]= item) ||
@ -706,7 +763,8 @@ int sp_cursor::fetch(THD *thd, List<sp_variable> *vars)
}
if (vars->elements != result.get_field_count() &&
(vars->elements != 1 ||
!vars->head()->field_def.is_row(result.get_field_count())))
result.get_field_count() !=
thd->spcont->get_item(vars->head()->offset)->cols()))
{
my_message(ER_SP_WRONG_NO_OF_FETCH_ARGS,
ER_THD(thd, ER_SP_WRONG_NO_OF_FETCH_ARGS), MYF(0));
@ -787,8 +845,11 @@ bool sp_cursor::Select_fetch_into_spvars::
int sp_cursor::Select_fetch_into_spvars::send_data(List<Item> &items)
{
return (spvar_list->elements == 1 &&
(spvar_list->head())->field_def.is_row(items.elements)) ?
Item *item;
return spvar_list->elements == 1 &&
(item= thd->spcont->get_item(spvar_list->head()->offset)) &&
item->type_handler() == &type_handler_row &&
item->cols() == items.elements ?
thd->spcont->set_variable_row(thd, spvar_list->head()->offset, items) :
send_data_to_variable_list(*spvar_list, items);
}

View File

@ -342,6 +342,8 @@ private:
bool resolve_type_refs(THD *, List<Spvar_definition> &defs);
bool resolve_type_ref(THD *thd, Column_definition *def,
Qualified_column_ident *ref);
bool resolve_table_rowtype_ref(THD *thd, Row_definition_list &defs,
Table_ident *ref);
/// Create and initialize an Item-adapter (Item_field) for each SP-var field.
///

View File

@ -5376,6 +5376,10 @@ class Qualified_column_ident: public Table_ident
public:
LEX_STRING m_column;
public:
Qualified_column_ident(const LEX_STRING column)
:Table_ident(null_lex_str),
m_column(column)
{ }
Qualified_column_ident(const LEX_STRING table, const LEX_STRING column)
:Table_ident(table),
m_column(column)

View File

@ -5280,9 +5280,9 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
spcont, var_idx, dflt_value_item,
spvar->field_def.sql_type, this, last);
sp_instr_set(sphead->instructions(),
spcont, var_idx, dflt_value_item,
this, last);
if (is == NULL || sphead->add_instr(is))
return true;
}
@ -5292,6 +5292,56 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
}
bool
LEX::sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
Qualified_column_ident *ref,
Item *def)
{
uint num_vars= spcont->context_var_count();
if (!def && !(def= new (thd->mem_root) Item_null(thd)))
return true;
for (uint i= num_vars - nvars; i < num_vars; i++)
{
bool last= i == num_vars - 1;
uint var_idx= spcont->var_context2runtime(i);
sp_variable *spvar= spcont->find_context_variable(i);
/*
When parsing a qualified identifier chain, the parser does not know yet
if it's going to be a qualified column name (for %TYPE),
or a qualified table name (for %ROWTYPE). So it collects the chain
into Qualified_column_ident.
Now we know that it was actually a qualified table name (%ROWTYPE).
Create a new Table_ident from Qualified_column_ident,
shifting fields as follows:
- ref->m_column becomes table_ref->table
- ref->table becomes table_ref->db
*/
Table_ident *table_ref;
if (!(table_ref= new (thd->mem_root) Table_ident(thd,
ref->table,
ref->m_column,
false)))
return true;
spvar->field_def.set_table_rowtype_ref(table_ref);
spvar->field_def.field_name= spvar->name.str;
spvar->default_value= def;
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
sp_instr_set(sphead->instructions(),
spcont, var_idx, def,
this, last);
if (is == NULL || sphead->add_instr(is))
return true;
}
// Make sure sp_rcontext is created using the invoker security context:
sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
spcont->declare_var_boundary(0);
return sphead->restore_lex(thd);
}
bool
LEX::sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
Qualified_column_ident *ref,
@ -6114,12 +6164,12 @@ Item *LEX::create_item_ident_nospvar(THD *thd,
}
Item_splocal_row_field *LEX::create_item_spvar_row_field(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b,
sp_variable *spv,
uint pos_in_q,
uint length_in_q)
Item_splocal *LEX::create_item_spvar_row_field(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b,
sp_variable *spv,
uint pos_in_q,
uint length_in_q)
{
if (!parsing_options.allows_variable)
{
@ -6127,17 +6177,28 @@ Item_splocal_row_field *LEX::create_item_spvar_row_field(THD *thd,
return NULL;
}
uint row_field_offset;
const Spvar_definition *def;
if (!(def= spv->find_row_field(a, b, &row_field_offset)))
return NULL;
Item_splocal *item;
if (spv->field_def.is_table_rowtype_ref())
{
if (!(item= new (thd->mem_root)
Item_splocal_row_field_by_name(thd, a, b, spv->offset,
MYSQL_TYPE_NULL,
pos_in_q, length_in_q)))
return NULL;
}
else
{
uint row_field_offset;
const Spvar_definition *def;
if (!(def= spv->find_row_field(a, b, &row_field_offset)))
return NULL;
Item_splocal_row_field *item;
if (!(item= new (thd->mem_root)
Item_splocal_row_field(thd, a, b,
spv->offset, row_field_offset,
def->sql_type, pos_in_q, length_in_q)))
return NULL;
if (!(item= new (thd->mem_root)
Item_splocal_row_field(thd, a, b,
spv->offset, row_field_offset,
def->sql_type, pos_in_q, length_in_q)))
return NULL;
}
#ifndef DBUG_OFF
item->m_sp= sphead;
#endif
@ -6298,7 +6359,7 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_STRING name,
spv->offset,
start_in_q,
length_in_q) :
spv->field_def.is_row() ?
spv->field_def.is_row() || spv->field_def.is_table_rowtype_ref() ?
new (thd->mem_root) Item_splocal_row(thd, name, spv->offset,
start_in_q, length_in_q) :
new (thd->mem_root) Item_splocal(thd, name,
@ -6345,6 +6406,10 @@ bool LEX::set_variable(const LEX_STRING &name1,
sp_variable *spv;
if (spcont && (spv= spcont->find_variable(name1, false)))
{
if (spv->field_def.is_table_rowtype_ref())
return sphead->set_local_variable_row_field_by_name(thd, spcont,
spv, name2,
item, this);
// A field of a ROW variable
uint row_field_offset;
return !spv->find_row_field(name1, name2, &row_field_offset) ||

View File

@ -3149,6 +3149,9 @@ public:
bool sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
Qualified_column_ident *col,
Item *def);
bool sp_variable_declarations_rowtype_finalize(THD *thd, int nvars,
Qualified_column_ident *,
Item *def);
bool sp_handler_declaration_init(THD *thd, int type);
bool sp_handler_declaration_finalize(THD *thd, int type);
@ -3202,12 +3205,12 @@ public:
@pos_in_q - position in the query (for binary log)
@length_in_q - length in the query (for binary log)
*/
Item_splocal_row_field *create_item_spvar_row_field(THD *thd,
const LEX_STRING &var,
const LEX_STRING &field,
sp_variable *spvar,
uint pos_in_q,
uint length_in_q);
Item_splocal *create_item_spvar_row_field(THD *thd,
const LEX_STRING &var,
const LEX_STRING &field,
sp_variable *spvar,
uint pos_in_q,
uint length_in_q);
/*
Create an item from its qualified name.
Depending on context, it can be either a ROW variable field,

View File

@ -1384,6 +1384,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ROWCOUNT_SYM /* Oracle-N */
%token ROW_SYM /* SQL-2003-R */
%token ROWS_SYM /* SQL-2003-R */
%token ROWTYPE_SYM /* Oracle-PLSQL-R */
%token ROW_COUNT_SYM /* SQL-2003-N */
%token ROW_FORMAT_SYM
%token ROW_NUMBER_SYM
@ -14419,6 +14420,7 @@ keyword_sp:
| ROLLUP_SYM {}
| ROUTINE_SYM {}
| ROWCOUNT_SYM {}
| ROWTYPE_SYM {}
| ROW_COUNT_SYM {}
| ROW_FORMAT_SYM {}
| ROW_SYM {}

View File

@ -802,6 +802,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%token ROWCOUNT_SYM /* Oracle-N */
%token ROW_SYM /* SQL-2003-R */
%token ROWS_SYM /* SQL-2003-R */
%token ROWTYPE_SYM /* Oracle-PLSQL-R */
%token ROW_COUNT_SYM /* SQL-2003-N */
%token ROW_FORMAT_SYM
%token ROW_NUMBER_SYM
@ -1027,6 +1028,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <qualified_column_ident>
qualified_column_ident
optionally_qualified_column_ident
%type <simple_string>
remember_name remember_end opt_db remember_tok_start
@ -1077,6 +1079,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
case_stmt_body opt_bin_mod
opt_if_exists_table_element opt_if_not_exists_table_element
opt_recursive
type_or_rowtype
%type <object_ddl_options>
create_or_replace
@ -2456,6 +2459,25 @@ qualified_column_ident:
}
;
optionally_qualified_column_ident:
sp_decl_ident
{
if (!($$= new (thd->mem_root) Qualified_column_ident($1)))
MYSQL_YYABORT;
}
| sp_decl_ident '.' ident
{
if (!($$= new (thd->mem_root) Qualified_column_ident($1, $3)))
MYSQL_YYABORT;
}
| sp_decl_ident '.' ident '.' ident
{
if (!($$= new (thd->mem_root) Qualified_column_ident(thd,
$1, $3, $5)))
MYSQL_YYABORT;
}
;
row_field_name:
ident_directly_assignable
{
@ -2493,6 +2515,10 @@ field_type_row:
ROW_SYM '(' row_field_definition_list ')' { $$= $3; }
;
type_or_rowtype:
TYPE_SYM { $$= 0; }
| ROWTYPE_SYM { $$= 1; }
;
sp_decl_body:
sp_decl_idents
@ -2511,10 +2537,12 @@ sp_decl_body:
{
Lex->sp_variable_declarations_init(thd, $1);
}
qualified_column_ident '%' TYPE_SYM
optionally_qualified_column_ident '%' type_or_rowtype
sp_opt_default
{
if (Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $3, $6))
if ($5 ?
Lex->sp_variable_declarations_rowtype_finalize(thd, $1, $3, $6) :
Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $3, $6))
MYSQL_YYABORT;
$$.init_using_vars($1);
}