1
0
mirror of https://github.com/MariaDB/server.git synced 2026-01-06 05:22:24 +03:00

MDEV-10914 ROW data type for stored routine variables

This commit is contained in:
Alexander Barkov
2017-02-02 22:59:07 +04:00
parent ffbb2bbc09
commit 72f43df623
42 changed files with 9226 additions and 303 deletions

View File

@@ -274,7 +274,7 @@ drop table option;
set option=1;
ERROR HY000: Unknown system variable 'option'
set option option=1;
ERROR HY000: Unknown system variable 'option'
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'option=1' at line 1
#
# MDEV-9979 Keywords UNBOUNDED, PRECEDING, FOLLOWING, TIES, OTHERS should be non-reserved
#

2122
mysql-test/r/sp-row.result Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -673,7 +673,6 @@ vb IS TRUE;
END|
call p1();
HEX(b) b = 0 b = FALSE b IS FALSE b = 1 b = TRUE b IS TRUE
0 1 1 1 0 0 0
1 0 0 0 1 1 1
call p2();

View File

@@ -672,9 +672,9 @@ select @a, @b;
@a @b
2 1
set @@global.global.key_buffer_size= 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'key_buffer_size= 1' at line 1
ERROR HY000: Unknown structured system variable or ROW routine variable 'global'
set GLOBAL global.key_buffer_size= 1;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'key_buffer_size= 1' at line 1
ERROR HY000: Unknown structured system variable or ROW routine variable 'global'
SELECT @@global.global.key_buffer_size;
ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'key_buffer_size' at line 1
SELECT @@global.session.key_buffer_size;

View File

@@ -0,0 +1,179 @@
SET sql_mode=ORACLE;
#
# MDEV-10914 ROW data type for stored routine variables
#
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1
AS
rec ROW(a INT,b INT);
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.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b INT)
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
AS
rec ROW(a INT,b INT);
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.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('rec.a',100), NAME_CONST('rec.b',200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,201))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,200)=ROW(NULL,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,201)=ROW(NULL,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE ROW(NULL,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(NULL,NULL)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(100,200)=ROW(NULL,NULL))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE ROW(NULL,NULL)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,NULL)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP TABLE "t1" /* generated by server */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP PROCEDURE p1
#
# Testing ROW fields in LIMIT
#
FLUSH LOGS;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(10);
CREATE TABLE t2 (a INT);
CREATE 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;
$$
CALL p1();
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
DROP TABLE t1,t2;
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
#
# End of MDEV-10914 ROW data type for stored routine variables
#

View File

@@ -0,0 +1,81 @@
--source include/not_embedded.inc
--source include/have_binlog_format_statement.inc
--disable_query_log
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
reset master; # get rid of previous tests binlog
--enable_query_log
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10914 ROW data type for stored routine variables
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b INT);
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;
--echo #
--echo # Testing ROW fields in LIMIT
--echo #
FLUSH LOGS;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(10);
CREATE TABLE t2 (a INT);
DELIMITER $$;
CREATE 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;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1,t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #

View File

@@ -0,0 +1,179 @@
SET sql_mode=ORACLE;
#
# MDEV-10914 ROW data type for stored routine variables
#
CREATE TABLE t1 (a INT, b INT);
CREATE PROCEDURE p1
AS
rec ROW(a INT,b INT);
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.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE TABLE t1 (a INT, b INT)
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; CREATE DEFINER="root"@"localhost" PROCEDURE "p1"()
AS
rec ROW(a INT,b INT);
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.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES ( NAME_CONST('rec.a',100), NAME_CONST('rec.b',200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (10, ROW(100,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 10, 20 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 10, 21 FROM DUAL WHERE ROW(100,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(NULL,200)=ROW(100,201))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,200)=ROW(NULL,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (11, ROW(100,201)=ROW(NULL,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 11, 20 FROM DUAL WHERE ROW(NULL,200)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 11, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(NULL,NULL)=ROW(100,200))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 VALUES (12, ROW(100,200)=ROW(NULL,NULL))
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 12, 20 FROM DUAL WHERE ROW(NULL,NULL)=ROW(100,200)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # BEGIN GTID #-#-#
master-bin.000001 # Query # # use `test`; INSERT INTO t1 SELECT 12, 21 FROM DUAL WHERE ROW(100,200)=ROW(NULL,NULL)
master-bin.000001 # Query # # COMMIT
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP TABLE "t1" /* generated by server */
master-bin.000001 # Gtid # # GTID #-#-#
master-bin.000001 # Query # # use `test`; DROP PROCEDURE p1
#
# Testing ROW fields in LIMIT
#
FLUSH LOGS;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(10);
CREATE TABLE t2 (a INT);
CREATE 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;
$$
CALL p1();
Warnings:
Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted
DROP TABLE t1,t2;
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
#
# End of MDEV-10914 ROW data type for stored routine variables
#

View File

@@ -141,7 +141,7 @@ INSERT INTO t1 VALUES (NULL, NULL, '');
INSERT INTO t1 VALUES (NULL, NULL, 'c');
INSERT INTO t1 VALUES (NULL, NULL, NULL);
SELECT LENGTH(a||b||c), a||b||c FROM t1 ORDER BY a,b,c;
LENGTH(a||b||c) a||b||c
LENGTH(a||b||c) a||b||c
NULL NULL
0
1 c

View File

@@ -887,3 +887,98 @@ DROP PROCEDURE p1;
#
# End of MDEV-10597 Cursors with parameters
#
#
# MDEV-10914 ROW data type for stored routine variables
#
CREATE FUNCTION f1() RETURN INT
AS
a ROW(a INT, b INT);
BEGIN
a.b:= 200;
RETURN a.b;
END;
$$
SHOW FUNCTION CODE f1;
Pos Instruction
0 set a@0 NULL
1 set a.b@0[1] 200
2 freturn 3 a.b@0[1]
SELECT f1();
f1()
200
DROP FUNCTION f1;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec:= ROW(10,20.123456,30.123,'test');
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set rec@0 NULL
1 set rec@0 (10,20.123456,30.123,'test')
2 stmt 0 "SELECT rec.a, rec.b, rec.c, rec.d"
CALL p1;
rec.a rec.b rec.c rec.d
10 20.123456 30.123 test
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
BEGIN
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set rec@0 (10,20.123456,30.123,'test')
1 stmt 0 "SELECT rec.a, rec.b, rec.c, rec.d"
CALL p1;
rec.a rec.b rec.c rec.d
10 20.123456 30.123 test
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec1:= ROW(10,20.123456,30.123,'test');
rec2:= rec1;
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set rec1@0 NULL
1 set rec2@1 NULL
2 set rec1@0 (10,20.123456,30.123,'test')
3 set rec2@1 rec1@0
4 stmt 0 "SELECT rec2.a, rec2.b, rec2.c, rec2.d"
CALL p1;
rec2.a rec2.b rec2.c rec2.d
10 20.123456 30.123 test
DROP PROCEDURE p1;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) := rec1;
BEGIN
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
SHOW PROCEDURE CODE p1;
Pos Instruction
0 set rec1@0 (10,20.123456,30.123,'test')
1 set rec2@1 rec1@0
2 stmt 0 "SELECT rec2.a, rec2.b, rec2.c, rec2.d"
CALL p1;
rec2.a rec2.b rec2.c rec2.d
10 20.123456 30.123 test
DROP PROCEDURE p1;
#
# End of MDEV-10914 ROW data type for stored routine variables
#

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,81 @@
--source include/not_embedded.inc
--source include/have_binlog_format_statement.inc
--disable_query_log
call mtr.add_suppression("Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT");
reset master; # get rid of previous tests binlog
--enable_query_log
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10914 ROW data type for stored routine variables
--echo #
CREATE TABLE t1 (a INT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b INT);
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;
--echo #
--echo # Testing ROW fields in LIMIT
--echo #
FLUSH LOGS;
CREATE TABLE t1 (a INT);
INSERT INTO t1 VALUES (10),(10);
CREATE TABLE t2 (a INT);
DELIMITER $$;
CREATE 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;
$$
DELIMITER ;$$
CALL p1();
DROP TABLE t1,t2;
DROP PROCEDURE p1;
--let $binlog_file = LAST
source include/show_binlog_events.inc;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #

View File

@@ -657,3 +657,88 @@ DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10597 Cursors with parameters
--echo #
--echo #
--echo # MDEV-10914 ROW data type for stored routine variables
--echo #
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
AS
a ROW(a INT, b INT);
BEGIN
a.b:= 200;
RETURN a.b;
END;
$$
DELIMITER ;$$
SHOW FUNCTION CODE f1;
SELECT f1();
DROP FUNCTION f1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec:= ROW(10,20.123456,30.123,'test');
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
BEGIN
SELECT rec.a, rec.b, rec.c, rec.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10));
BEGIN
rec1:= ROW(10,20.123456,30.123,'test');
rec2:= rec1;
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
rec1 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) :=
ROW(10,20.123456,30.123,'test');
rec2 ROW(a INT,b DOUBLE,c DECIMAL(10,3),d VARCHAR(10)) := rec1;
BEGIN
SELECT rec2.a, rec2.b, rec2.c, rec2.d;
END;
$$
DELIMITER ;$$
SHOW PROCEDURE CODE p1;
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10914 ROW data type for stored routine variables
--echo #

View File

@@ -0,0 +1,6 @@
--let $query= CREATE PROCEDURE p1() AS var $type; rec ROW(var $type); BEGIN CREATE TABLE t1 AS SELECT var,rec.var FROM DUAL;END
--eval $query
CALL p1();
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;

File diff suppressed because it is too large Load Diff

View File

@@ -171,7 +171,7 @@ create table option (option int not null);
drop table option;
--error 1193
set option=1;
--error 1193
--error ER_PARSE_ERROR
set option option=1;
--echo #

View File

@@ -0,0 +1,6 @@
--let $query= CREATE PROCEDURE p1() BEGIN DECLARE var $type; DECLARE rec ROW(var $type); CREATE TABLE t1 AS SELECT var,rec.var FROM DUAL;END
--eval $query
CALL p1();
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;

1329
mysql-test/t/sp-row.test Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -438,9 +438,9 @@ select @a, @b;
#
# Bug#2586:Disallow global/session/local as structured var. instance names
#
--error ER_PARSE_ERROR
--error ER_UNKNOWN_STRUCTURED_VARIABLE
set @@global.global.key_buffer_size= 1;
--error ER_PARSE_ERROR
--error ER_UNKNOWN_STRUCTURED_VARIABLE
set GLOBAL global.key_buffer_size= 1;
--error ER_PARSE_ERROR
SELECT @@global.global.key_buffer_size;

View File

@@ -10203,6 +10203,7 @@ bool Column_definition::check(THD *thd)
((length > max_field_charlength &&
sql_type != MYSQL_TYPE_VARCHAR) ||
(length == 0 &&
sql_type != MYSQL_TYPE_NULL /* e.g. a ROW variable */ &&
sql_type != MYSQL_TYPE_ENUM && sql_type != MYSQL_TYPE_SET &&
sql_type != MYSQL_TYPE_STRING && sql_type != MYSQL_TYPE_VARCHAR &&
sql_type != MYSQL_TYPE_GEOMETRY)))

View File

@@ -3957,12 +3957,48 @@ public:
};
/**
List of ROW element definitions, e.g.:
DECLARE a ROW(a INT,b VARCHAR(10))
*/
class Row_definition_list: public List<class Spvar_definition>
{
public:
inline bool eq_name(const Spvar_definition *def, const char *name) const;
/**
Find a ROW field by name.
@param [IN] name - the name
@param [OUT] offset - if the ROW field found, its offset it returned here
@retval NULL - the ROW field was not found
@retval !NULL - the pointer to the found ROW field
*/
Spvar_definition *find_row_field_by_name(const char *name, uint *offset) const
{
// Cast-off the "const" qualifier
List_iterator<Spvar_definition> it(*((List<Spvar_definition>*)this));
Spvar_definition *def;
for (*offset= 0; (def= it++); (*offset)++)
{
if (eq_name(def, name))
return def;
}
return 0;
}
};
/**
This class is used during a stored routine or a trigger execution,
at sp_rcontext::create() time.
Currently it can represent:
- variables with explicit data types: DECLARE a INT;
- variables with data type references: DECLARE a t1.a%TYPE;
- ROW type variables
Notes:
- Scalar variables have m_field_definitions==NULL.
- ROW variables are defined as having MYSQL_TYPE_NULL,
with a non-empty m_field_definitions.
Data type references to other object types will be added soon, e.g.:
- DECLARE a table_name%ROWTYPE;
@@ -3973,9 +4009,11 @@ public:
class Spvar_definition: public Column_definition
{
class Qualified_column_ident *m_column_type_ref; // for %TYPE
Row_definition_list *m_row_field_definitions; // for ROW
public:
Spvar_definition()
:m_column_type_ref(NULL) { }
:m_column_type_ref(NULL),
m_row_field_definitions(NULL) { }
bool is_column_type_ref() const { return m_column_type_ref != 0; }
class Qualified_column_ident *column_type_ref() const
{
@@ -3985,9 +4023,39 @@ public:
{
m_column_type_ref= ref;
}
/*
Find a ROW field by name.
See Row_field_list::find_row_field_by_name() for details.
*/
Spvar_definition *find_row_field_by_name(const char *name, uint *offset) const
{
DBUG_ASSERT(m_row_field_definitions);
return m_row_field_definitions->find_row_field_by_name(name, offset);
}
uint is_row() const
{
return m_row_field_definitions != NULL;
}
Row_definition_list *row_field_definitions() const
{
return m_row_field_definitions;
}
void set_row_field_definitions(Row_definition_list *list)
{
m_row_field_definitions= list;
}
};
inline bool Row_definition_list::eq_name(const Spvar_definition *def,
const char *name) const
{
return my_strcasecmp(system_charset_info, def->field_name, name) == 0;
}
class Create_field :public Column_definition
{
public:

View File

@@ -1653,6 +1653,126 @@ bool Item_splocal::set_value(THD *thd, sp_rcontext *ctx, Item **it)
}
/**
These two declarations are different:
x INT;
ROW(x INT);
A ROW with one elements should not be comparable to scalar value.
TODO: Currently we don't support one argument with the function ROW(), so
this query returns a syntax error, meaning that more arguments are expected:
SELECT ROW(1);
Therefore, all around the code we assume that cols()==1 means a scalar value
and cols()>1 means a ROW value. With adding ROW SP variables this
assumption is not true any more. ROW variables with one element are
now possible.
To implement Item::check_cols() correctly, we now should extend it to
know if a ROW or a scalar value is being tested. For example,
these new prototypes should work:
virtual bool check_cols(Item_result result, uint c);
or
virtual bool check_cols(const Type_handler *type, uint c);
The current implementation of Item_splocal::check_cols() is a compromise
that should be more or less fine until we extend check_cols().
It disallows ROW variables to appear in a scalar context.
The "|| n == 1" part of the conditon is responsible for this.
For example, it disallows ROW variables to appear in SELECT list:
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a ROW (a INT);
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
--error ER_OPERAND_COLUMNS
CALL p1();
But is produces false negatives with ROW variables consisting of one element.
For example, this script fails:
SET sql_mode=ORACLE;
DROP PROCEDURE IF EXISTS p1;
DELIMITER $$
CREATE PROCEDURE p1
AS
a ROW(a INT);
b ROW(a INT);
BEGIN
SELECT a=b;
END;
$$
DELIMITER ;
CALL p1();
and returns "ERROR 1241 (21000): Operand should contain 1 column(s)".
This will be fixed that we change check_cols().
*/
bool Item_splocal::check_cols(uint n)
{
DBUG_ASSERT(m_thd->spcont);
if (cmp_type() != ROW_RESULT)
return Item::check_cols(n);
if (n != this_item()->cols() || n == 1)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), n);
return true;
}
return false;
}
Item *
Item_splocal_row_field::this_item()
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx);
}
const Item *
Item_splocal_row_field::this_item() const
{
DBUG_ASSERT(m_sp == m_thd->spcont->sp);
return m_thd->spcont->get_item(m_var_idx)->element_index(m_field_idx);
}
Item **
Item_splocal_row_field::this_item_addr(THD *thd, Item **)
{
DBUG_ASSERT(m_sp == thd->spcont->sp);
return thd->spcont->get_item(m_var_idx)->addr(m_field_idx);
}
void Item_splocal_row_field::print(String *str, enum_query_type)
{
str->reserve(m_name.length + 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->qs_append(m_var_idx);
str->append('[');
str->qs_append(m_field_idx);
str->append(']');
}
bool Item_splocal_row_field::set_value(THD *thd, sp_rcontext *ctx, Item **it)
{
return ctx->set_variable_row_field(thd, m_var_idx, m_field_idx, it);
}
/*****************************************************************************
Item_case_expr methods
*****************************************************************************/

View File

@@ -1843,6 +1843,94 @@ inline Item* get_item_copy (THD *thd, MEM_ROOT *mem_root, T* item)
bool cmp_items(Item *a, Item *b);
/**
Array of items, e.g. function or aggerate function arguments.
*/
class Item_args
{
protected:
Item **args, *tmp_arg[2];
uint arg_count;
bool alloc_arguments(THD *thd, uint count);
void set_arguments(THD *thd, List<Item> &list);
bool walk_args(Item_processor processor, bool walk_subquery, void *arg)
{
for (uint i= 0; i < arg_count; i++)
{
if (args[i]->walk(processor, walk_subquery, arg))
return true;
}
return false;
}
bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
public:
Item_args(void)
:args(NULL), arg_count(0)
{ }
Item_args(Item *a)
:args(tmp_arg), arg_count(1)
{
args[0]= a;
}
Item_args(Item *a, Item *b)
:args(tmp_arg), arg_count(2)
{
args[0]= a; args[1]= b;
}
Item_args(THD *thd, Item *a, Item *b, Item *c)
{
arg_count= 0;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3)))
{
arg_count= 3;
args[0]= a; args[1]= b; args[2]= c;
}
}
Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d)
{
arg_count= 0;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4)))
{
arg_count= 4;
args[0]= a; args[1]= b; args[2]= c; args[3]= d;
}
}
Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e)
{
arg_count= 5;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5)))
{
arg_count= 5;
args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
}
}
Item_args(THD *thd, List<Item> &list)
{
set_arguments(thd, list);
}
Item_args(THD *thd, const Item_args *other);
inline Item **arguments() const { return args; }
inline uint argument_count() const { return arg_count; }
inline void remove_arguments() { arg_count=0; }
};
class Item_spvar_args: public Item_args
{
TABLE *m_table;
public:
Item_spvar_args():Item_args(), m_table(NULL) { }
~Item_spvar_args();
bool row_create_items(THD *thd, List<Spvar_definition> *list);
Field *get_row_field(uint i) const
{
DBUG_ASSERT(m_table);
return m_table->field[i];
}
};
/*
Class to be used to enumerate all field references in an item tree. This
includes references to outside but not fields of the tables within a
@@ -2076,9 +2164,12 @@ class Item_splocal :public Item_sp_variable,
public Rewritable_query_parameter,
public Type_handler_hybrid_field_type
{
protected:
uint m_var_idx;
Type m_type;
bool append_value_for_log(THD *thd, String *str);
public:
Item_splocal(THD *thd, const LEX_STRING &sp_var_name, uint sp_var_idx,
enum_field_types sp_var_type,
@@ -2104,6 +2195,10 @@ public:
{ return Type_handler_hybrid_field_type::result_type(); }
enum Item_result cmp_type () const
{ return Type_handler_hybrid_field_type::cmp_type(); }
uint cols() { return this_item()->cols(); }
Item* element_index(uint i) { return this_item()->element_index(i); }
Item** addr(uint i) { return this_item()->addr(i); }
bool check_cols(uint c);
private:
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
@@ -2123,6 +2218,20 @@ public:
};
class Item_splocal_row: public Item_splocal
{
public:
Item_splocal_row(THD *thd, const LEX_STRING &sp_var_name,
uint sp_var_idx, uint pos_in_q, uint len_in_q)
:Item_splocal(thd, sp_var_name, sp_var_idx, MYSQL_TYPE_NULL,
pos_in_q, len_in_q)
{
set_handler(&type_handler_row);
}
enum Type type() const { return ROW_ITEM; }
};
/**
An Item_splocal variant whose data type becomes known only at
sp_rcontext creation time, e.g. "DECLARE var1 t1.col1%TYPE".
@@ -2156,6 +2265,37 @@ public:
{ return tmp_table_field_from_field_type(table, false, true); }
};
/**
SP variables that are fields of a ROW.
DELCARE r ROW(a INT,b INT);
SELECT r.a; -- This is handled by Item_splocal_row_field
*/
class Item_splocal_row_field :public Item_splocal
{
LEX_STRING m_field_name;
uint m_field_idx;
bool set_value(THD *thd, sp_rcontext *ctx, Item **it);
public:
Item_splocal_row_field(THD *thd,
const LEX_STRING &sp_var_name,
const LEX_STRING &sp_field_name,
uint sp_var_idx, uint sp_field_idx,
enum_field_types sp_var_type,
uint pos_in_q= 0, uint len_in_q= 0)
:Item_splocal(thd, sp_var_name, sp_var_idx, sp_var_type,
pos_in_q, len_in_q),
m_field_name(sp_field_name),
m_field_idx(sp_field_idx)
{ }
Item *this_item();
const Item *this_item() const;
Item **this_item_addr(THD *thd, Item **);
bool append_for_log(THD *thd, String *str);
void print(String *str, enum_query_type query_type);
};
/*****************************************************************************
Item_splocal inline implementation.
*****************************************************************************/
@@ -2599,6 +2739,39 @@ public:
};
/**
Item_field for the ROW data type
*/
class Item_field_row: public Item_field,
public Item_spvar_args
{
public:
Item_field_row(THD *thd, Field *field)
:Item_field(thd, field),
Item_spvar_args()
{ }
Item *get_copy(THD *thd, MEM_ROOT *mem_root)
{ return get_item_copy<Item_field_row>(thd, mem_root, this); }
const Type_handler *type_handler() const { return &type_handler_row; }
Item_result result_type() const{ return ROW_RESULT ; }
Item_result cmp_type() const { return ROW_RESULT; }
uint cols() { return arg_count; }
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)
{
if (cols() != c)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), c);
return true;
}
return false;
}
};
/*
@brief
Item_temptable_field is the same as Item_field, except that print()
@@ -3785,78 +3958,6 @@ public:
};
/**
Array of items, e.g. function or aggerate function arguments.
*/
class Item_args
{
protected:
Item **args, *tmp_arg[2];
uint arg_count;
void set_arguments(THD *thd, List<Item> &list);
bool walk_args(Item_processor processor, bool walk_subquery, void *arg)
{
for (uint i= 0; i < arg_count; i++)
{
if (args[i]->walk(processor, walk_subquery, arg))
return true;
}
return false;
}
bool transform_args(THD *thd, Item_transformer transformer, uchar *arg);
void propagate_equal_fields(THD *, const Item::Context &, COND_EQUAL *);
public:
Item_args(void)
:args(NULL), arg_count(0)
{ }
Item_args(Item *a)
:args(tmp_arg), arg_count(1)
{
args[0]= a;
}
Item_args(Item *a, Item *b)
:args(tmp_arg), arg_count(2)
{
args[0]= a; args[1]= b;
}
Item_args(THD *thd, Item *a, Item *b, Item *c)
{
arg_count= 0;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 3)))
{
arg_count= 3;
args[0]= a; args[1]= b; args[2]= c;
}
}
Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d)
{
arg_count= 0;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 4)))
{
arg_count= 4;
args[0]= a; args[1]= b; args[2]= c; args[3]= d;
}
}
Item_args(THD *thd, Item *a, Item *b, Item *c, Item *d, Item* e)
{
arg_count= 5;
if ((args= (Item**) thd_alloc(thd, sizeof(Item*) * 5)))
{
arg_count= 5;
args[0]= a; args[1]= b; args[2]= c; args[3]= d; args[4]= e;
}
}
Item_args(THD *thd, List<Item> &list)
{
set_arguments(thd, list);
}
Item_args(THD *thd, const Item_args *other);
inline Item **arguments() const { return args; }
inline uint argument_count() const { return arg_count; }
inline void remove_arguments() { arg_count=0; }
};
class Used_tables_and_const_cache
{
public:

View File

@@ -89,23 +89,35 @@ static inline bool test_if_sum_overflows_ull(ulonglong arg1, ulonglong arg2)
}
void Item_args::set_arguments(THD *thd, List<Item> &list)
/**
Allocate memory for arguments using tmp_args or thd->alloc().
@retval false - success
@retval true - error (arg_count is set to 0 for conveniece)
*/
bool Item_args::alloc_arguments(THD *thd, uint count)
{
arg_count= list.elements;
if (arg_count <= 2)
if (count <= 2)
{
args= tmp_arg;
return false;
}
else if (!(args= (Item**) thd->alloc(sizeof(Item*) * arg_count)))
if ((args= (Item**) thd->alloc(sizeof(Item*) * count)) == NULL)
{
arg_count= 0;
return;
return true;
}
uint i= 0;
return false;
}
void Item_args::set_arguments(THD *thd, List<Item> &list)
{
if (alloc_arguments(thd, list.elements))
return;
List_iterator_fast<Item> li(list);
Item *item;
while ((item= li++))
args[i++]= item;
for (arg_count= 0; (item= li++); )
args[arg_count++]= item;
}
@@ -138,6 +150,19 @@ void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
}
bool Item_func::check_allowed_arg_cols(uint n)
{
if (allowed_arg_cols)
return args[n]->check_cols(allowed_arg_cols);
/* we have to fetch allowed_arg_cols from first argument */
DBUG_ASSERT(n == 0); // it is first argument
allowed_arg_cols= args[n]->cols();
DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more
return false;
}
/*
Resolve references to table column for a function and its argument
@@ -210,18 +235,8 @@ Item_func::fix_fields(THD *thd, Item **ref)
return TRUE; /* purecov: inspected */
item= *arg;
if (allowed_arg_cols)
{
if (item->check_cols(allowed_arg_cols))
return 1;
}
else
{
/* we have to fetch allowed_arg_cols from first argument */
DBUG_ASSERT(arg == args); // it is first argument
allowed_arg_cols= item->cols();
DBUG_ASSERT(allowed_arg_cols); // Can't be 0 any more
}
if (check_allowed_arg_cols(arg - args))
return true;
if (item->maybe_null)
maybe_null=1;

View File

@@ -53,6 +53,7 @@ protected:
set_if_bigger(res, item[i]->decimals);
return res;
}
virtual bool check_allowed_arg_cols(uint argno);
public:
void aggregate_attributes_int(Item **items, uint nitems)
{
@@ -2547,7 +2548,12 @@ private:
protected:
bool is_expensive_processor(void *arg)
{ return is_expensive(); }
bool check_allowed_arg_cols(uint n)
{
// sp_prepare_func_item() checks that the number of columns is correct
return false;
}
public:
Item_func_sp(THD *thd, Name_resolution_context *context_arg, sp_name *name);

View File

@@ -7460,3 +7460,5 @@ ER_ILLEGAL_PARAMETER_DATA_TYPE_FOR_OPERATION
ER_WRONG_PARAMCOUNT_TO_CURSOR 42000
eng "Incorrect parameter count to cursor '%-.192s'"
rus "Некорректное количество параметров для курсора '%-.192s'"
ER_UNKNOWN_STRUCTURED_VARIABLE
eng "Unknown structured system variable or ROW routine variable '%-.*s'"

View File

@@ -100,19 +100,51 @@ bool Item_splocal::append_for_log(THD *thd, String *str)
if (limit_clause_param)
return str->append_ulonglong(val_uint());
/*
ROW variables are currently not allowed in select_list, e.g.:
SELECT row_variable;
ROW variables can appear in query parts where name is not important, e.g.:
SELECT ROW(1,2)=row_variable FROM t1;
So we can skip using NAME_CONST() and use ROW() constants directly.
*/
if (type_handler() == &type_handler_row)
return append_value_for_log(thd, str);
if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
str->append(&m_name) ||
str->append(STRING_WITH_LEN("',")))
return true;
return append_value_for_log(thd, str) || str->append(')');
}
bool Item_splocal::append_value_for_log(THD *thd, String *str)
{
StringBuffer<STRING_BUFFER_USUAL_SIZE> str_value_holder(&my_charset_latin1);
Item *item= this_item();
String *str_value= item->type_handler()->print_item_value(thd, item,
&str_value_holder);
if (str_value)
return str->append(*str_value) || str->append(')');
else
return str->append(STRING_WITH_LEN("NULL)"));
return str_value ?
str->append(*str_value) :
str->append(STRING_WITH_LEN("NULL"));
}
bool Item_splocal_row_field::append_for_log(THD *thd, String *str)
{
if (fix_fields(thd, NULL))
return true;
if (limit_clause_param)
return str->append_ulonglong(val_uint());
if (str->append(STRING_WITH_LEN(" NAME_CONST('")) ||
str->append(&m_name) ||
str->append(".") ||
str->append(&m_field_name) ||
str->append(STRING_WITH_LEN("',")))
return true;
return append_value_for_log(thd, str) || str->append(')');
}
@@ -308,14 +340,14 @@ sp_get_flags_for_command(LEX *lex)
*/
Item *
sp_prepare_func_item(THD* thd, Item **it_addr)
sp_prepare_func_item(THD* thd, Item **it_addr, uint cols)
{
DBUG_ENTER("sp_prepare_func_item");
it_addr= (*it_addr)->this_item_addr(thd, it_addr);
if (!(*it_addr)->fixed &&
((*it_addr)->fix_fields(thd, it_addr) ||
(*it_addr)->check_cols(1)))
if ((!(*it_addr)->fixed &&
(*it_addr)->fix_fields(thd, it_addr)) ||
(*it_addr)->check_cols(cols))
{
DBUG_PRINT("info", ("fix_fields() failed"));
DBUG_RETURN(NULL);
@@ -338,7 +370,8 @@ sp_prepare_func_item(THD* thd, Item **it_addr)
*/
bool
sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
sp_eval_expr(THD *thd, Item *result_item, Field *result_field,
Item **expr_item_ptr)
{
Item *expr_item;
enum_check_fields save_count_cuted_fields= thd->count_cuted_fields;
@@ -351,9 +384,20 @@ sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr)
if (!*expr_item_ptr)
goto error;
if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr)))
if (!(expr_item= sp_prepare_func_item(thd, expr_item_ptr,
result_item ? result_item->cols() : 1)))
goto error;
/*
expr_item is now fixed, it's safe to call cmp_type()
If result_item is NULL, then we're setting the RETURN value.
*/
if (!result_item && expr_item->cmp_type() == ROW_RESULT)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), 1);
goto error;
}
/*
Set THD flags to emit warnings/errors in case of overflow/type errors
during saving the item into the field.
@@ -3184,6 +3228,56 @@ sp_instr_set::print(String *str)
}
/*
sp_instr_set_field class functions
*/
int
sp_instr_set_row_field::exec_core(THD *thd, uint *nextp)
{
int res= thd->spcont->set_variable_row_field(thd, m_offset, m_field_offset,
&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, m_field_offset);
}
delete_explain_query(thd->lex);
*nextp= m_ip + 1;
return res;
}
void
sp_instr_set_row_field::print(String *str)
{
/* set name@offset[field_offset] ... */
int rsrv= SP_INSTR_UINT_MAXLEN + 6 + 6 + 3;
sp_variable *var= m_ctx->find_variable(m_offset);
DBUG_ASSERT(var);
DBUG_ASSERT(var->field_def.is_row());
const Column_definition *def=
var->field_def.row_field_definitions()->elem(m_field_offset);
DBUG_ASSERT(def);
rsrv+= var->name.length + strlen(def->field_name);
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(def->field_name);
str->qs_append('@');
str->qs_append(m_offset);
str->qs_append('[');
str->qs_append(m_field_offset);
str->qs_append(']');
str->qs_append(' ');
m_value->print(str, enum_query_type(QT_ORDINARY |
QT_ITEM_ORIGINAL_FUNC_NULLIF));
}
/*
sp_instr_set_trigger_field class functions
*/
@@ -4209,6 +4303,11 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
}
Item *sp_head::adjust_assignment_source(THD *thd, Item *val, Item *val2)
{
return val ? val : val2 ? val2 : new (thd->mem_root) Item_null(thd);
}
/**
Helper action for a SET statement.
Used to push a SP local variable into the assignment list.
@@ -4223,28 +4322,40 @@ bool
sp_head::set_local_variable(THD *thd, sp_pcontext *spcont,
sp_variable *spv, Item *val, LEX *lex)
{
Item *it;
if (val)
it= val;
else if (spv->default_value)
it= spv->default_value;
else
{
it= new (thd->mem_root) Item_null(thd);
if (it == NULL)
return TRUE;
}
if (!(val= adjust_assignment_source(thd, val, spv->default_value)))
return true;
sp_instr_set *sp_set= new (thd->mem_root)
sp_instr_set(instructions(), spcont,
spv->offset, it, spv->sql_type(),
spv->offset, val, spv->sql_type(),
lex, true);
return sp_set == NULL || add_instr(sp_set);
}
/**
Similar to set_local_variable(), but for ROW variable fields.
*/
bool
sp_head::set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex)
{
if (!(val= adjust_assignment_source(thd, val, NULL)))
return true;
sp_instr_set_row_field *sp_set= new (thd->mem_root)
sp_instr_set_row_field(instructions(),
spcont,
spv->offset,
field_idx, val,
spv->sql_type(),
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

@@ -360,9 +360,12 @@ public:
spcont->last_label());
}
Item *adjust_assignment_source(THD *thd, Item *val, Item *val2);
bool set_local_variable(THD *thd, sp_pcontext *spcont,
sp_variable *spv, Item *val, LEX *lex);
bool set_local_variable_row_field(THD *thd, sp_pcontext *spcont,
sp_variable *spv, uint field_idx,
Item *val, LEX *lex);
private:
/**
Generate a code to set a single cursor parameter variable.
@@ -992,7 +995,7 @@ public:
virtual void print(String *str);
private:
protected:
uint m_offset; ///< Frame offset
Item *m_value;
@@ -1002,6 +1005,36 @@ private:
}; // class sp_instr_set : public sp_instr
/*
This class handles assignments of a ROW fields:
DECLARE rec ROW (a INT,b INT);
SET rec.a= 10;
*/
class sp_instr_set_row_field : public sp_instr_set
{
sp_instr_set_row_field(const sp_instr_set_row_field &); // Prevent use of this
void operator=(sp_instr_set_row_field &);
uint m_field_offset;
public:
sp_instr_set_row_field(uint ip, sp_pcontext *ctx,
uint offset, uint field_offset,
Item *val, enum enum_field_types type_arg,
LEX *lex, bool lex_resp)
: sp_instr_set(ip, ctx, offset, val, type_arg, lex, lex_resp),
m_field_offset(field_offset)
{}
virtual ~sp_instr_set_row_field()
{}
virtual int exec_core(THD *thd, uint *nextp);
virtual void print(String *str);
}; // class sp_instr_set_field : public sp_instr_set
/**
Set NEW/OLD row field value instruction. Used in triggers.
*/
@@ -1603,10 +1636,11 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
enum_mdl_type mdl_type);
Item *
sp_prepare_func_item(THD* thd, Item **it_addr);
sp_prepare_func_item(THD* thd, Item **it_addr, uint cols= 1);
bool
sp_eval_expr(THD *thd, Field *result_field, Item **expr_item_ptr);
sp_eval_expr(THD *thd, Item *result_item, Field *result_field,
Item **expr_item_ptr);
/**
@} (end of group Stored_Routines)

View File

@@ -563,3 +563,24 @@ const sp_pcursor *sp_pcontext::find_cursor(uint offset) const
m_parent->find_cursor(offset) : // Some previous frame
NULL; // Index out of bounds
}
const Spvar_definition *
sp_variable::find_row_field(const LEX_STRING &var_name,
const LEX_STRING &field_name,
uint *row_field_offset)
{
if (!field_def.is_row())
{
my_printf_error(ER_UNKNOWN_ERROR,
"'%s' is not a row variable", MYF(0), var_name.str);
return NULL;
}
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);
return NULL;
}

View File

@@ -69,6 +69,20 @@ public:
offset(_offset),
default_value(NULL)
{ }
/*
Find a ROW field by its qualified name.
@param var_name - the name of the variable
@param field_name - the name of the variable field
@param[OUT] row_field_offset - the index of the field
@retval NULL if the variable with the given name was not found,
or it is not a row variable, or it does not have a field
with the given name, or a non-null pointer otherwise.
row_field_offset[0] is set only when the method returns !NULL.
*/
const Spvar_definition *find_row_field(const LEX_STRING &var_name,
const LEX_STRING &field_name,
uint *row_field_offset);
};
///////////////////////////////////////////////////////////////////////////

View File

@@ -222,23 +222,65 @@ bool sp_rcontext::init_var_items(THD *thd,
if (!m_var_items.array())
return true;
for (uint idx = 0; idx < num_vars; ++idx)
DBUG_ASSERT(field_def_lst.elements == num_vars);
List_iterator<Spvar_definition> it(field_def_lst);
Spvar_definition *def= it++;
for (uint idx= 0; idx < num_vars; ++idx, def= it++)
{
if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, m_var_table->field[idx])))
Field *field= m_var_table->field[idx];
if (def->is_row())
{
Item_field_row *item= new (thd->mem_root) Item_field_row(thd, field);
if (!(m_var_items[idx]= item) ||
item->row_create_items(thd, def->row_field_definitions()))
return true;
}
else
{
if (!(m_var_items[idx]= new (thd->mem_root) Item_field(thd, field)))
return true;
}
}
return false;
}
bool Item_spvar_args::row_create_items(THD *thd, List<Spvar_definition> *list)
{
DBUG_ASSERT(list);
if (!(m_table= create_virtual_tmp_table(thd, *list)))
return true;
if (alloc_arguments(thd, list->elements))
return true;
List_iterator<Spvar_definition> it(*list);
Spvar_definition *def;
for (arg_count= 0; (def= it++); arg_count++)
{
if (!(args[arg_count]= new (thd->mem_root)
Item_field(thd, m_table->field[arg_count])))
return true;
}
return false;
}
Item_spvar_args::~Item_spvar_args()
{
if (m_table)
free_blobs(m_table);
}
bool sp_rcontext::set_return_value(THD *thd, Item **return_value_item)
{
DBUG_ASSERT(m_return_value_fld);
m_return_value_set = true;
return sp_eval_expr(thd, m_return_value_fld, return_value_item);
return sp_eval_expr(thd, NULL, m_return_value_fld, return_value_item);
}
@@ -455,8 +497,78 @@ int sp_rcontext::set_variable(THD *thd, uint idx, Item **value)
field->set_null();
return 0;
}
Item *dst= m_var_items[idx];
return sp_eval_expr(thd, field, value);
if (dst->cmp_type() != ROW_RESULT)
return sp_eval_expr(thd, dst, m_var_table->field[idx], value);
DBUG_ASSERT(dst->type() == Item::FIELD_ITEM);
if (value[0]->type() == Item::NULL_ITEM)
{
/*
We're in a auto-generated sp_inst_set, to assign
the explicit default NULL value to a ROW variable.
*/
for (uint i= 0; i < dst->cols(); i++)
{
Item_field_row *item_field_row= (Item_field_row*) dst;
item_field_row->get_row_field(i)->set_null();
}
return false;
}
/**
- In case if we're assigning a ROW variable from another ROW variable,
value[0] points to Item_splocal. sp_prepare_func_item() will return the
fixed underlying Item_field_spvar with ROW members in its aguments().
- In case if we're assigning from a ROW() value, src and value[0] will
point to the same Item_row.
*/
Item *src;
if (!(src= sp_prepare_func_item(thd, value, dst->cols())) ||
src->cmp_type() != ROW_RESULT)
{
my_error(ER_OPERAND_COLUMNS, MYF(0), dst->cols());
return true;
}
DBUG_ASSERT(dst->cols() == src->cols());
for (uint i= 0; i < src->cols(); i++)
set_variable_row_field(thd, idx, i, src->addr(i));
return false;
}
void sp_rcontext::set_variable_row_field_to_null(THD *thd,
uint var_idx,
uint field_idx)
{
Item *dst= get_item(var_idx);
DBUG_ASSERT(dst->type() == Item::FIELD_ITEM);
DBUG_ASSERT(dst->cmp_type() == ROW_RESULT);
Item_field_row *item_field_row= (Item_field_row*) dst;
item_field_row->get_row_field(field_idx)->set_null();
}
int sp_rcontext::set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
Item **value)
{
DBUG_ASSERT(value);
Item *dst= get_item(var_idx);
DBUG_ASSERT(dst->type() == Item::FIELD_ITEM);
DBUG_ASSERT(dst->cmp_type() == ROW_RESULT);
Item_field_row *item_field_row= (Item_field_row*) dst;
Item *expr_item= sp_prepare_func_item(thd, value);
if (!expr_item)
{
DBUG_ASSERT(thd->is_error());
return true;
}
return sp_eval_expr(thd,
item_field_row->arguments()[field_idx],
item_field_row->get_row_field(field_idx),
value);
}

View File

@@ -187,7 +187,9 @@ public:
/////////////////////////////////////////////////////////////////////////
int set_variable(THD *thd, uint var_idx, Item **value);
void set_variable_row_field_to_null(THD *thd, uint var_idx, uint field_idx);
int set_variable_row_field(THD *thd, uint var_idx, uint field_idx,
Item **value);
Item *get_item(uint var_idx) const
{ return m_var_items[var_idx]; }

View File

@@ -3839,6 +3839,11 @@ bool my_var_sp::set(THD *thd, Item *item)
return thd->spcont->set_variable(thd, offset, &item);
}
bool my_var_sp_row_field::set(THD *thd, Item *item)
{
return thd->spcont->set_variable_row_field(thd, offset, m_field_offset, &item);
}
int select_dumpvar::send_data(List<Item> &items)
{
List_iterator_fast<my_var> var_li(var_list);

View File

@@ -5522,6 +5522,22 @@ public:
bool set(THD *thd, Item *val);
};
/*
This class handles fields of a ROW SP variable when it's used as a OUT
parameter in a stored procedure.
*/
class my_var_sp_row_field: public my_var_sp
{
uint m_field_offset;
public:
my_var_sp_row_field(const LEX_STRING &varname, const LEX_STRING &fieldname,
uint var_idx, uint field_idx, sp_head *s)
:my_var_sp(varname, var_idx, MYSQL_TYPE_DOUBLE/*Not really used*/, s),
m_field_offset(field_idx)
{ }
bool set(THD *thd, Item *val);
};
class my_var_user: public my_var {
public:
my_var_user(const LEX_STRING& j)

View File

@@ -5153,7 +5153,8 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable,
{
if (check_reserved_words(&dbname))
{
thd->parse_error();
my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
(int) dbname.length, dbname.str);
return true;
}
if (is_trigger_new_or_old_reference(dbname))
@@ -5179,9 +5180,13 @@ bool LEX::init_internal_variable(struct sys_var_with_base *variable,
return false;
}
sys_var *tmp= find_sys_var(thd, name.str, name.length);
sys_var *tmp= find_sys_var_ex(thd, name.str, name.length, true, false);
if (!tmp)
{
my_error(ER_UNKNOWN_STRUCTURED_VARIABLE, MYF(0),
(int) dbname.length, dbname.str);
return true;
}
if (!tmp->is_struct())
my_error(ER_VARIABLE_IS_NOT_STRUCT, MYF(0), name.str);
variable->var= tmp;
@@ -5219,6 +5224,7 @@ void LEX::sp_variable_declarations_init(THD *thd, int nvars)
bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition *cdef,
Row_definition_list *row,
Item *dflt_value_item)
{
uint num_vars= spcont->context_var_count();
@@ -5226,7 +5232,31 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
if (!dflt_value_item &&
!(dflt_value_item= new (thd->mem_root) Item_null(thd)))
return true;
/* QQ Set to the var_type with null_value? */
if (row)
{
/*
Prepare all row fields. This will (among other things)
- convert VARCHAR lengths from character length to octet length
- calculate interval lengths for SET and ENUM
Note, we do it only one time outside of the below loop.
The converted list in "row" is further reused by all variable
declarations processed by the current call.
Example:
DECLARE
a, b, c ROW(x VARCHAR(10) CHARACTER SET utf8);
BEGIN
...
END;
*/
List_iterator<Spvar_definition> it(*row);
for (Spvar_definition *def= it++; def; def= it++)
{
def->pack_flag|= FIELDFLAG_MAYBE_NULL;
if (sphead->fill_field_definition(thd, def))
return true;
}
}
for (uint i= num_vars - nvars ; i < num_vars ; i++)
{
@@ -5246,6 +5276,7 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
if (sphead->fill_spvar_definition(thd, &spvar->field_def, spvar->name.str))
return true;
}
spvar->field_def.set_row_field_definitions(row);
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
@@ -5274,7 +5305,7 @@ LEX::sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
spvar->field_def.field_name= spvar->name.str;
}
sphead->m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
return sp_variable_declarations_finalize(thd, nvars, NULL, def);
return sp_variable_declarations_finalize(thd, nvars, NULL, NULL, def);
}
@@ -5315,7 +5346,7 @@ sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_STRING name,
*/
spvar->field_def.pack_flag= (FIELDFLAG_NUMBER |
f_settype((uint) MYSQL_TYPE_LONGLONG));
if (sp_variable_declarations_finalize(thd, 1, NULL, value))
if (sp_variable_declarations_finalize(thd, 1, NULL, NULL, value))
return NULL;
return spvar;
}
@@ -5953,11 +5984,9 @@ Item_param *LEX::add_placeholder(THD *thd, char *name,
}
Item_param *LEX::add_placeholder(THD *thd, char *name,
const char *pos, const char *end)
const char *LEX::substatement_query(THD *thd) const
{
const char *query_start= sphead ? sphead->m_tmp_query : thd->query();
return add_placeholder(thd, name, pos - query_start, end - pos);
return sphead ? sphead->m_tmp_query : thd->query();
}
@@ -5979,6 +6008,160 @@ bool LEX::add_resignal_statement(THD *thd, const sp_condition_value *v)
}
Item *LEX::create_item_ident_nospvar(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b)
{
DBUG_ASSERT(this == thd->lex);
/*
FIXME This will work ok in simple_ident_nospvar case because
we can't meet simple_ident_nospvar in trigger now. But it
should be changed in future.
*/
if (is_trigger_new_or_old_reference(a))
{
bool new_row= (a.str[0]=='N' || a.str[0]=='n');
return create_and_link_Item_trigger_field(thd, b.str, new_row);
}
if (current_select->no_table_names_allowed)
{
my_error(ER_TABLENAME_NOT_ALLOWED_HERE, MYF(0), a.str, thd->where);
return NULL;
}
if ((current_select->parsing_place != IN_HAVING) ||
(current_select->get_in_sum_expr() > 0))
return new (thd->mem_root) Item_field(thd, current_context(),
NullS, a.str, b.str);
return new (thd->mem_root) Item_ref(thd, current_context(),
NullS, a.str, b.str);
}
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)
{
if (!parsing_options.allows_variable)
{
my_error(ER_VIEW_SELECT_VARIABLE, MYF(0));
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_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;
#ifndef DBUG_OFF
item->m_sp= sphead;
#endif
safe_to_cache_query=0;
return item;
}
my_var *LEX::create_outvar(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b)
{
sp_variable *t;
if (!spcont || !(t= spcont->find_variable(a, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a.str);
return NULL;
}
uint row_field_offset;
if (!t->find_row_field(a, b, &row_field_offset))
return NULL;
return result ?
new (thd->mem_root) my_var_sp_row_field(a, b, t->offset,
row_field_offset, sphead) :
NULL;
}
Item *LEX::create_item_ident(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b,
uint pos_in_q, uint length_in_q)
{
sp_variable *spv;
if (spcont && (spv= spcont->find_variable(a, false)))
return create_item_spvar_row_field(thd, a, b, spv, pos_in_q, length_in_q);
return create_item_ident_nospvar(thd, a, b);
}
Item *LEX::create_item_limit(THD *thd,
const LEX_STRING &a,
uint pos_in_q, uint length_in_q)
{
sp_variable *spv;
if (!spcont || !(spv= spcont->find_variable(a, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a.str);
return NULL;
}
Item_splocal *item;
if (!(item= new (thd->mem_root) Item_splocal(thd, a,
spv->offset, spv->sql_type(),
pos_in_q, length_in_q)))
return NULL;
#ifndef DBUG_OFF
item->m_sp= sphead;
#endif
safe_to_cache_query= 0;
if (item->type() != Item::INT_ITEM)
{
my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
return NULL;
}
item->limit_clause_param= true;
return item;
}
Item *LEX::create_item_limit(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b,
uint pos_in_q, uint length_in_q)
{
sp_variable *spv;
if (!spcont || !(spv= spcont->find_variable(a, false)))
{
my_error(ER_SP_UNDECLARED_VAR, MYF(0), a.str);
return NULL;
}
// Qualified %TYPE variables are not possible
DBUG_ASSERT(!spv->field_def.column_type_ref());
Item_splocal *item;
if (!(item= create_item_spvar_row_field(thd, a, b, spv,
pos_in_q, length_in_q)))
return NULL;
if (item->type() != Item::INT_ITEM)
{
my_error(ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0));
return NULL;
}
item->limit_clause_param= true;
return item;
}
/*
Perform assignment for a trigger, a system variable, or an SP variable.
"variable" be previously set by init_internal_variable(variable, name).
@@ -6040,6 +6223,9 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_STRING name,
spv->offset,
start_in_q,
length_in_q) :
spv->field_def.is_row() ?
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,
spv->offset, spv->sql_type(),
start_in_q, length_in_q);
@@ -6073,6 +6259,32 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_STRING name,
}
/**
Generate instructions for:
SET x.y= expr;
*/
bool LEX::set_variable(const LEX_STRING &name1,
const LEX_STRING &name2,
Item *item)
{
sp_variable *spv;
if (spcont && (spv= spcont->find_variable(name1, false)))
{
// A field of a ROW variable
uint row_field_offset;
return !spv->find_row_field(name1, name2, &row_field_offset) ||
sphead->set_local_variable_row_field(thd, spcont,
spv, row_field_offset,
item, this);
}
// A trigger field or a system variable
sys_var_with_base sysvar;
return init_internal_variable(&sysvar, name1, name2) ||
set_variable(&sysvar, item);
}
#ifdef MYSQL_SERVER
uint binlog_unsafe_map[256];

View File

@@ -96,6 +96,7 @@ class Key_part_spec;
class Item_window_func;
struct sql_digest_state;
class With_clause;
class my_var;
#define ALLOC_ROOT_SET 1024
@@ -2226,6 +2227,20 @@ public:
return m_cpp_tok_end;
}
/**
Get the token end position in the pre-processed buffer,
with trailing spaces removed.
*/
const char *get_cpp_tok_end_rtrim()
{
const char *p;
for (p= m_cpp_tok_end;
p > m_cpp_buf && my_isspace(system_charset_info, p[-1]);
p--)
{ }
return p;
}
/** Get the current stream pointer, in the pre-processed buffer. */
const char *get_cpp_ptr()
{
@@ -2923,6 +2938,8 @@ public:
void start(THD *thd);
const char *substatement_query(THD *thd) const;
inline bool is_ps_or_view_context_analysis()
{
return (context_analysis_only &
@@ -3108,10 +3125,25 @@ public:
bool init_default_internal_variable(struct sys_var_with_base *variable,
LEX_STRING name);
bool set_variable(struct sys_var_with_base *variable, Item *item);
bool set_variable(const LEX_STRING &name1, const LEX_STRING &name2,
Item *item);
void sp_variable_declarations_init(THD *thd, int nvars);
bool sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition *cdef,
Row_definition_list *row,
Item *def);
bool sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition *cdef,
Item *def)
{
return sp_variable_declarations_finalize(thd, nvars, cdef, NULL, def);
}
bool sp_variable_declarations_row_finalize(THD *thd, int nvars,
Row_definition_list *row,
Item *def)
{
return sp_variable_declarations_finalize(thd, nvars, NULL, row, def);
}
bool sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
Qualified_column_ident *col,
Item *def);
@@ -3141,6 +3173,98 @@ public:
create_item_ident_sp(thd, name, start_in_q, end_in_q) :
create_item_ident_nosp(thd, name);
}
/*
Create an Item corresponding to a qualified name: a.b
when the parser is out of an SP context.
@param THD - THD, for mem_root
@param a - the first name
@param b - the second name
@retval - a pointer to a created item, or NULL on error.
Possible Item types that can be created:
- Item_trigger_field
- Item_field
- Item_ref
*/
Item *create_item_ident_nospvar(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b);
/*
Create an Item corresponding to a ROW field valiable: var.field
@param THD - THD, for mem_root
@param var - the ROW variable name
@param field - the ROW variable field name
@param spvar - the variable that was previously found by name
using "var_name".
@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);
/*
Create an item from its qualified name.
Depending on context, it can be either a ROW variable field,
or trigger, table field, table field reference.
See comments to create_item_spvar_row_field() and
create_item_ident_nospvar().
@param thd - THD, for mem_root
@param a - the first name
@param b - the second name
@param pos_in_q - position in the query (for binary log)
@param length_in_q - length in the query (for binary log)
@retval - NULL on error, or a pointer to a new Item.
*/
Item *create_item_ident(THD *thd,
const LEX_STRING &a,
const LEX_STRING &b,
uint pos_in_q, uint length_in_q);
/*
Create an item for a name in LIMIT clause: LIMIT var
@param THD - THD, for mem_root
@param var_name - the variable name
@param pos_in_q - position in the query (for binary log)
@param length_in_q - length in the query (for binary log)
@retval - a new Item corresponding to the SP variable,
or NULL on error
(non in SP, unknown variable, wrong data type).
*/
Item *create_item_limit(THD *thd,
const LEX_STRING &var_name,
uint pos_in_q, uint length_in_q);
/*
Create an item for a qualified name in LIMIT clause: LIMIT var.field
@param THD - THD, for mem_root
@param var_name - the variable name
@param field_name - the variable field name
@param pos_in_q - position in the query (for binary log)
@param length_in_q - length in the query (for binary log)
@retval - a new Item corresponding to the SP variable,
or NULL on error
(non in SP, unknown variable, unknown ROW field,
wrong data type).
*/
Item *create_item_limit(THD *thd,
const LEX_STRING &var_name,
const LEX_STRING &field_name,
uint pos_in_q, uint length_in_q);
/*
Create a my_var instance for a ROW field variable that was used
as an OUT SP parameter: CALL p1(var.field);
@param THD - THD, for mem_root
@param var_name - the variable name
@param field_name - the variable field name
*/
my_var *create_outvar(THD *thd,
const LEX_STRING &var_name,
const LEX_STRING &field_name);
bool is_trigger_new_or_old_reference(const LEX_STRING name);
Item *create_and_link_Item_trigger_field(THD *thd, const char *name,
@@ -3209,8 +3333,10 @@ public:
Item_param *add_placeholder(THD *thd, char *name,
uint pos_in_query, uint len_in_query);
Item_param *add_placeholder(THD *thd, char *name,
const char *pos, const char *end);
const char *pos, const char *end)
{
return add_placeholder(thd, name, pos - substatement_query(thd), end - pos);
}
sp_variable *sp_add_for_loop_variable(THD *thd, const LEX_STRING name,
Item *value);
sp_variable *sp_add_for_loop_upper_bound(THD *thd, Item *value)

View File

@@ -334,16 +334,15 @@ public:
friend class error_list;
friend class error_list_iterator;
#ifndef DBUG_OFF
/*
Debugging help: return N-th element in the list, or NULL if the list has
Return N-th element in the list, or NULL if the list has
less than N elements.
*/
void *elem(int n)
void *elem(uint n)
{
list_node *node= first;
void *data= NULL;
for (int i=0; i <= n; i++)
for (uint i= 0; i <= n; i++)
{
if (node == &end_of_list)
{
@@ -355,7 +354,6 @@ public:
}
return data;
}
#endif
#ifdef LIST_EXTRA_DEBUG
/*
@@ -546,9 +544,7 @@ public:
}
empty();
}
#ifndef DBUG_OFF
T *elem(int n) { return (T*)base_list::elem(n); }
#endif
T *elem(uint n) { return (T*) base_list::elem(n); }
};

View File

@@ -12955,6 +12955,14 @@ static bool check_row_equality(THD *thd, const Arg_comparator *comparators,
if (left_item->type() == Item::ROW_ITEM &&
right_item->type() == Item::ROW_ITEM)
{
/*
Item_splocal for ROW SP variables return Item::ROW_ITEM.
Here we know that left_item and right_item are not Item_splocal,
because ROW SP variables with nested ROWs are not supported yet.
It's safe to cast left_item and right_item to Item_row.
*/
DBUG_ASSERT(!left_item->get_item_splocal());
DBUG_ASSERT(!right_item->get_item_splocal());
is_converted= check_row_equality(thd,
comparators[i].subcomparators(),
(Item_row *) left_item,
@@ -13025,6 +13033,15 @@ bool Item_func_eq::check_equality(THD *thd, COND_EQUAL *cond_equal,
if (left_item->type() == Item::ROW_ITEM &&
right_item->type() == Item::ROW_ITEM)
{
/*
Item_splocal::type() for ROW variables returns Item::ROW_ITEM.
Distinguish ROW-type Item_splocal from Item_row.
Example query:
SELECT 1 FROM DUAL WHERE row_sp_variable=ROW(100,200);
*/
if (left_item->get_item_splocal() ||
right_item->get_item_splocal())
return false;
return check_row_equality(thd,
cmp.subcomparators(),
(Item_row *) left_item,

View File

@@ -385,10 +385,10 @@ void print_sjm(SJ_MATERIALIZATION_INFO *sjm)
/*
Debugging help: force List<...>::elem function not be removed as unused.
*/
Item* (List<Item>:: *dbug_list_item_elem_ptr)(int)= &List<Item>::elem;
Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(int)=
Item* (List<Item>:: *dbug_list_item_elem_ptr)(uint)= &List<Item>::elem;
Item_equal* (List<Item_equal>:: *dbug_list_item_equal_elem_ptr)(uint)=
&List<Item_equal>::elem;
TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(int) =
TABLE_LIST* (List<TABLE_LIST>:: *dbug_list_table_list_elem_ptr)(uint) =
&List<TABLE_LIST>::elem;
#endif

View File

@@ -2225,8 +2225,22 @@ bool Type_handler_temporal_result::
String *Type_handler_row::
print_item_value(THD *thd, Item *item, String *str) const
{
DBUG_ASSERT(0);
return NULL;
CHARSET_INFO *cs= thd->variables.character_set_client;
StringBuffer<STRING_BUFFER_USUAL_SIZE> val(cs);
str->append(C_STRING_WITH_LEN("ROW("));
for (uint i= 0 ; i < item->cols(); i++)
{
if (i > 0)
str->append(',');
Item *elem= item->element_index(i);
String *tmp= elem->type_handler()->print_item_value(thd, elem, &val);
if (tmp)
str->append(*tmp);
else
str->append(STRING_WITH_LEN("NULL"));
}
str->append(C_STRING_WITH_LEN(")"));
return str;
}

View File

@@ -761,6 +761,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
LEX_SYMBOL symbol;
Lex_string_with_metadata_st lex_string_with_metadata;
struct sys_var_with_base variable;
Lex_string_with_pos_st lex_string_with_pos;
Lex_spblock_st spblock;
Lex_spblock_handlers_st spblock_handlers;
Lex_length_and_dec_st Lex_length_and_dec;
@@ -771,6 +772,8 @@ Virtual_column_info *add_virtual_expression(THD *thd, Item *expr)
/* pointers */
Create_field *create_field;
Spvar_definition *spvar_definition;
Row_definition_list *spvar_definition_list;
CHARSET_INFO *charset;
Condition_information_item *cond_info_item;
DYNCALL_CREATE_DEF *dyncol_def;
@@ -1579,7 +1582,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str>
IDENT IDENT_QUOTED DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
HEX_NUM HEX_STRING
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
opt_component key_cache_name
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
@@ -1593,6 +1596,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str_ptr>
opt_table_alias
%type <lex_string_with_pos>
ident ident_with_tok_start
%type <table>
table_ident table_ident_nodb references xid
table_ident_opt_wild create_like
@@ -1679,7 +1685,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
geometry_function signed_literal expr_or_literal
opt_escape
sp_opt_default
simple_ident_nospvar simple_ident_q
simple_ident_nospvar simple_ident_q simple_ident_q2
field_or_var limit_option
part_func_expr
window_func_expr
@@ -1886,6 +1892,9 @@ END_OF_INPUT
%type <cond_info_item_name> condition_information_item_name;
%type <cond_info_list> condition_information;
%type <spvar_definition> row_field_name row_field_definition
%type <spvar_definition_list> row_field_definition_list field_type_row
%type <NONE> opt_window_clause window_def_list window_def window_spec
%type <lex_str_ptr> window_name
%type <NONE> opt_window_ref opt_window_frame_clause
@@ -2853,6 +2862,12 @@ sp_param_name_and_type:
if (Lex->sp_param_fill_definition($$= $1))
MYSQL_YYABORT;
}
| sp_param_name field_type_row
{
$$= $1;
$$->field_def.field_name= $$->name.str;
$$->field_def.set_row_field_definitions($2);
}
;
/* Stored PROCEDURE parameter declaration list */
@@ -2931,6 +2946,44 @@ sp_decl:
DECLARE_SYM sp_decl_body { $$= $2; }
;
row_field_name:
ident
{
if (check_string_char_length(&$1, 0, NAME_CHAR_LEN,
system_charset_info, 1))
my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str));
if (!($$= new (thd->mem_root) Spvar_definition()))
MYSQL_YYABORT;
Lex->init_last_field($$, $1.str, thd->variables.collation_database);
}
;
row_field_definition:
row_field_name type_with_opt_collate
;
row_field_definition_list:
row_field_definition
{
if (!($$= new (thd->mem_root) Row_definition_list()))
MYSQL_YYABORT;
$$->push_back($1, thd->mem_root);
}
| row_field_definition_list ',' row_field_definition
{
uint unused;
if ($1->find_row_field_by_name($3->field_name, &unused))
my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name));
$$= $1;
$$->push_back($3, thd->mem_root);
}
;
field_type_row:
ROW_SYM '(' row_field_definition_list ')' { $$= $3; }
;
sp_decl_body:
sp_decl_idents
{
@@ -2942,8 +2995,18 @@ sp_decl_body:
if (Lex->sp_variable_declarations_finalize(thd, $1,
&Lex->last_field[0], $4))
MYSQL_YYABORT;
$$.vars= $1;
$$.conds= $$.hndlrs= $$.curs= 0;
$$.init_using_vars($1);
}
| sp_decl_idents
{
Lex->sp_variable_declarations_init(thd, $1);
}
field_type_row
sp_opt_default
{
if (Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4))
MYSQL_YYABORT;
$$.init_using_vars($1);
}
| ident CONDITION_SYM FOR_SYM sp_cond
{
@@ -8357,7 +8420,7 @@ remember_name:
remember_end:
{
$$= (char*) YYLIP->get_cpp_tok_end();
$$= (char*) YYLIP->get_cpp_tok_end_rtrim();
}
;
@@ -11421,32 +11484,25 @@ limit_options:
;
limit_option:
ident
ident_with_tok_start
{
Item_splocal *splocal;
LEX *lex= thd->lex;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
sp_variable *spv;
sp_pcontext *spc = lex->spcont;
if (spc && (spv = spc->find_variable($1, false)))
{
splocal= new (thd->mem_root)
Item_splocal(thd, $1, spv->offset, spv->sql_type(),
lip->get_tok_start() - lex->sphead->m_tmp_query,
lip->get_ptr() - lip->get_tok_start());
if (splocal == NULL)
MYSQL_YYABORT;
#ifndef DBUG_OFF
splocal->m_sp= lex->sphead;
#endif
lex->safe_to_cache_query=0;
}
else
my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
if (splocal->type() != Item::INT_ITEM)
my_yyabort_error((ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)));
splocal->limit_clause_param= TRUE;
$$= splocal;
if (!($$= lex->create_item_limit(thd, $1,
$1.m_pos -
lex->substatement_query(thd),
lip->get_tok_end() - $1.m_pos)))
MYSQL_YYABORT;
}
| ident_with_tok_start '.' ident
{
LEX *lex= thd->lex;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
if (!($$= lex->create_item_limit(thd, $1, $3,
$1.m_pos -
lex->substatement_query(thd),
lip->get_ptr() - $1.m_pos)))
MYSQL_YYABORT;
}
| param_marker
{
@@ -11655,6 +11711,11 @@ select_outvar:
Lex->sphead)) :
NULL;
}
| ident '.' ident
{
if (!($$= Lex->create_outvar(thd, $1, $3)))
MYSQL_YYABORT;
}
;
into:
@@ -13706,7 +13767,16 @@ simple_ident:
lip->get_tok_end())))
MYSQL_YYABORT;
}
| simple_ident_q { $$= $1; }
| simple_ident_q2 { $$= $1; }
| ident '.' ident
{
LEX *lex= thd->lex;
if (!($$= lex->create_item_ident(thd, $1, $3,
$1.m_pos -
lex->substatement_query(thd),
YYLIP->get_tok_end() - $1.m_pos)))
MYSQL_YYABORT;
}
;
simple_ident_nospvar:
@@ -13721,45 +13791,14 @@ simple_ident_nospvar:
simple_ident_q:
ident '.' ident
{
LEX *lex= thd->lex;
/*
FIXME This will work ok in simple_ident_nospvar case because
we can't meet simple_ident_nospvar in trigger now. But it
should be changed in future.
*/
if (lex->is_trigger_new_or_old_reference($1))
{
bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
if (!($$= lex->create_and_link_Item_trigger_field(thd, $3.str,
new_row)))
MYSQL_YYABORT;
}
else
{
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
MYF(0), $1.str, thd->where);
}
if ((sel->parsing_place != IN_HAVING) ||
(sel->get_in_sum_expr() > 0))
{
$$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
NullS, $1.str, $3.str);
}
else
{
$$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
NullS, $1.str, $3.str);
}
if ($$ == NULL)
MYSQL_YYABORT;
}
if (!($$= Lex->create_item_ident_nospvar(thd, $1, $3)))
MYSQL_YYABORT;
}
| '.' ident '.' ident
| simple_ident_q2
;
simple_ident_q2:
'.' ident '.' ident
{
LEX *lex= thd->lex;
SELECT_LEX *sel= lex->current_select;
@@ -13951,13 +13990,33 @@ TEXT_STRING_filesystem:
;
ident:
IDENT_sys { $$=$1; }
IDENT_sys
{
(LEX_STRING &)$$= $1;
$$.m_pos= (char *) YYLIP->get_tok_start_prev();
}
| keyword
{
$$.str= thd->strmake($1.str, $1.length);
if ($$.str == NULL)
MYSQL_YYABORT;
$$.length= $1.length;
$$.m_pos= (char *) YYLIP->get_tok_start_prev();
}
;
ident_with_tok_start:
IDENT_sys
{
(LEX_STRING &)$$= $1;
$$.m_pos= (char *) YYLIP->get_tok_start();
}
| keyword
{
if (!($$.str= thd->strmake($1.str, $1.length)))
MYSQL_YYABORT;
$$.length= $1.length;
$$.m_pos= (char *) YYLIP->get_tok_start();
}
;
@@ -14616,9 +14675,24 @@ option_value_following_option_type:
// Option values without preceding option_type.
option_value_no_option_type:
internal_variable_name equal set_expr_or_default
ident equal set_expr_or_default
{
if (Lex->set_variable(&$1, $3))
struct sys_var_with_base var;
if (Lex->init_internal_variable(&var, $1) ||
Lex->set_variable(&var, $3))
MYSQL_YYABORT;
}
| ident '.' ident equal set_expr_or_default
{
DBUG_ASSERT(Lex->var_list.is_empty());
if (Lex->set_variable($1, $3, $5))
MYSQL_YYABORT;
}
| DEFAULT '.' ident equal set_expr_or_default
{
struct sys_var_with_base var;
if (Lex->init_default_internal_variable(&var, $3) ||
Lex->set_variable(&var, $5))
MYSQL_YYABORT;
}
| '@' ident_or_text equal expr

View File

@@ -171,6 +171,7 @@ void ORAerror(THD *thd, const char *s)
LEX_SYMBOL symbol;
Lex_string_with_metadata_st lex_string_with_metadata;
struct sys_var_with_base variable;
Lex_string_with_pos_st lex_string_with_pos;
Lex_spblock_st spblock;
Lex_spblock_handlers_st spblock_handlers;
Lex_length_and_dec_st Lex_length_and_dec;
@@ -186,6 +187,8 @@ void ORAerror(THD *thd, const char *s)
/* pointers */
Create_field *create_field;
Spvar_definition *spvar_definition;
Row_definition_list *spvar_definition_list;
CHARSET_INFO *charset;
Condition_information_item *cond_info_item;
DYNCALL_CREATE_DEF *dyncol_def;
@@ -997,7 +1000,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str>
IDENT IDENT_QUOTED DECIMAL_NUM FLOAT_NUM NUM LONG_NUM
HEX_NUM HEX_STRING
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident ident_or_text
LEX_HOSTNAME ULONGLONG_NUM field_ident select_alias ident_or_text
IDENT_sys TEXT_STRING_sys TEXT_STRING_literal
opt_component key_cache_name
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
@@ -1013,6 +1016,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <lex_str_ptr>
opt_table_alias
%type <lex_string_with_pos>
ident ident_with_tok_start
%type <table>
table_ident table_ident_nodb references xid
table_ident_opt_wild create_like
@@ -1113,7 +1119,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
geometry_function signed_literal expr_or_literal
opt_escape
sp_opt_default
simple_ident_nospvar simple_ident_q
simple_ident_nospvar simple_ident_q simple_ident_q2
field_or_var limit_option
part_func_expr
window_func_expr
@@ -1346,6 +1352,9 @@ END_OF_INPUT
%type <cond_info_item_name> condition_information_item_name;
%type <cond_info_list> condition_information;
%type <spvar_definition> row_field_name row_field_definition
%type <spvar_definition_list> row_field_definition_list field_type_row
%type <NONE> opt_window_clause window_def_list window_def window_spec
%type <lex_str_ptr> window_name
%type <NONE> opt_window_ref opt_window_frame_clause
@@ -2318,6 +2327,12 @@ sp_param_name_and_type:
{
Lex->sphead->fill_spvar_using_type_reference($$= $1, $2);
}
| sp_param_name field_type_row
{
$$= $1;
$$->field_def.field_name= $$->name.str;
$$->field_def.set_row_field_definitions($2);
}
;
/* Stored PROCEDURE parameter declaration list */
@@ -2342,6 +2357,12 @@ sp_pdparam:
{
Lex->sphead->fill_spvar_using_type_reference($1, $3);
}
| sp_param_name sp_opt_inout field_type_row
{
$1->mode= $2;
$1->field_def.field_name= $1->name.str;
$1->field_def.set_row_field_definitions($3);
}
;
sp_opt_inout:
@@ -2435,6 +2456,44 @@ qualified_column_ident:
}
;
row_field_name:
ident_directly_assignable
{
if (check_string_char_length(&$1, 0, NAME_CHAR_LEN,
system_charset_info, 1))
my_yyabort_error((ER_TOO_LONG_IDENT, MYF(0), $1.str));
if (!($$= new (thd->mem_root) Spvar_definition()))
MYSQL_YYABORT;
Lex->init_last_field($$, $1.str, thd->variables.collation_database);
}
;
row_field_definition:
row_field_name type_with_opt_collate
;
row_field_definition_list:
row_field_definition
{
if (!($$= new (thd->mem_root) Row_definition_list()))
MYSQL_YYABORT;
$$->push_back($1, thd->mem_root);
}
| row_field_definition_list ',' row_field_definition
{
uint unused;
if ($1->find_row_field_by_name($3->field_name, &unused))
my_yyabort_error((ER_DUP_FIELDNAME, MYF(0), $3->field_name));
$$= $1;
$$->push_back($3, thd->mem_root);
}
;
field_type_row:
ROW_SYM '(' row_field_definition_list ')' { $$= $3; }
;
sp_decl_body:
sp_decl_idents
{
@@ -2459,6 +2518,17 @@ sp_decl_body:
MYSQL_YYABORT;
$$.init_using_vars($1);
}
| sp_decl_idents
{
Lex->sp_variable_declarations_init(thd, $1);
}
field_type_row
sp_opt_default
{
if (Lex->sp_variable_declarations_row_finalize(thd, $1, $3, $4))
MYSQL_YYABORT;
$$.init_using_vars($1);
}
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
{
if (Lex->spcont->declare_condition(thd, $1, $4))
@@ -8360,7 +8430,7 @@ remember_name:
remember_end:
{
$$= (char*) YYLIP->get_cpp_tok_end();
$$= (char*) YYLIP->get_cpp_tok_end_rtrim();
}
;
@@ -11516,32 +11586,25 @@ limit_options:
;
limit_option:
ident
ident_with_tok_start
{
Item_splocal *splocal;
LEX *lex= thd->lex;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
sp_variable *spv;
sp_pcontext *spc = lex->spcont;
if (spc && (spv = spc->find_variable($1, false)))
{
splocal= new (thd->mem_root)
Item_splocal(thd, $1, spv->offset, spv->sql_type(),
lip->get_tok_start() - lex->sphead->m_tmp_query,
lip->get_ptr() - lip->get_tok_start());
if (splocal == NULL)
MYSQL_YYABORT;
#ifndef DBUG_OFF
splocal->m_sp= lex->sphead;
#endif
lex->safe_to_cache_query=0;
}
else
my_yyabort_error((ER_SP_UNDECLARED_VAR, MYF(0), $1.str));
if (splocal->type() != Item::INT_ITEM)
my_yyabort_error((ER_WRONG_SPVAR_TYPE_IN_LIMIT, MYF(0)));
splocal->limit_clause_param= TRUE;
$$= splocal;
if (!($$= lex->create_item_limit(thd, $1,
$1.m_pos -
lex->substatement_query(thd),
lip->get_tok_end() - $1.m_pos)))
MYSQL_YYABORT;
}
| ident_with_tok_start '.' ident
{
LEX *lex= thd->lex;
Lex_input_stream *lip= & thd->m_parser_state->m_lip;
if (!($$= lex->create_item_limit(thd, $1, $3,
$1.m_pos -
lex->substatement_query(thd),
lip->get_ptr() - $1.m_pos)))
MYSQL_YYABORT;
}
| param_marker
{
@@ -11750,6 +11813,11 @@ select_outvar:
Lex->sphead)) :
NULL;
}
| ident '.' ident
{
if (!($$= Lex->create_outvar(thd, $1, $3)))
MYSQL_YYABORT;
}
;
into:
@@ -13820,7 +13888,16 @@ simple_ident:
lip->get_tok_end())))
MYSQL_YYABORT;
}
| simple_ident_q { $$= $1; }
| simple_ident_q2
| ident '.' ident
{
LEX *lex= thd->lex;
if (!($$= lex->create_item_ident(thd, $1, $3,
$1.m_pos -
lex->substatement_query(thd),
YYLIP->get_tok_end() - $1.m_pos)))
MYSQL_YYABORT;
}
;
simple_ident_nospvar:
@@ -13835,45 +13912,14 @@ simple_ident_nospvar:
simple_ident_q:
ident '.' ident
{
LEX *lex= thd->lex;
/*
FIXME This will work ok in simple_ident_nospvar case because
we can't meet simple_ident_nospvar in trigger now. But it
should be changed in future.
*/
if (lex->is_trigger_new_or_old_reference($1))
{
bool new_row= ($1.str[0]=='N' || $1.str[0]=='n');
if (!($$= lex->create_and_link_Item_trigger_field(thd, $3.str,
new_row)))
MYSQL_YYABORT;
}
else
{
SELECT_LEX *sel= lex->current_select;
if (sel->no_table_names_allowed)
{
my_error(ER_TABLENAME_NOT_ALLOWED_HERE,
MYF(0), $1.str, thd->where);
}
if ((sel->parsing_place != IN_HAVING) ||
(sel->get_in_sum_expr() > 0))
{
$$= new (thd->mem_root) Item_field(thd, Lex->current_context(),
NullS, $1.str, $3.str);
}
else
{
$$= new (thd->mem_root) Item_ref(thd, Lex->current_context(),
NullS, $1.str, $3.str);
}
if ($$ == NULL)
MYSQL_YYABORT;
}
if (!($$= Lex->create_item_ident_nospvar(thd, $1, $3)))
MYSQL_YYABORT;
}
| colon_with_pos ident '.' ident
| simple_ident_q2
;
simple_ident_q2:
colon_with_pos ident '.' ident
{
LEX *lex= Lex;
if (lex->is_trigger_new_or_old_reference($2))
@@ -14082,16 +14128,35 @@ TEXT_STRING_filesystem:
;
ident:
IDENT_sys { $$=$1; }
IDENT_sys
{
(LEX_STRING &)$$= $1;
$$.m_pos= (char *) YYLIP->get_tok_start_prev();
}
| keyword
{
$$.str= thd->strmake($1.str, $1.length);
if ($$.str == NULL)
MYSQL_YYABORT;
$$.length= $1.length;
$$.m_pos= (char *) YYLIP->get_tok_start_prev();
}
;
ident_with_tok_start:
IDENT_sys
{
(LEX_STRING &)$$= $1;
$$.m_pos= (char *) YYLIP->get_tok_start();
}
| keyword
{
if (!($$.str= thd->strmake($1.str, $1.length)))
MYSQL_YYABORT;
$$.length= $1.length;
$$.m_pos= (char *) YYLIP->get_tok_start();
}
;
ident_directly_assignable:
IDENT_sys { $$=$1; }
@@ -14358,6 +14423,7 @@ keyword_sp_data_type:
| POINT_SYM {}
| POLYGON {}
| RAW {} /* Oracle-R */
| ROW_SYM {}
| SERIAL_SYM {}
| TEXT_SYM {}
| TIMESTAMP {}
@@ -14593,7 +14659,6 @@ keyword_sp_not_data_type:
| ROWCOUNT_SYM {}
| ROW_COUNT_SYM {}
| ROW_FORMAT_SYM {}
| ROW_SYM {}
| RTREE_SYM {}
| SCHEDULE_SYM {}
| SCHEMA_NAME_SYM {}
@@ -14711,6 +14776,21 @@ set_assign:
sp_create_assignment_instr(thd, yychar == YYEMPTY))
MYSQL_YYABORT;
}
| ident_directly_assignable '.' ident SET_VAR
{
LEX *lex=Lex;
lex->set_stmt_init();
lex->var_list.empty();
sp_create_assignment_lex(thd, yychar == YYEMPTY);
}
set_expr_or_default
{
LEX *lex= Lex;
DBUG_ASSERT(lex->var_list.is_empty());
if (lex->set_variable($1, $3, $6) ||
lex->sphead->restore_lex(thd))
MYSQL_YYABORT;
}
;
set_stmt_option_value_following_option_type_list:
@@ -14845,11 +14925,26 @@ option_value_following_option_type:
// Option values without preceding option_type.
option_value_no_option_type:
internal_variable_name equal set_expr_or_default
ident equal set_expr_or_default
{
if (Lex->set_variable(&$1, $3))
struct sys_var_with_base var;
if (Lex->init_internal_variable(&var, $1) ||
Lex->set_variable(&var, $3))
MYSQL_YYABORT;
}
| ident '.' ident equal set_expr_or_default
{
DBUG_ASSERT(Lex->var_list.is_empty());
if (Lex->set_variable($1, $3, $5))
MYSQL_YYABORT;
}
| DEFAULT '.' ident equal set_expr_or_default
{
struct sys_var_with_base var;
if (Lex->init_default_internal_variable(&var, $3) ||
Lex->set_variable(&var, $5))
MYSQL_YYABORT;
}
| '@' ident_or_text equal expr
{
Item_func_set_user_var *item;
@@ -14998,11 +15093,6 @@ internal_variable_name_directly_assignable:
if (Lex->init_internal_variable(&$$, $1))
MYSQL_YYABORT;
}
| ident_directly_assignable '.' ident
{
if (Lex->init_internal_variable(&$$, $1, $3))
MYSQL_YYABORT;
}
| DEFAULT '.' ident
{
if (Lex->init_default_internal_variable(&$$, $3))

View File

@@ -693,4 +693,10 @@ public:
};
struct Lex_string_with_pos_st: public LEX_STRING
{
const char *m_pos;
};
#endif /* STRUCTS_INCLUDED */