1
0
mirror of https://github.com/MariaDB/server.git synced 2025-08-08 11:22:35 +03:00

MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations

This commit is contained in:
Alexander Barkov
2016-12-06 09:05:52 +04:00
parent cd1afe0aac
commit 46d076d67a
30 changed files with 2713 additions and 112 deletions

View File

@@ -752,3 +752,34 @@ DROP PROCEDURE p1;
#
# End of MDEV-10597 Cursors with parameters
#
#
# MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#
CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3));
INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123');
INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123');
CREATE TABLE t2 LIKE t1;
CREATE PROCEDURE p1()
AS
v_a t1.a%TYPE;
v_b t1.b%TYPE;
v_c t1.c%TYPE;
CURSOR c IS SELECT a,b,c FROM t1;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_a, v_b, v_c;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c);
END LOOP;
CLOSE c;
END;
$$
CALL p1();
SELECT * FROM t2;
a b c
1 b1 2001-01-01 10:20:30.123
2 b2 2001-01-02 10:20:30.123
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@@ -0,0 +1,239 @@
SET sql_mode=ORACLE;
#
# MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#
#
# Initiation:
# - creating database db1
# - creating user user1 with access rights to db1
#
CREATE DATABASE db1;
CREATE TABLE db1.t1 (a INT, b VARCHAR(10));
CREATE USER user1;
GRANT ALL PRIVILEGES ON test.* TO user1;
connect conn1,localhost,user1,,test;
SET sql_mode=ORACLE;
SELECT database();
database()
test
SELECT user();
user()
user1@localhost
#
# Making sure that user1 does not have privileges to db1.t1
#
SHOW CREATE TABLE db1.t1;
ERROR 42000: SHOW command denied to user 'user1'@'localhost' for table 't1'
SHOW FIELDS IN db1.t1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
#
# Trigger: using %TYPE with a table we don't have access to
#
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
a b
10 20
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
INSERT INTO t1 (a) VALUES (10);
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
SELECT * FROM t1;
a b
10 20
DROP TRIGGER tr1;
DROP TABLE t1;
#
# Stored procedure: Using %TYPE for with a table that we don't have access to
# DEFINER user1, SQL SECURITY DEFAULT
#
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT 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
#
connection default;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT 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
#
connection default;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
connection conn1;
CALL p1;
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
#
CREATE TABLE t1 (a INT);
CREATE FUNCTION f1() RETURN INT
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
SELECT f1();
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP FUNCTION f1;
DROP TABLE t1;
#
# Stored function: Using %TYPE for with a table that we don't have access to
# DEFINER root, SQL SECURITY INVOKER
#
connection default;
CREATE TABLE t1 (a INT);
CREATE FUNCTION f1() RETURN INT
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
connection conn1;
SELECT f1();
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for table 't1'
DROP FUNCTION f1;
DROP TABLE t1;
#
# Stored function: Using %TYPE for with a table that we don't have access to
# DEFINER root, SQL SECURITY DEFINER
#
connection default;
CREATE TABLE t1 (a INT);
CREATE FUNCTION f1() RETURN INT
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
connection conn1;
SELECT f1();
f1()
1
DROP FUNCTION f1;
DROP TABLE t1;
connection default;
GRANT SELECT (a) ON db1.t1 TO user1;
connection conn1;
#
# Making sure that user1 has access to db1.t1.a, but not to db1.t1.b
#
SHOW CREATE TABLE db1.t1;
ERROR 42000: SHOW command denied to user 'user1'@'localhost' for table 't1'
SHOW FIELDS IN db1.t1;
Field Type Null Key Default Extra
a int(11) YES NULL
#
# Trigger: Per-column privileges
#
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
a b
10 20
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE a db1.t1.a%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
a b
10 20
10 10
DROP TRIGGER tr1;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
INSERT INTO t1 (a) VALUES (10);
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1'
SELECT * FROM t1;
a b
10 20
10 10
DROP TRIGGER tr1;
DROP TABLE t1;
#
# Stored procedure: Per-column privileges
# DEFINER user1, SQL SECURITY DEFAULT
#
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
CALL p1;
a
10
DROP PROCEDURE p1;
CREATE PROCEDURE p1()
AS
b db1.t1.b%TYPE := 10;
BEGIN
SELECT b;
END;
$$
CALL p1;
ERROR 42000: SELECT command denied to user 'user1'@'localhost' for column 'b' in table 't1'
DROP PROCEDURE p1;
#
# Clean up
#
disconnect conn1;
connection default;
DROP USER user1;
DROP DATABASE db1;
#
# End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#

View File

@@ -1240,3 +1240,939 @@ DROP TABLE t1, t2;
#
# End of MDEV-10583 sql_mode=ORACLE: SQL%ROWCOUNT
#
#
# MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#
#
# Missing table
#
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
NULL;
END;
$$
CALL p1();
ERROR 42S02: Table 'test.t1' doesn't exist
DROP PROCEDURE p1;
#
# Missing column
#
CREATE TABLE t1 (b INT);
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
NULL;
END;
$$
CALL p1();
ERROR 42S22: Unknown column 'a' in 't1'
DROP PROCEDURE p1;
DROP TABLE t1;
#
# One %TYPE variable
#
CREATE TABLE t1 (a INT);
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
a:= 123;
SELECT a;
END;
$$
CALL p1();
a
123
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Two %TYPE variables, with a truncation warning on assignment
#
CREATE TABLE t1 (a TINYINT, b INT);
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
b t1.b%TYPE;
BEGIN
a:= 200;
b:= 200;
SELECT a, b;
END;
$$
CALL p1();
a b
127 200
Warnings:
Warning 1264 Out of range value for column 'a' at row 1
DROP PROCEDURE p1;
DROP TABLE t1;
#
# %TYPE variables for fields with various attributes
#
CREATE TABLE t1 (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
a TINYINT NOT NULL,
b INT NOT NULL,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE(a)
);
CREATE PROCEDURE p1
AS
id t1.id%TYPE;
a t1.a%TYPE;
b t1.b%TYPE;
ts t1.ts%TYPE;
BEGIN
SELECT id, a, b, ts;
CREATE TABLE t2 AS SELECT id, a, b, ts;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
id a b ts
NULL NULL NULL NULL
Table Create Table
t2 CREATE TABLE "t2" (
"id" int(11) DEFAULT NULL,
"a" tinyint(4) DEFAULT NULL,
"b" int(11) DEFAULT NULL,
"ts" timestamp NULL DEFAULT NULL
)
DROP PROCEDURE p1;
DROP TABLE t1;
#
# %TYPE + virtual columns
#
CREATE TABLE t1 (
a INT NOT NULL,
b VARCHAR(32),
c INT AS (a + 10) VIRTUAL,
d VARCHAR(5) AS (left(b,5)) PERSISTENT
);
CREATE PROCEDURE p1
AS
c t1.c%TYPE;
d t1.d%TYPE;
BEGIN
SELECT c, d;
CREATE TABLE t2 AS SELECT c, d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
c d
NULL NULL
Table Create Table
t2 CREATE TABLE "t2" (
"c" int(11) DEFAULT NULL,
"d" varchar(5) DEFAULT NULL
)
DROP PROCEDURE p1;
DROP TABLE t1;
#
# %TYPE + the ZEROFILL attribute
#
CREATE TABLE t1 (
dz DECIMAL(10,3) ZEROFILL
);
CREATE PROCEDURE p1
AS
dzr t1.dz%TYPE := 10;
dzt DECIMAL(10,3) ZEROFILL := 10;
BEGIN
SELECT dzr, dzt;
CREATE TABLE t2 AS SELECT dzr,dzt;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
CALL p1();
dzr dzt
0000010.000 0000010.000
Table Create Table
t2 CREATE TABLE "t2" (
"dzr" decimal(10,3) unsigned DEFAULT NULL,
"dzt" decimal(10,3) unsigned DEFAULT NULL
)
DROP PROCEDURE p1;
DROP TABLE t1;
#
# Temporary tables shadow real tables for %TYPE purposes
#
CREATE TABLE t1 (a VARCHAR(10));
INSERT INTO t1 VALUES ('t1');
CREATE TEMPORARY TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
SELECT * FROM t1;
a
10
CREATE PROCEDURE p1
AS
a t1.a%TYPE:=11;
BEGIN
CREATE TABLE t2 AS SELECT a;
END;
$$
#
# Should use INT(11) as %TYPE, as in the temporary table
#
CALL p1();
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" int(11) DEFAULT NULL
)
SELECT * FROM t2;
a
11
DROP TABLE t2;
SELECT * FROM t1;
a
10
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
a
t1
#
# Should use VARCHAR(10) as %TYPE, as in the real table
#
CALL p1();
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" varchar(10) DEFAULT NULL
)
SELECT * FROM t2;
a
11
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# t1.a%TYPE searches for "t1" in the current database
#
CREATE TABLE t1 (a VARCHAR(10));
CREATE DATABASE test1;
CREATE TABLE test1.t1 (a INT);
CREATE PROCEDURE p1
AS
a t1.a%TYPE:=11;
BEGIN
CREATE TABLE test.t2 AS SELECT a;
END;
$$
#
# This interprets t1.a%TYPE as VARCHAR(10), as in test.t1.a
#
USE test;
CALL test.p1();
SHOW CREATE TABLE test.t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" varchar(10) DEFAULT NULL
)
DROP TABLE test.t2;
#
# This interprets t1.a%TYPE as INT, as in test1.t1.a
#
USE test1;
CALL test.p1();
SHOW CREATE TABLE test.t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" int(11) DEFAULT NULL
)
DROP TABLE test.t2;
#
# Error if there is no an active database
#
DROP DATABASE test1;
CALL test.p1();
ERROR 3D000: No database selected
USE test;
DROP PROCEDURE p1;
DROP TABLE t1;
#
# A reference to a table in a non-existing database
#
CREATE PROCEDURE p1
AS
a test1.t1.a%TYPE;
BEGIN
CREATE TABLE t1 AS SELECT a;
END;
$$
CALL p1;
ERROR 42S02: Table 'test1.t1' doesn't exist
DROP PROCEDURE p1;
#
# A reference to a table in a different database
#
CREATE TABLE t1(a INT);
CREATE DATABASE test1;
CREATE TABLE test1.t1 (a VARCHAR(10));
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
b test1.t1.a%TYPE;
BEGIN
CREATE TABLE t2 AS SELECT a,b;
END;
$$
CALL p1;
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" int(11) DEFAULT NULL,
"b" varchar(10) DEFAULT NULL
)
DROP PROCEDURE p1;
DROP TABLE t2;
DROP DATABASE test1;
DROP TABLE t1;
#
# Using a table before it appears in a %TYPE declaration + multiple %TYPE declarations
#
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 (a,b) VALUES (10,'b10');
CREATE PROCEDURE p1
AS
BEGIN
INSERT INTO t1 (a,b) VALUES (11, 'b11');
SELECT * FROM t1;
DECLARE
va t1.a%TYPE:= 30;
vb t1.b%TYPE:= 'b30';
BEGIN
INSERT INTO t1 (a,b) VALUES (12,'b12');
SELECT * FROM t1;
INSERT INTO t1 (a,b) VALUES (va, vb);
SELECT * FROM t1;
END;
DECLARE
va t1.a%TYPE:= 40;
vb t1.b%TYPE:= 'b40';
BEGIN
INSERT INTO t1 (a,b) VALUES (va,vb);
SELECT * FROM t1;
END;
END;
$$
CALL p1;
a b
10 b10
11 b11
a b
10 b10
11 b11
12 b12
a b
10 b10
11 b11
12 b12
30 b30
a b
10 b10
11 b11
12 b12
30 b30
40 b40
DROP TABLE t1;
DROP PROCEDURE p1;
#
# %TYPE variables + TABLE vs VIEW
#
CREATE TABLE t1 (
bit6 BIT(6),
bit7 BIT(7),
bit8 BIT(8),
i1 TINYINT,
i2 SMALLINT,
i3 MEDIUMINT,
i4 INT,
i8 BIGINT,
ff FLOAT,
fd DOUBLE,
cc CHAR(10),
cv VARCHAR(10),
cvu VARCHAR(10) CHARACTER SET utf8,
t1 TINYTEXT,
t2 TEXT,
t3 MEDIUMTEXT,
t4 LONGTEXT,
enum1 ENUM('a','b','c'),
set1 SET('a','b','c'),
blob1 TINYBLOB,
blob2 BLOB,
blob3 MEDIUMBLOB,
blob4 LONGBLOB,
yy YEAR,
dd DATE,
tm0 TIME,
tm3 TIME(3),
tm6 TIME(6),
dt0 DATETIME,
dt3 DATETIME(3),
dt6 DATETIME(6),
ts0 TIMESTAMP,
ts3 TIMESTAMP(3),
ts6 TIMESTAMP(6),
dc100 DECIMAL(10,0),
dc103 DECIMAL(10,3),
dc209 DECIMAL(20,9)
);
CREATE PROCEDURE p1(command enum('create','select'))
AS
bit6 t1.bit6%TYPE := 0x30;
bit7 t1.bit7%TYPE := 0x41;
bit8 t1.bit8%TYPE := 0x7E;
i1 t1.i1%TYPE := 11;
i2 t1.i2%TYPE := 12;
i3 t1.i3%TYPE := 13;
i4 t1.i4%TYPE := 14;
i8 t1.i8%TYPE := 18;
ff t1.ff%TYPE := 21;
fd t1.fd%TYPE := 22;
cc t1.cc%TYPE := 'char';
cv t1.cv%TYPE := 'varchar';
cvu t1.cvu%TYPE := 'varcharu8';
t1 t1.t1%TYPE := 'text1';
t2 t1.t2%TYPE := 'text2';
t3 t1.t3%TYPE := 'text3';
t4 t1.t4%TYPE := 'text4';
enum1 t1.enum1%TYPE := 'b';
set1 t1.set1%TYPE := 'a,c';
blob1 t1.blob1%TYPE := 'blob1';
blob2 t1.blob2%TYPE := 'blob2';
blob3 t1.blob3%TYPE := 'blob3';
blob4 t1.blob4%TYPE := 'blob4';
yy t1.yy%TYPE := 2001;
dd t1.dd%TYPE := '2001-01-01';
tm0 t1.tm0%TYPE := '00:00:01';
tm3 t1.tm3%TYPE := '00:00:03.333';
tm6 t1.tm6%TYPE := '00:00:06.666666';
dt0 t1.dt0%TYPE := '2001-01-01 00:00:01';
dt3 t1.dt3%TYPE := '2001-01-03 00:00:01.333';
dt6 t1.dt6%TYPE := '2001-01-06 00:00:01.666666';
ts0 t1.ts0%TYPE := '2002-01-01 00:00:01';
ts3 t1.ts3%TYPE := '2002-01-03 00:00:01.333';
ts6 t1.ts6%TYPE := '2002-01-06 00:00:01.666666';
dc100 t1.dc100%TYPE := 10;
dc103 t1.dc103%TYPE := 10.123;
dc209 t1.dc209%TYPE := 10.123456789;
BEGIN
CASE
WHEN command='create' THEN
CREATE TABLE t2 AS SELECT
bit6, bit7, bit8,
i1,i2,i3,i4,i8,
ff,fd, dc100, dc103, dc209,
cc,cv,cvu,
t1,t2,t3,t4,
enum1, set1,
blob1, blob2, blob3, blob4,
dd, yy,
tm0, tm3, tm6,
dt0, dt3, dt6,
ts0, ts3, ts6;
WHEN command='select' THEN
SELECT
bit6, bit7, bit8,
i1,i2,i3,i4,i8,
ff,fd, dc100, dc103, dc209,
cc,cv,cvu,
t1,t2,t3,t4,
enum1, set1,
blob1, blob2, blob3, blob4,
dd, yy,
tm0, tm3, tm6,
dt0, dt3, dt6,
ts0, ts3, ts6;
END CASE;
END;
$$
#
# TABLE
#
CALL p1('create');
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"bit6" bit(6) DEFAULT NULL,
"bit7" bit(7) DEFAULT NULL,
"bit8" bit(8) DEFAULT NULL,
"i1" tinyint(4) DEFAULT NULL,
"i2" smallint(6) DEFAULT NULL,
"i3" mediumint(9) DEFAULT NULL,
"i4" int(11) DEFAULT NULL,
"i8" bigint(20) DEFAULT NULL,
"ff" float DEFAULT NULL,
"fd" double DEFAULT NULL,
"dc100" decimal(10,0) DEFAULT NULL,
"dc103" decimal(10,3) DEFAULT NULL,
"dc209" decimal(20,9) DEFAULT NULL,
"cc" varchar(10) DEFAULT NULL,
"cv" varchar(10) DEFAULT NULL,
"cvu" varchar(10) CHARACTER SET utf8 DEFAULT NULL,
"t1" tinytext DEFAULT NULL,
"t2" text DEFAULT NULL,
"t3" mediumtext DEFAULT NULL,
"t4" longtext DEFAULT NULL,
"enum1" varchar(1) DEFAULT NULL,
"set1" varchar(5) DEFAULT NULL,
"blob1" tinyblob DEFAULT NULL,
"blob2" longblob DEFAULT NULL,
"blob3" mediumblob DEFAULT NULL,
"blob4" longblob DEFAULT NULL,
"dd" datetime DEFAULT NULL,
"yy" year(4) DEFAULT NULL,
"tm0" time DEFAULT NULL,
"tm3" time(3) DEFAULT NULL,
"tm6" time(6) DEFAULT NULL,
"dt0" datetime DEFAULT NULL,
"dt3" datetime(3) DEFAULT NULL,
"dt6" datetime(6) DEFAULT NULL,
"ts0" timestamp NULL DEFAULT NULL,
"ts3" timestamp(3) NULL DEFAULT NULL,
"ts6" timestamp(6) NULL DEFAULT NULL
)
SELECT * FROM t2;
bit6 0
bit7 A
bit8 ~
i1 11
i2 12
i3 13
i4 14
i8 18
ff 21
fd 22
dc100 10
dc103 10.123
dc209 10.123456789
cc char
cv varchar
cvu varcharu8
t1 text1
t2 text2
t3 text3
t4 text4
enum1 b
set1 a,c
blob1 blob1
blob2 blob2
blob3 blob3
blob4 blob4
dd 2001-01-01 00:00:00
yy 2001
tm0 00:00:01
tm3 00:00:03.333
tm6 00:00:06.666666
dt0 2001-01-01 00:00:01
dt3 2001-01-03 00:00:01.333
dt6 2001-01-06 00:00:01.666666
ts0 2002-01-01 00:00:01
ts3 2002-01-03 00:00:01.333
ts6 2002-01-06 00:00:01.666666
DROP TABLE t2;
CALL p1('select');
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
def bit6 bit6 16 6 1 Y 32 0 63
def bit7 bit7 16 7 1 Y 32 0 63
def bit8 bit8 16 8 1 Y 32 0 63
def i1 i1 1 4 2 Y 32768 0 63
def i2 i2 2 6 2 Y 32768 0 63
def i3 i3 9 9 2 Y 32768 0 63
def i4 i4 3 11 2 Y 32768 0 63
def i8 i8 8 20 2 Y 32768 0 63
def ff ff 4 12 2 Y 32768 31 63
def fd fd 5 22 2 Y 32768 31 63
def dc100 dc100 246 11 2 Y 32768 0 63
def dc103 dc103 246 12 6 Y 32768 3 63
def dc209 dc209 246 22 12 Y 32768 9 63
def cc cc 254 10 4 Y 0 0 8
def cv cv 253 10 7 Y 0 0 8
def cvu cvu 253 10 9 Y 0 0 8
def t1 t1 252 255 5 Y 16 0 8
def t2 t2 252 65535 5 Y 16 0 8
def t3 t3 252 16777215 5 Y 16 0 8
def t4 t4 252 4294967295 5 Y 16 0 8
def enum1 enum1 254 1 1 Y 256 0 8
def set1 set1 254 5 3 Y 2048 0 8
def blob1 blob1 252 255 5 Y 144 0 63
def blob2 blob2 252 4294967295 5 Y 144 0 63
def blob3 blob3 252 16777215 5 Y 144 0 63
def blob4 blob4 252 4294967295 5 Y 144 0 63
def dd dd 12 19 19 Y 128 0 63
def yy yy 13 4 4 Y 32864 0 63
def tm0 tm0 11 10 8 Y 128 0 63
def tm3 tm3 11 14 12 Y 128 3 63
def tm6 tm6 11 17 15 Y 128 6 63
def dt0 dt0 12 19 19 Y 128 0 63
def dt3 dt3 12 23 23 Y 128 3 63
def dt6 dt6 12 26 26 Y 128 6 63
def ts0 ts0 7 19 19 Y 9376 0 63
def ts3 ts3 7 23 23 Y 160 3 63
def ts6 ts6 7 26 26 Y 160 6 63
bit6 0
bit7 A
bit8 ~
i1 11
i2 12
i3 13
i4 14
i8 18
ff 21
fd 22
dc100 10
dc103 10.123
dc209 10.123456789
cc char
cv varchar
cvu varcharu8
t1 text1
t2 text2
t3 text3
t4 text4
enum1 b
set1 a,c
blob1 blob1
blob2 blob2
blob3 blob3
blob4 blob4
dd 2001-01-01 00:00:00
yy 2001
tm0 00:00:01
tm3 00:00:03.333
tm6 00:00:06.666666
dt0 2001-01-01 00:00:01
dt3 2001-01-03 00:00:01.333
dt6 2001-01-06 00:00:01.666666
ts0 2002-01-01 00:00:01
ts3 2002-01-03 00:00:01.333
ts6 2002-01-06 00:00:01.666666
#
# VIEW
#
ALTER TABLE t1 RENAME t0;
CREATE VIEW t1 AS SELECT * FROM t0;
CALL p1('create');
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"bit6" bit(6) DEFAULT NULL,
"bit7" bit(7) DEFAULT NULL,
"bit8" bit(8) DEFAULT NULL,
"i1" tinyint(4) DEFAULT NULL,
"i2" smallint(6) DEFAULT NULL,
"i3" mediumint(9) DEFAULT NULL,
"i4" int(11) DEFAULT NULL,
"i8" bigint(20) DEFAULT NULL,
"ff" float DEFAULT NULL,
"fd" double DEFAULT NULL,
"dc100" decimal(10,0) DEFAULT NULL,
"dc103" decimal(10,3) DEFAULT NULL,
"dc209" decimal(20,9) DEFAULT NULL,
"cc" varchar(10) DEFAULT NULL,
"cv" varchar(10) DEFAULT NULL,
"cvu" varchar(10) CHARACTER SET utf8 DEFAULT NULL,
"t1" tinytext DEFAULT NULL,
"t2" text DEFAULT NULL,
"t3" mediumtext DEFAULT NULL,
"t4" longtext DEFAULT NULL,
"enum1" varchar(1) DEFAULT NULL,
"set1" varchar(5) DEFAULT NULL,
"blob1" tinyblob DEFAULT NULL,
"blob2" longblob DEFAULT NULL,
"blob3" mediumblob DEFAULT NULL,
"blob4" longblob DEFAULT NULL,
"dd" datetime DEFAULT NULL,
"yy" year(4) DEFAULT NULL,
"tm0" time DEFAULT NULL,
"tm3" time(3) DEFAULT NULL,
"tm6" time(6) DEFAULT NULL,
"dt0" datetime DEFAULT NULL,
"dt3" datetime(3) DEFAULT NULL,
"dt6" datetime(6) DEFAULT NULL,
"ts0" timestamp NULL DEFAULT NULL,
"ts3" timestamp(3) NULL DEFAULT NULL,
"ts6" timestamp(6) NULL DEFAULT NULL
)
SELECT * FROM t2;
bit6 0
bit7 A
bit8 ~
i1 11
i2 12
i3 13
i4 14
i8 18
ff 21
fd 22
dc100 10
dc103 10.123
dc209 10.123456789
cc char
cv varchar
cvu varcharu8
t1 text1
t2 text2
t3 text3
t4 text4
enum1 b
set1 a,c
blob1 blob1
blob2 blob2
blob3 blob3
blob4 blob4
dd 2001-01-01 00:00:00
yy 2001
tm0 00:00:01
tm3 00:00:03.333
tm6 00:00:06.666666
dt0 2001-01-01 00:00:01
dt3 2001-01-03 00:00:01.333
dt6 2001-01-06 00:00:01.666666
ts0 2002-01-01 00:00:01
ts3 2002-01-03 00:00:01.333
ts6 2002-01-06 00:00:01.666666
DROP TABLE t2;
CALL p1('select');
Catalog Database Table Table_alias Column Column_alias Type Length Max length Is_null Flags Decimals Charsetnr
def bit6 bit6 16 6 1 Y 32 0 63
def bit7 bit7 16 7 1 Y 32 0 63
def bit8 bit8 16 8 1 Y 32 0 63
def i1 i1 1 4 2 Y 32768 0 63
def i2 i2 2 6 2 Y 32768 0 63
def i3 i3 9 9 2 Y 32768 0 63
def i4 i4 3 11 2 Y 32768 0 63
def i8 i8 8 20 2 Y 32768 0 63
def ff ff 4 12 2 Y 32768 31 63
def fd fd 5 22 2 Y 32768 31 63
def dc100 dc100 246 11 2 Y 32768 0 63
def dc103 dc103 246 12 6 Y 32768 3 63
def dc209 dc209 246 22 12 Y 32768 9 63
def cc cc 254 10 4 Y 0 0 8
def cv cv 253 10 7 Y 0 0 8
def cvu cvu 253 10 9 Y 0 0 8
def t1 t1 252 255 5 Y 16 0 8
def t2 t2 252 65535 5 Y 16 0 8
def t3 t3 252 16777215 5 Y 16 0 8
def t4 t4 252 4294967295 5 Y 16 0 8
def enum1 enum1 254 1 1 Y 256 0 8
def set1 set1 254 5 3 Y 2048 0 8
def blob1 blob1 252 255 5 Y 144 0 63
def blob2 blob2 252 4294967295 5 Y 144 0 63
def blob3 blob3 252 16777215 5 Y 144 0 63
def blob4 blob4 252 4294967295 5 Y 144 0 63
def dd dd 12 19 19 Y 128 0 63
def yy yy 13 4 4 Y 32864 0 63
def tm0 tm0 11 10 8 Y 128 0 63
def tm3 tm3 11 14 12 Y 128 3 63
def tm6 tm6 11 17 15 Y 128 6 63
def dt0 dt0 12 19 19 Y 128 0 63
def dt3 dt3 12 23 23 Y 128 3 63
def dt6 dt6 12 26 26 Y 128 6 63
def ts0 ts0 7 19 19 Y 160 0 63
def ts3 ts3 7 23 23 Y 160 3 63
def ts6 ts6 7 26 26 Y 160 6 63
bit6 0
bit7 A
bit8 ~
i1 11
i2 12
i3 13
i4 14
i8 18
ff 21
fd 22
dc100 10
dc103 10.123
dc209 10.123456789
cc char
cv varchar
cvu varcharu8
t1 text1
t2 text2
t3 text3
t4 text4
enum1 b
set1 a,c
blob1 blob1
blob2 blob2
blob3 blob3
blob4 blob4
dd 2001-01-01 00:00:00
yy 2001
tm0 00:00:01
tm3 00:00:03.333
tm6 00:00:06.666666
dt0 2001-01-01 00:00:01
dt3 2001-01-03 00:00:01.333
dt6 2001-01-06 00:00:01.666666
ts0 2002-01-01 00:00:01
ts3 2002-01-03 00:00:01.333
ts6 2002-01-06 00:00:01.666666
DROP VIEW t1;
DROP TABLE t0;
DROP PROCEDURE p1;
#
# VIEW with subqueries
#
CREATE TABLE t1 (a INT,b INT);
INSERT INTO t1 VALUES (10,1),(20,2),(30,3),(40,4);
SELECT AVG(a) FROM t1;
AVG(a)
25.0000
CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1);
SELECT * FROM v1;
a b
30 1
40 1
CREATE PROCEDURE p1
AS
a v1.a%TYPE := 10;
b v1.b%TYPE := 1;
BEGIN
SELECT a,b;
END;
$$
CALL p1;
a b
10 1
DROP PROCEDURE p1;
CREATE FUNCTION f1 RETURN INT
AS
a v1.a%TYPE := 10;
b v1.b%TYPE := 1;
BEGIN
RETURN a+b;
END;
$$
SELECT f1();
f1()
11
DROP FUNCTION f1;
DROP VIEW v1;
DROP TABLE t1;
#
# %TYPE variables + INFORMATION_SCHEMA
#
CREATE PROCEDURE p1
AS
tables_table_name INFORMATION_SCHEMA.TABLES.TABLE_NAME%TYPE;
tables_table_rows INFORMATION_SCHEMA.TABLES.TABLE_ROWS%TYPE;
processlist_info INFORMATION_SCHEMA.PROCESSLIST.INFO%TYPE;
processlist_info_binary INFORMATION_SCHEMA.PROCESSLIST.INFO_BINARY%TYPE;
BEGIN
CREATE TABLE t1 AS SELECT
tables_table_name,
tables_table_rows,
processlist_info,
processlist_info_binary;
END;
$$
CALL p1();
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE "t1" (
"tables_table_name" varchar(64) CHARACTER SET utf8 DEFAULT NULL,
"tables_table_rows" bigint(21) unsigned DEFAULT NULL,
"processlist_info" longtext CHARACTER SET utf8 DEFAULT NULL,
"processlist_info_binary" blob DEFAULT NULL
)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# %TYPE + Table structure change
# Data type for both a0 and a1 is chosen in the very beginning
#
CREATE PROCEDURE p1
AS
a0 t1.a%TYPE;
BEGIN
ALTER TABLE t1 MODIFY a VARCHAR(10); -- This does not affect a1
DECLARE
a1 t1.a%TYPE;
BEGIN
CREATE TABLE t2 AS SELECT a0, a1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
END
$$
CREATE TABLE t1 (a INT);
CALL p1;
Table Create Table
t2 CREATE TABLE "t2" (
"a0" int(11) DEFAULT NULL,
"a1" int(11) DEFAULT NULL
)
DROP TABLE t1;
DROP PROCEDURE p1;
#
# %TYPE in parameters
#
CREATE TABLE t1 (a VARCHAR(10));
CREATE DATABASE test1;
CREATE TABLE test1.t1 (b SMALLINT);
CREATE PROCEDURE p1(a t1.a%TYPE, b test1.t1.b%TYPE)
AS
BEGIN
CREATE TABLE t2 AS SELECT a, b;
END;
$$
CALL p1('test', 123);
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE "t2" (
"a" varchar(10) DEFAULT NULL,
"b" smallint(6) DEFAULT NULL
)
SELECT * FROM t2;
a b
test 123
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE test1.t1;
DROP DATABASE test1;
DROP TABLE t1;
#
# %TYPE in a stored function variables and arguments
#
CREATE TABLE t1 (a INT);
SET sql_mode=ORACLE;
CREATE FUNCTION f1 (prm t1.a%TYPE) RETURN INT
AS
a t1.a%TYPE:= prm;
BEGIN
RETURN a;
END;
$$
SELECT f1(20);
f1(20)
20
DROP FUNCTION f1;
DROP TABLE t1;
#
# %TYPE in function RETURN clause is not supported yet
#
CREATE FUNCTION f1 RETURN t1.a%TYPE
AS
BEGIN
RETURN 0;
END;
$$
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 't1.a%TYPE
AS
BEGIN
RETURN 0;
END' at line 1
#
# End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#

View File

@@ -80,3 +80,21 @@ NULL NULL 2
NULL 1 1
1 1 0
DROP TABLE t1;
#
# MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
#
CREATE TABLE t1 (a INT, b INT, total INT);
CREATE TRIGGER tr1 BEFORE INSERT ON t1
FOR EACH ROW
DECLARE
va t1.a%TYPE:= :NEW.a;
vb t1.b%TYPE:= :NEW.b;
BEGIN
:NEW.total:= va + vb;
END;
$$
INSERT INTO t1 (a,b) VALUES (10, 20);
SELECT * FROM t1;
a b total
10 20 30
DROP TABLE t1;

View File

@@ -750,3 +750,36 @@ DROP PROCEDURE p1;
--echo #
--echo # End of MDEV-10597 Cursors with parameters
--echo #
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10),c DATETIME(3));
INSERT INTO t1 VALUES (1,'b1','2001-01-01 10:20:30.123');
INSERT INTO t1 VALUES (2,'b2','2001-01-02 10:20:30.123');
CREATE TABLE t2 LIKE t1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
v_a t1.a%TYPE;
v_b t1.b%TYPE;
v_c t1.c%TYPE;
CURSOR c IS SELECT a,b,c FROM t1;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_a, v_b, v_c;
EXIT WHEN c%NOTFOUND;
INSERT INTO t2 (a,b,c) VALUES (v_a, v_b, v_c);
END LOOP;
CLOSE c;
END;
$$
DELIMITER ;$$
CALL p1();
SELECT * FROM t2;
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t1;

View File

@@ -0,0 +1,285 @@
--source include/not_embedded.inc
SET sql_mode=ORACLE;
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
--echo #
--echo # Initiation:
--echo # - creating database db1
--echo # - creating user user1 with access rights to db1
--echo #
CREATE DATABASE db1;
CREATE TABLE db1.t1 (a INT, b VARCHAR(10));
CREATE USER user1;
GRANT ALL PRIVILEGES ON test.* TO user1;
connect (conn1,localhost,user1,,test);
SET sql_mode=ORACLE;
SELECT database();
SELECT user();
--echo #
--echo # Making sure that user1 does not have privileges to db1.t1
--echo #
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE TABLE db1.t1;
--error ER_TABLEACCESS_DENIED_ERROR
SHOW FIELDS IN db1.t1;
--echo #
--echo # Trigger: using %TYPE with a table we don't have access to
--echo #
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
--echo #
--echo # Stored procedure: Using %TYPE for with a table that we don't have access to
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT 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
--echo # DEFINER root, SQL SECURITY INVOKER
--echo #
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT 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
--echo #
connection default;
DELIMITER $$;
CREATE PROCEDURE p1()
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT 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
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
--error ER_TABLEACCESS_DENIED_ERROR
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY INVOKER
--echo #
connection default;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
SQL SECURITY INVOKER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
connection conn1;
--error ER_TABLEACCESS_DENIED_ERROR
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # Stored function: Using %TYPE for with a table that we don't have access to
--echo # DEFINER root, SQL SECURITY DEFINER
--echo #
connection default;
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE FUNCTION f1() RETURN INT
SQL SECURITY DEFINER
AS
a db1.t1.a%TYPE:=0;
BEGIN
RETURN OCTET_LENGTH(a);
END;
$$
DELIMITER ;$$
connection conn1;
SELECT f1();
DROP FUNCTION f1;
DROP TABLE t1;
connection default;
GRANT SELECT (a) ON db1.t1 TO user1;
connection conn1;
--echo #
--echo # Making sure that user1 has access to db1.t1.a, but not to db1.t1.b
--echo #
--error ER_TABLEACCESS_DENIED_ERROR
SHOW CREATE TABLE db1.t1;
SHOW FIELDS IN db1.t1;
--echo #
--echo # Trigger: Per-column privileges
--echo #
CREATE TABLE test.t1 (a INT, b INT);
INSERT INTO test.t1 (a,b) VALUES (10,20);
SELECT * FROM t1;
# %TYPE reference using a column we have access to
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE a db1.t1.a%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
# %TYPE reference using a column that we don't have access to
DELIMITER $$;
CREATE TRIGGER test.tr1 BEFORE INSERT ON test.t1 FOR EACH ROW
BEGIN
DECLARE b db1.t1.b%TYPE := 20;
BEGIN
:NEW.b := 10;
END;
END
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
INSERT INTO t1 (a) VALUES (10);
SELECT * FROM t1;
DROP TRIGGER tr1;
DROP TABLE t1;
--echo #
--echo # Stored procedure: Per-column privileges
--echo # DEFINER user1, SQL SECURITY DEFAULT
--echo #
DELIMITER $$;
CREATE PROCEDURE p1()
AS
a db1.t1.a%TYPE := 10;
BEGIN
SELECT a;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE PROCEDURE p1()
AS
b db1.t1.b%TYPE := 10;
BEGIN
SELECT b;
END;
$$
DELIMITER ;$$
--error ER_COLUMNACCESS_DENIED_ERROR
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # Clean up
--echo #
disconnect conn1;
connection default;
DROP USER user1;
DROP DATABASE db1;
--echo #
--echo # End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #

View File

@@ -1348,3 +1348,648 @@ DROP TABLE t1, t2;
--echo #
--echo # End of MDEV-10583 sql_mode=ORACLE: SQL%ROWCOUNT
--echo #
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
--echo #
--echo # Missing table
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1();
DROP PROCEDURE p1;
--echo #
--echo # Missing column
--echo #
CREATE TABLE t1 (b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
NULL;
END;
$$
DELIMITER ;$$
--error ER_BAD_FIELD_ERROR
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # One %TYPE variable
--echo #
CREATE TABLE t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
BEGIN
a:= 123;
SELECT a;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Two %TYPE variables, with a truncation warning on assignment
--echo #
CREATE TABLE t1 (a TINYINT, b INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
b t1.b%TYPE;
BEGIN
a:= 200;
b:= 200;
SELECT a, b;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # %TYPE variables for fields with various attributes
--echo #
CREATE TABLE t1 (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
a TINYINT NOT NULL,
b INT NOT NULL,
ts TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE(a)
);
DELIMITER $$;
CREATE PROCEDURE p1
AS
id t1.id%TYPE;
a t1.a%TYPE;
b t1.b%TYPE;
ts t1.ts%TYPE;
BEGIN
SELECT id, a, b, ts;
CREATE TABLE t2 AS SELECT id, a, b, ts;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # %TYPE + virtual columns
--echo #
#
# TODO: Changing 'a + 10' to 'a mod 10' make it fail, because
# it's Item::print() returns 'a % 10' which makes grammar conflict
# with cursor attributes
CREATE TABLE t1 (
a INT NOT NULL,
b VARCHAR(32),
c INT AS (a + 10) VIRTUAL,
d VARCHAR(5) AS (left(b,5)) PERSISTENT
);
DELIMITER $$;
CREATE PROCEDURE p1
AS
c t1.c%TYPE;
d t1.d%TYPE;
BEGIN
SELECT c, d;
CREATE TABLE t2 AS SELECT c, d;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # %TYPE + the ZEROFILL attribute
--echo #
CREATE TABLE t1 (
dz DECIMAL(10,3) ZEROFILL
);
DELIMITER $$;
CREATE PROCEDURE p1
AS
dzr t1.dz%TYPE := 10;
dzt DECIMAL(10,3) ZEROFILL := 10;
BEGIN
SELECT dzr, dzt;
CREATE TABLE t2 AS SELECT dzr,dzt;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
$$
DELIMITER ;$$
CALL p1();
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # Temporary tables shadow real tables for %TYPE purposes
--echo #
CREATE TABLE t1 (a VARCHAR(10));
INSERT INTO t1 VALUES ('t1');
CREATE TEMPORARY TABLE t1 (a INT);
INSERT INTO t1 VALUES (10);
SELECT * FROM t1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE:=11;
BEGIN
CREATE TABLE t2 AS SELECT a;
END;
$$
DELIMITER ;$$
--echo #
--echo # Should use INT(11) as %TYPE, as in the temporary table
--echo #
CALL p1();
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
SELECT * FROM t1;
DROP TEMPORARY TABLE t1;
SELECT * FROM t1;
--echo #
--echo # Should use VARCHAR(10) as %TYPE, as in the real table
--echo #
CALL p1();
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # t1.a%TYPE searches for "t1" in the current database
--echo #
CREATE TABLE t1 (a VARCHAR(10));
CREATE DATABASE test1;
CREATE TABLE test1.t1 (a INT);
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE:=11;
BEGIN
CREATE TABLE test.t2 AS SELECT a;
END;
$$
DELIMITER ;$$
--echo #
--echo # This interprets t1.a%TYPE as VARCHAR(10), as in test.t1.a
--echo #
USE test;
CALL test.p1();
SHOW CREATE TABLE test.t2;
DROP TABLE test.t2;
--echo #
--echo # This interprets t1.a%TYPE as INT, as in test1.t1.a
--echo #
USE test1;
CALL test.p1();
SHOW CREATE TABLE test.t2;
DROP TABLE test.t2;
--echo #
--echo # Error if there is no an active database
--echo #
DROP DATABASE test1;
--error ER_NO_DB_ERROR
CALL test.p1();
USE test;
DROP PROCEDURE p1;
DROP TABLE t1;
--echo #
--echo # A reference to a table in a non-existing database
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
a test1.t1.a%TYPE;
BEGIN
CREATE TABLE t1 AS SELECT a;
END;
$$
DELIMITER ;$$
--error ER_NO_SUCH_TABLE
CALL p1;
DROP PROCEDURE p1;
--echo #
--echo # A reference to a table in a different database
--echo #
CREATE TABLE t1(a INT);
CREATE DATABASE test1;
CREATE TABLE test1.t1 (a VARCHAR(10));
DELIMITER $$;
CREATE PROCEDURE p1
AS
a t1.a%TYPE;
b test1.t1.a%TYPE;
BEGIN
CREATE TABLE t2 AS SELECT a,b;
END;
$$
DELIMITER ;$$
CALL p1;
SHOW CREATE TABLE t2;
DROP PROCEDURE p1;
DROP TABLE t2;
DROP DATABASE test1;
DROP TABLE t1;
--echo #
--echo # Using a table before it appears in a %TYPE declaration + multiple %TYPE declarations
--echo #
CREATE TABLE t1 (a INT, b VARCHAR(10));
INSERT INTO t1 (a,b) VALUES (10,'b10');
DELIMITER $$;
CREATE PROCEDURE p1
AS
BEGIN
INSERT INTO t1 (a,b) VALUES (11, 'b11');
SELECT * FROM t1;
DECLARE
va t1.a%TYPE:= 30;
vb t1.b%TYPE:= 'b30';
BEGIN
INSERT INTO t1 (a,b) VALUES (12,'b12');
SELECT * FROM t1;
INSERT INTO t1 (a,b) VALUES (va, vb);
SELECT * FROM t1;
END;
DECLARE
va t1.a%TYPE:= 40;
vb t1.b%TYPE:= 'b40';
BEGIN
INSERT INTO t1 (a,b) VALUES (va,vb);
SELECT * FROM t1;
END;
END;
$$
DELIMITER ;$$
CALL p1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # %TYPE variables + TABLE vs VIEW
--echo #
CREATE TABLE t1 (
bit6 BIT(6),
bit7 BIT(7),
bit8 BIT(8),
i1 TINYINT,
i2 SMALLINT,
i3 MEDIUMINT,
i4 INT,
i8 BIGINT,
ff FLOAT,
fd DOUBLE,
cc CHAR(10),
cv VARCHAR(10),
cvu VARCHAR(10) CHARACTER SET utf8,
t1 TINYTEXT,
t2 TEXT,
t3 MEDIUMTEXT,
t4 LONGTEXT,
enum1 ENUM('a','b','c'),
set1 SET('a','b','c'),
blob1 TINYBLOB,
blob2 BLOB,
blob3 MEDIUMBLOB,
blob4 LONGBLOB,
yy YEAR,
dd DATE,
tm0 TIME,
tm3 TIME(3),
tm6 TIME(6),
dt0 DATETIME,
dt3 DATETIME(3),
dt6 DATETIME(6),
ts0 TIMESTAMP,
ts3 TIMESTAMP(3),
ts6 TIMESTAMP(6),
dc100 DECIMAL(10,0),
dc103 DECIMAL(10,3),
dc209 DECIMAL(20,9)
);
DELIMITER $$;
CREATE PROCEDURE p1(command enum('create','select'))
AS
bit6 t1.bit6%TYPE := 0x30;
bit7 t1.bit7%TYPE := 0x41;
bit8 t1.bit8%TYPE := 0x7E;
i1 t1.i1%TYPE := 11;
i2 t1.i2%TYPE := 12;
i3 t1.i3%TYPE := 13;
i4 t1.i4%TYPE := 14;
i8 t1.i8%TYPE := 18;
ff t1.ff%TYPE := 21;
fd t1.fd%TYPE := 22;
cc t1.cc%TYPE := 'char';
cv t1.cv%TYPE := 'varchar';
cvu t1.cvu%TYPE := 'varcharu8';
t1 t1.t1%TYPE := 'text1';
t2 t1.t2%TYPE := 'text2';
t3 t1.t3%TYPE := 'text3';
t4 t1.t4%TYPE := 'text4';
enum1 t1.enum1%TYPE := 'b';
set1 t1.set1%TYPE := 'a,c';
blob1 t1.blob1%TYPE := 'blob1';
blob2 t1.blob2%TYPE := 'blob2';
blob3 t1.blob3%TYPE := 'blob3';
blob4 t1.blob4%TYPE := 'blob4';
yy t1.yy%TYPE := 2001;
dd t1.dd%TYPE := '2001-01-01';
tm0 t1.tm0%TYPE := '00:00:01';
tm3 t1.tm3%TYPE := '00:00:03.333';
tm6 t1.tm6%TYPE := '00:00:06.666666';
dt0 t1.dt0%TYPE := '2001-01-01 00:00:01';
dt3 t1.dt3%TYPE := '2001-01-03 00:00:01.333';
dt6 t1.dt6%TYPE := '2001-01-06 00:00:01.666666';
ts0 t1.ts0%TYPE := '2002-01-01 00:00:01';
ts3 t1.ts3%TYPE := '2002-01-03 00:00:01.333';
ts6 t1.ts6%TYPE := '2002-01-06 00:00:01.666666';
dc100 t1.dc100%TYPE := 10;
dc103 t1.dc103%TYPE := 10.123;
dc209 t1.dc209%TYPE := 10.123456789;
BEGIN
CASE
WHEN command='create' THEN
CREATE TABLE t2 AS SELECT
bit6, bit7, bit8,
i1,i2,i3,i4,i8,
ff,fd, dc100, dc103, dc209,
cc,cv,cvu,
t1,t2,t3,t4,
enum1, set1,
blob1, blob2, blob3, blob4,
dd, yy,
tm0, tm3, tm6,
dt0, dt3, dt6,
ts0, ts3, ts6;
WHEN command='select' THEN
SELECT
bit6, bit7, bit8,
i1,i2,i3,i4,i8,
ff,fd, dc100, dc103, dc209,
cc,cv,cvu,
t1,t2,t3,t4,
enum1, set1,
blob1, blob2, blob3, blob4,
dd, yy,
tm0, tm3, tm6,
dt0, dt3, dt6,
ts0, ts3, ts6;
END CASE;
END;
$$
DELIMITER ;$$
--echo #
--echo # TABLE
--echo #
CALL p1('create');
SHOW CREATE TABLE t2;
--vertical_results
SELECT * FROM t2;
--horizontal_results
DROP TABLE t2;
--disable_ps_protocol
--enable_metadata
--vertical_results
CALL p1('select');
--horizontal_results
--disable_metadata
--enable_ps_protocol
--echo #
--echo # VIEW
--echo #
ALTER TABLE t1 RENAME t0;
CREATE VIEW t1 AS SELECT * FROM t0;
CALL p1('create');
SHOW CREATE TABLE t2;
--vertical_results
SELECT * FROM t2;
--horizontal_results
DROP TABLE t2;
--disable_ps_protocol
--enable_metadata
--vertical_results
CALL p1('select');
--horizontal_results
--disable_metadata
--enable_ps_protocol
DROP VIEW t1;
DROP TABLE t0;
DROP PROCEDURE p1;
--echo #
--echo # VIEW with subqueries
--echo #
CREATE TABLE t1 (a INT,b INT);
INSERT INTO t1 VALUES (10,1),(20,2),(30,3),(40,4);
SELECT AVG(a) FROM t1;
CREATE VIEW v1 AS SELECT a,1 as b FROM t1 WHERE a>(SELECT AVG(a) FROM t1) AND b>(SELECT 1);
SELECT * FROM v1;
DELIMITER $$;
CREATE PROCEDURE p1
AS
a v1.a%TYPE := 10;
b v1.b%TYPE := 1;
BEGIN
SELECT a,b;
END;
$$
DELIMITER ;$$
CALL p1;
DROP PROCEDURE p1;
DELIMITER $$;
CREATE FUNCTION f1 RETURN INT
AS
a v1.a%TYPE := 10;
b v1.b%TYPE := 1;
BEGIN
RETURN a+b;
END;
$$
DELIMITER ;$$
SELECT f1();
DROP FUNCTION f1;
DROP VIEW v1;
DROP TABLE t1;
--echo #
--echo # %TYPE variables + INFORMATION_SCHEMA
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
tables_table_name INFORMATION_SCHEMA.TABLES.TABLE_NAME%TYPE;
tables_table_rows INFORMATION_SCHEMA.TABLES.TABLE_ROWS%TYPE;
processlist_info INFORMATION_SCHEMA.PROCESSLIST.INFO%TYPE;
processlist_info_binary INFORMATION_SCHEMA.PROCESSLIST.INFO_BINARY%TYPE;
BEGIN
CREATE TABLE t1 AS SELECT
tables_table_name,
tables_table_rows,
processlist_info,
processlist_info_binary;
END;
$$
DELIMITER ;$$
CALL p1();
SHOW CREATE TABLE t1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # %TYPE + Table structure change
--echo # Data type for both a0 and a1 is chosen in the very beginning
--echo #
DELIMITER $$;
CREATE PROCEDURE p1
AS
a0 t1.a%TYPE;
BEGIN
ALTER TABLE t1 MODIFY a VARCHAR(10); -- This does not affect a1
DECLARE
a1 t1.a%TYPE;
BEGIN
CREATE TABLE t2 AS SELECT a0, a1;
SHOW CREATE TABLE t2;
DROP TABLE t2;
END;
END
$$
DELIMITER ;$$
CREATE TABLE t1 (a INT);
CALL p1;
DROP TABLE t1;
DROP PROCEDURE p1;
--echo #
--echo # %TYPE in parameters
--echo #
CREATE TABLE t1 (a VARCHAR(10));
CREATE DATABASE test1;
CREATE TABLE test1.t1 (b SMALLINT);
DELIMITER $$;
CREATE PROCEDURE p1(a t1.a%TYPE, b test1.t1.b%TYPE)
AS
BEGIN
CREATE TABLE t2 AS SELECT a, b;
END;
$$
DELIMITER ;$$
CALL p1('test', 123);
SHOW CREATE TABLE t2;
SELECT * FROM t2;
DROP TABLE t2;
DROP PROCEDURE p1;
DROP TABLE test1.t1;
DROP DATABASE test1;
DROP TABLE t1;
--echo #
--echo # %TYPE in a stored function variables and arguments
--echo #
CREATE TABLE t1 (a INT);
SET sql_mode=ORACLE;
DELIMITER $$;
CREATE FUNCTION f1 (prm t1.a%TYPE) RETURN INT
AS
a t1.a%TYPE:= prm;
BEGIN
RETURN a;
END;
$$
DELIMITER ;$$
SELECT f1(20);
DROP FUNCTION f1;
DROP TABLE t1;
--echo #
--echo # %TYPE in function RETURN clause is not supported yet
--echo #
DELIMITER $$;
--error ER_PARSE_ERROR
CREATE FUNCTION f1 RETURN t1.a%TYPE
AS
BEGIN
RETURN 0;
END;
$$
DELIMITER ;$$
--echo #
--echo # End of MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #

View File

@@ -84,3 +84,23 @@ INSERT INTO t1 VALUES (NULL, 1, NULL);
INSERT INTO t1 VALUES (1, 1, NULL);
SELECT * FROM t1;
DROP TABLE t1;
--echo #
--echo # MDEV-10577 sql_mode=ORACLE: %TYPE in variable declarations
--echo #
CREATE TABLE t1 (a INT, b INT, total INT);
DELIMITER $$;
CREATE TRIGGER tr1 BEFORE INSERT ON t1
FOR EACH ROW
DECLARE
va t1.a%TYPE:= :NEW.a;
vb t1.b%TYPE:= :NEW.b;
BEGIN
:NEW.total:= va + vb;
END;
$$
DELIMITER ;$$
INSERT INTO t1 (a,b) VALUES (10, 20);
SELECT * FROM t1;
DROP TABLE t1;

View File

@@ -3853,7 +3853,7 @@ public:
Column_definition(const char *name, enum_field_types type):
field_name(name),
comment(null_lex_str),
on_update(0), sql_type(type), length(0), decimals(0),
on_update(NULL), sql_type(type), length(0), decimals(0),
flags(0), pack_length(0), key_length(0), unireg_check(Field::NONE),
interval(0), charset(&my_charset_bin),
srid(0), geom_type(Field::GEOM_GEOMETRY),
@@ -3948,6 +3948,43 @@ public:
return unireg_check == Field::TIMESTAMP_DN_FIELD
|| unireg_check == Field::TIMESTAMP_DNUN_FIELD;
}
// Replace the entire value by another definition
void set_column_definition(const Column_definition *def)
{
*this= *def;
}
};
/**
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;
Data type references to other object types will be added soon, e.g.:
- DECLARE a table_name%ROWTYPE;
- DECLARE a cursor_name%ROWTYPE;
- DECLARE a record_name%TYPE;
- DECLARE a variable_name%TYPE;
*/
class Spvar_definition: public Column_definition
{
class Qualified_column_ident *m_column_type_ref; // for %TYPE
public:
Spvar_definition()
:m_column_type_ref(NULL) { }
bool is_column_type_ref() const { return m_column_type_ref != 0; }
class Qualified_column_ident *column_type_ref() const
{
return m_column_type_ref;
}
void set_column_type_ref(class Qualified_column_ident *ref)
{
m_column_type_ref= ref;
}
};

View File

@@ -6100,8 +6100,11 @@ Field *Item::make_string_field(TABLE *table)
/**
Create a field based on field_type of argument.
For now, this is only used to create a field for
IFNULL(x,something) and time functions
This is used to create a field for
- IFNULL(x,something)
- time functions
- prepared statement placeholders
- SP variables with data type references: DECLARE a t1.a%TYPE;
@retval
NULL error

View File

@@ -2122,6 +2122,40 @@ public:
Item *get_copy(THD *thd, MEM_ROOT *mem_root) { return 0; }
};
/**
An Item_splocal variant whose data type becomes known only at
sp_rcontext creation time, e.g. "DECLARE var1 t1.col1%TYPE".
*/
class Item_splocal_with_delayed_data_type: public Item_splocal
{
public:
Item_splocal_with_delayed_data_type(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)
{ }
bool fix_fields(THD *thd, Item **it)
{
if (Item_splocal::fix_fields(thd, it))
return true;
set_handler(this_item()->type_handler());
return false;
}
/*
Override the inherited create_field_for_create_select(),
because we want to preserve the exact data type for:
DECLARE a t1.a%TYPE;
CREATE TABLE t1 AS SELECT a;
The inherited implementation would create a column
based on result_type(), which is less exact.
*/
Field *create_field_for_create_select(TABLE *table)
{ return tmp_table_field_from_field_type(table, false, true); }
};
/*****************************************************************************
Item_splocal inline implementation.
*****************************************************************************/

View File

@@ -1396,6 +1396,41 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
#endif // ! NO_EMBEDDED_ACCESS_CHECKS
/**
Create rcontext using the routine security.
This is important for sql_mode=ORACLE to make sure that the invoker has
access to the tables mentioned in the %TYPE references.
In non-Oracle sql_modes we do not need access to any tables,
so we can omit the security context switch for performance purposes.
@param thd
@param sphead
@param is_proc
@param root_pctx
@param ret_value
@retval NULL - error (access denided or EOM)
@retval !NULL - success (the invoker has rights to all %TYPE tables)
*/
sp_rcontext *sp_head::rcontext_create(THD *thd, bool is_proc, Field *ret_value)
{
bool has_column_type_refs= m_flags & HAS_COLUMN_TYPE_REFS;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
Security_context *save_security_ctx;
if (has_column_type_refs &&
set_routine_security_ctx(thd, this, is_proc, &save_security_ctx))
return NULL;
#endif
sp_rcontext *res= sp_rcontext::create(thd, m_pcont, ret_value,
has_column_type_refs);
#ifndef NO_EMBEDDED_ACCESS_CHECKS
if (has_column_type_refs)
m_security_ctx.restore_security_context(thd, save_security_ctx);
#endif
return res;
}
/**
Execute trigger stored program.
@@ -1493,7 +1528,8 @@ sp_head::execute_trigger(THD *thd,
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL,
m_flags & HAS_COLUMN_TYPE_REFS)))
{
err_status= TRUE;
goto err_with_cleanup;
@@ -1608,7 +1644,7 @@ sp_head::execute_function(THD *thd, Item **argp, uint argcount,
init_sql_alloc(&call_mem_root, MEM_ROOT_BLOCK_SIZE, 0, MYF(0));
thd->set_n_backup_active_arena(&call_arena, &backup_arena);
if (!(nctx= sp_rcontext::create(thd, m_pcont, return_value_fld)))
if (!(nctx= rcontext_create(thd, false, return_value_fld)))
{
thd->restore_active_arena(&call_arena, &backup_arena);
err_status= TRUE;
@@ -1823,7 +1859,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
if (! octx)
{
/* Create a temporary old context. */
if (!(octx= sp_rcontext::create(thd, m_pcont, NULL)))
if (!(octx= rcontext_create(thd, true, NULL)))
{
DBUG_PRINT("error", ("Could not create octx"));
DBUG_RETURN(TRUE);
@@ -1838,7 +1874,7 @@ sp_head::execute_procedure(THD *thd, List<Item> *args)
thd->spcont->callers_arena= thd;
}
if (!(nctx= sp_rcontext::create(thd, m_pcont, NULL)))
if (!(nctx= rcontext_create(thd, true, NULL)))
{
delete nctx; /* Delete nctx if it was init() that failed. */
thd->spcont= save_spcont;

View File

@@ -165,7 +165,11 @@ public:
b) because in CONTAINS SQL case they don't provide enough
information anyway.
*/
MODIFIES_DATA= 4096
MODIFIES_DATA= 4096,
/*
Marks routines that have column type references: DECLARE a t1.a%TYPE;
*/
HAS_COLUMN_TYPE_REFS= 8192
};
stored_procedure_type m_type;
@@ -201,6 +205,9 @@ public:
{
m_sp_cache_version= version_arg;
}
sp_rcontext *rcontext_create(THD *thd, bool is_proc, Field *ret_value);
private:
/**
Version of the stored routine cache at the moment when the
@@ -539,7 +546,8 @@ public:
/**
Check and prepare an instance of Column_definition for field creation
(fill all necessary attributes).
(fill all necessary attributes), for variables, parameters and
function return values.
@param[in] thd Thread handle
@param[in] lex Yacc parsing context
@@ -553,6 +561,32 @@ public:
return field_def->check(thd) ||
field_def->sp_prepare_create_field(thd, mem_root);
}
/**
Check and prepare a Column_definition for a variable or a parameter.
*/
bool fill_spvar_definition(THD *thd, Column_definition *def)
{
if (fill_field_definition(thd, def))
return true;
def->pack_flag|= FIELDFLAG_MAYBE_NULL;
return false;
}
bool fill_spvar_definition(THD *thd, Column_definition *def, const char *name)
{
def->field_name= name;
return fill_spvar_definition(thd, def);
}
/**
Set a column type reference for a parameter definition
*/
void fill_spvar_using_type_reference(sp_variable *spvar,
Qualified_column_ident *ref)
{
spvar->field_def.set_column_type_ref(ref);
spvar->field_def.field_name= spvar->name.str;
m_flags|= sp_head::HAS_COLUMN_TYPE_REFS;
}
void set_info(longlong created, longlong modified,
st_sp_chistics *chistics, sql_mode_t sql_mode);

View File

@@ -533,7 +533,7 @@ const sp_pcursor *sp_pcontext::find_cursor(const LEX_STRING name,
void sp_pcontext::retrieve_field_definitions(
List<Column_definition> *field_def_lst) const
List<Spvar_definition> *field_def_lst) const
{
/* Put local/context fields in the result list. */

View File

@@ -57,7 +57,7 @@ public:
Item *default_value;
/// Full type information (field meta-data) of the SP-variable.
Column_definition field_def;
Spvar_definition field_def;
/// Field-type of the SP-variable.
enum_field_types sql_type() const { return field_def.sql_type; }
@@ -436,7 +436,7 @@ public:
/// context and its children.
///
/// @param field_def_lst[out] Container to store type information.
void retrieve_field_definitions(List<Column_definition> *field_def_lst) const;
void retrieve_field_definitions(List<Spvar_definition> *field_def_lst) const;
/// Find SP-variable by name.
///

View File

@@ -26,6 +26,9 @@
#include "sp_rcontext.h"
#include "sp_pcontext.h"
#include "sql_select.h" // create_virtual_tmp_table
#include "sql_base.h" // open_tables_only_view_structure
#include "sql_acl.h" // SELECT_ACL
#include "sql_parse.h" // check_table_access
///////////////////////////////////////////////////////////////////////////
// sp_rcontext implementation.
@@ -59,7 +62,8 @@ sp_rcontext::~sp_rcontext()
sp_rcontext *sp_rcontext::create(THD *thd,
const sp_pcontext *root_parsing_ctx,
Field *return_value_fld)
Field *return_value_fld,
bool resolve_type_refs)
{
sp_rcontext *ctx= new (thd->mem_root) sp_rcontext(root_parsing_ctx,
return_value_fld,
@@ -68,9 +72,13 @@ sp_rcontext *sp_rcontext::create(THD *thd,
if (!ctx)
return NULL;
List<Spvar_definition> field_def_lst;
ctx->m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
if (ctx->alloc_arrays(thd) ||
ctx->init_var_table(thd) ||
ctx->init_var_items(thd))
(resolve_type_refs && ctx->resolve_type_refs(thd, field_def_lst)) ||
ctx->init_var_table(thd, field_def_lst) ||
ctx->init_var_items(thd, field_def_lst))
{
delete ctx;
return NULL;
@@ -102,28 +110,107 @@ bool sp_rcontext::alloc_arrays(THD *thd)
}
bool sp_rcontext::init_var_table(THD *thd)
bool sp_rcontext::init_var_table(THD *thd,
List<Spvar_definition> &field_def_lst)
{
List<Column_definition> field_def_lst;
if (!m_root_parsing_ctx->max_var_index())
return false;
m_root_parsing_ctx->retrieve_field_definitions(&field_def_lst);
DBUG_ASSERT(field_def_lst.elements == m_root_parsing_ctx->max_var_index());
if (!(m_var_table= create_virtual_tmp_table(thd, field_def_lst)))
return true;
m_var_table->copy_blobs= true;
m_var_table->alias.set("", 0, m_var_table->alias.charset());
return false;
}
bool sp_rcontext::init_var_items(THD *thd)
/**
Check if we have access to use a column as a %TYPE reference.
@return false - OK
@return true - access denied
*/
static inline bool
check_column_grant_for_type_ref(THD *thd, TABLE_LIST *table_list,
const Qualified_column_ident *col)
{
#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);
#else
return false;
#endif
}
/**
This method implementation is very close to fill_schema_table_by_open().
*/
bool sp_rcontext::resolve_type_ref(THD *thd, Column_definition *def,
Qualified_column_ident *ref)
{
Open_tables_backup open_tables_state_backup;
thd->reset_n_backup_open_tables_state(&open_tables_state_backup);
TABLE_LIST *table_list;
Field *src;
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 %TYPE 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()))
{
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)))
{
*def= Column_definition(thd, src, NULL/*No defaults,no constraints*/);
def->flags&= (uint) ~NOT_NULL_FLAG;
rc= def->sp_prepare_create_field(thd, thd->mem_root);
}
}
else
my_error(ER_BAD_FIELD_ERROR, MYF(0), ref->m_column.str, ref->table.str);
}
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);
Spvar_definition *def;
while ((def= it++))
{
if (def->is_column_type_ref() &&
resolve_type_ref(thd, def, def->column_type_ref()))
return true;
}
return false;
};
bool sp_rcontext::init_var_items(THD *thd,
List<Spvar_definition> &field_def_lst)
{
uint num_vars= m_root_parsing_ctx->max_var_index();
@@ -360,8 +447,9 @@ uint sp_rcontext::exit_handler(Diagnostics_area *da)
}
int sp_rcontext::set_variable(THD *thd, Field *field, Item **value)
int sp_rcontext::set_variable(THD *thd, uint idx, Item **value)
{
Field *field= m_var_table->field[idx];
if (!value)
{
field->set_null();

View File

@@ -70,7 +70,8 @@ public:
/// @return valid sp_rcontext object or NULL in case of OOM-error.
static sp_rcontext *create(THD *thd,
const sp_pcontext *root_parsing_ctx,
Field *return_value_fld);
Field *return_value_fld,
bool resolve_type_refs);
~sp_rcontext();
@@ -185,8 +186,7 @@ public:
// SP-variables.
/////////////////////////////////////////////////////////////////////////
int set_variable(THD *thd, uint var_idx, Item **value)
{ return set_variable(thd, m_var_table->field[var_idx], value); }
int set_variable(THD *thd, uint var_idx, Item **value);
Item *get_item(uint var_idx) const
{ return m_var_items[var_idx]; }
@@ -334,7 +334,11 @@ private:
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
bool init_var_table(THD *thd);
bool init_var_table(THD *thd, List<Spvar_definition> &defs);
bool resolve_type_refs(THD *, List<Spvar_definition> &defs);
bool resolve_type_ref(THD *thd, Column_definition *def,
Qualified_column_ident *ref);
/// Create and initialize an Item-adapter (Item_field) for each SP-var field.
///
@@ -343,7 +347,7 @@ private:
/// @return error flag.
/// @retval false on success.
/// @retval true on error.
bool init_var_items(THD *thd);
bool init_var_items(THD *thd, List<Spvar_definition> &defs);
/// Create an instance of appropriate Item_cache class depending on the
/// specified type in the callers arena.
@@ -357,8 +361,6 @@ private:
/// @return Pointer to valid object on success, or NULL in case of error.
Item_cache *create_case_expr_holder(THD *thd, const Item *item) const;
int set_variable(THD *thd, Field *field, Item **value);
private:
/// Top-level (root) parsing context for this runtime context.
const sp_pcontext *m_root_parsing_ctx;

View File

@@ -4763,6 +4763,51 @@ end:
}
/**
Open a table to read its structure, e.g. for:
- SHOW FIELDS
- delayed SP variable data type definition: DECLARE a t1.a%TYPE
The flag MYSQL_OPEN_GET_NEW_TABLE is passed to make %TYPE work
in stored functions, as during a stored function call
(e.g. in a SELECT query) the tables referenced in %TYPE can already be locked,
and attempt to open it again would return an error in open_table().
The flag MYSQL_OPEN_GET_NEW_TABLE is not really needed for
SHOW FIELDS or for a "CALL sp()" statement, but it's not harmful,
so let's pass it unconditionally.
*/
bool open_tables_only_view_structure(THD *thd, TABLE_LIST *table_list,
bool can_deadlock)
{
DBUG_ENTER("open_tables_only_view_structure");
/*
Let us set fake sql_command so views won't try to merge
themselves into main statement. If we don't do this,
SELECT * from information_schema.xxxx will cause problems.
SQLCOM_SHOW_FIELDS is used because it satisfies
'LEX::only_view_structure()'.
*/
enum_sql_command save_sql_command= thd->lex->sql_command;
thd->lex->sql_command= SQLCOM_SHOW_FIELDS;
bool rc= (thd->open_temporary_tables(table_list) ||
open_normal_and_derived_tables(thd, table_list,
(MYSQL_OPEN_IGNORE_FLUSH |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
MYSQL_OPEN_GET_NEW_TABLE |
(can_deadlock ?
MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
DT_PREPARE | DT_CREATE));
/*
Restore old value of sql_command back as it is being looked at in
process_table() function.
*/
thd->lex->sql_command= save_sql_command;
DBUG_RETURN(rc);
}
/*
Mark all real tables in the list as free for reuse.

View File

@@ -254,6 +254,8 @@ TABLE *open_n_lock_single_table(THD *thd, TABLE_LIST *table_l,
Prelocking_strategy *prelocking_strategy);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags,
uint dt_phases);
bool open_tables_only_view_structure(THD *thd, TABLE_LIST *tables,
bool can_deadlock);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
int decide_logging_format(THD *thd, TABLE_LIST *tables);
void close_thread_table(THD *thd, TABLE **table_ptr);

View File

@@ -5370,6 +5370,26 @@ public:
}
};
class Qualified_column_ident: public Table_ident
{
public:
LEX_STRING m_column;
public:
Qualified_column_ident(const LEX_STRING table, const LEX_STRING column)
:Table_ident(table),
m_column(column)
{ }
Qualified_column_ident(THD *thd,
const LEX_STRING db,
const LEX_STRING table,
const LEX_STRING column)
:Table_ident(thd, db, table, false),
m_column(column)
{ }
};
// this is needed for user_vars hash
class user_var_entry
{

View File

@@ -5098,11 +5098,7 @@ sp_variable *LEX::sp_param_init(LEX_STRING name)
bool LEX::sp_param_fill_definition(sp_variable *spvar)
{
if (sphead->fill_field_definition(thd, last_field))
return true;
spvar->field_def.field_name= spvar->name.str;
spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
return false;
return sphead->fill_spvar_definition(thd, last_field, spvar->name.str);
}
@@ -5222,7 +5218,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,
const Column_definition *cdef,
Item *dflt_value_item)
{
uint num_vars= spcont->context_var_count();
@@ -5241,16 +5237,15 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
if (!spvar)
return true;
if (!last)
spvar->field_def= cdef;
spvar->default_value= dflt_value_item;
spvar->field_def.field_name= spvar->name.str;
if (sphead->fill_field_definition(thd, &spvar->field_def))
return true;
spvar->field_def.pack_flag |= FIELDFLAG_MAYBE_NULL;
if (cdef)
{
if (!last)
spvar->field_def.set_column_definition(cdef);
if (sphead->fill_spvar_definition(thd, &spvar->field_def, spvar->name.str))
return true;
}
/* The last instruction is responsible for freeing LEX. */
sp_instr_set *is= new (this->thd->mem_root)
@@ -5266,6 +5261,23 @@ bool LEX::sp_variable_declarations_finalize(THD *thd, int nvars,
}
bool
LEX::sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
Qualified_column_ident *ref,
Item *def)
{
uint num_vars= spcont->context_var_count();
for (uint i= num_vars - nvars; i < num_vars; i++)
{
sp_variable *spvar= spcont->find_context_variable(i);
spvar->field_def.set_column_type_ref(ref);
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);
}
/**********************************************************************
The FOR LOOP statement
@@ -5295,8 +5307,9 @@ sp_variable *LEX::sp_add_for_loop_variable(THD *thd, const LEX_STRING name,
{
sp_variable *spvar= spcont->add_variable(thd, name);
spcont->declare_var_boundary(1);
spvar->field_def= Column_definition(spvar->name.str, MYSQL_TYPE_LONGLONG);
if (sp_variable_declarations_finalize(thd, 1, spvar->field_def, value))
spvar->field_def.field_name= spvar->name.str;
spvar->field_def.sql_type= MYSQL_TYPE_LONGLONG;
if (sp_variable_declarations_finalize(thd, 1, NULL, value))
return NULL;
return spvar;
}
@@ -6016,10 +6029,14 @@ Item *LEX::create_item_ident_sp(THD *thd, LEX_STRING name,
return NULL;
}
Item_splocal *splocal;
splocal= new (thd->mem_root) Item_splocal(thd, name,
spv->offset, spv->sql_type(),
start_in_q, length_in_q);
Item_splocal *splocal= spv->field_def.is_column_type_ref() ?
new (thd->mem_root) Item_splocal_with_delayed_data_type(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);
if (splocal == NULL)
return NULL;
#ifndef DBUG_OFF

View File

@@ -3110,8 +3110,11 @@ public:
bool set_variable(struct sys_var_with_base *variable, Item *item);
void sp_variable_declarations_init(THD *thd, int nvars);
bool sp_variable_declarations_finalize(THD *thd, int nvars,
const Column_definition &cdef,
const Column_definition *cdef,
Item *def);
bool sp_variable_declarations_with_ref_finalize(THD *thd, int nvars,
Qualified_column_ident *col,
Item *def);
bool sp_handler_declaration_init(THD *thd, int type);
bool sp_handler_declaration_finalize(THD *thd, int type);

View File

@@ -17086,11 +17086,11 @@ bool Virtual_tmp_table::init(uint field_count)
};
bool Virtual_tmp_table::add(List<Column_definition> &field_list)
bool Virtual_tmp_table::add(List<Spvar_definition> &field_list)
{
/* Create all fields and calculate the total length of record */
Column_definition *cdef; /* column definition */
List_iterator_fast<Column_definition> it(field_list);
Spvar_definition *cdef; /* column definition */
List_iterator_fast<Spvar_definition> it(field_list);
for ( ; (cdef= it++); )
{
Field *tmp;

View File

@@ -2032,6 +2032,8 @@ public:
bzero(this, sizeof(*this));
temp_pool_slot= MY_BIT_NONE;
in_use= thd;
copy_blobs= true;
alias.set("", 0, &my_charset_bin);
}
~Virtual_tmp_table()
@@ -2075,11 +2077,11 @@ public:
}
/**
Add fields from a Column_definition list
Add fields from a Spvar_definition list
@returns false - on success.
@returns true - on error.
*/
bool add(List<Column_definition> &field_list);
bool add(List<Spvar_definition> &field_list);
/**
Open a virtual table for read/write:
@@ -2117,7 +2119,7 @@ public:
*/
inline TABLE *
create_virtual_tmp_table(THD *thd, List<Column_definition> &field_list)
create_virtual_tmp_table(THD *thd, List<Spvar_definition> &field_list)
{
Virtual_tmp_table *table;
if (!(table= new(thd) Virtual_tmp_table(thd)))

View File

@@ -4207,6 +4207,7 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
/* Prepare temporary LEX. */
thd->lex= lex= &temp_lex;
lex_start(thd);
lex->sql_command= old_lex->sql_command;
/* Disable constant subquery evaluation as we won't be locking tables. */
lex->context_analysis_only= CONTEXT_ANALYSIS_ONLY_VIEW;
@@ -4259,26 +4260,8 @@ fill_schema_table_by_open(THD *thd, bool is_show_fields_or_keys,
table_list->i_s_requested_object= schema_table->i_s_requested_object;
}
/*
Let us set fake sql_command so views won't try to merge
themselves into main statement. If we don't do this,
SELECT * from information_schema.xxxx will cause problems.
SQLCOM_SHOW_FIELDS is used because it satisfies
'only_view_structure()'.
*/
lex->sql_command= SQLCOM_SHOW_FIELDS;
result= (thd->open_temporary_tables(table_list) ||
open_normal_and_derived_tables(thd, table_list,
(MYSQL_OPEN_IGNORE_FLUSH |
MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL |
(can_deadlock ?
MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)),
DT_PREPARE | DT_CREATE));
/*
Restore old value of sql_command back as it is being looked at in
process_table() function.
*/
lex->sql_command= old_lex->sql_command;
DBUG_ASSERT(thd->lex == lex);
result= open_tables_only_view_structure(thd, table_list, can_deadlock);
DEBUG_SYNC(thd, "after_open_table_ignore_flush");

View File

@@ -2940,7 +2940,7 @@ sp_decl_body:
sp_opt_default
{
if (Lex->sp_variable_declarations_finalize(thd, $1,
Lex->last_field[0], $4))
&Lex->last_field[0], $4))
MYSQL_YYABORT;
$$.vars= $1;
$$.conds= $$.hndlrs= $$.curs= 0;

View File

@@ -209,6 +209,7 @@ void ORAerror(THD *thd, const char *s)
String *string;
TABLE_LIST *table_list;
Table_ident *table;
Qualified_column_ident *qualified_column_ident;
char *simple_string;
const char *const_simple_string;
chooser_compare_func_creator boolfunc2creator;
@@ -1002,6 +1003,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
sp_opt_label BIN_NUM label_ident TEXT_STRING_filesystem ident_or_empty
opt_constraint constraint opt_ident
label_declaration_oracle ident_directly_assignable
sp_decl_ident
sp_block_label
%type <lex_string_with_metadata>
@@ -1015,6 +1017,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
table_ident table_ident_nodb references xid
table_ident_opt_wild create_like
%type <qualified_column_ident>
qualified_column_ident
%type <simple_string>
remember_name remember_end opt_db remember_tok_start
wild_and_where
@@ -1184,6 +1189,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize);
%type <symbol> keyword keyword_sp
keyword_directly_assignable
keyword_directly_not_assignable
sp_decl_ident_keyword
keyword_sp_data_type
keyword_sp_not_data_type
%type <lex_user> user grant_user grant_role user_or_role current_role
admin_option_for_role user_maybe_role
@@ -2307,6 +2315,10 @@ sp_param_name_and_type:
if (Lex->sp_param_fill_definition($$= $1))
MYSQL_YYABORT;
}
| sp_param_name qualified_column_ident '%' TYPE_SYM
{
Lex->sphead->fill_spvar_using_type_reference($$= $1, $2);
}
;
/* Stored PROCEDURE parameter declaration list */
@@ -2327,6 +2339,10 @@ sp_pdparam:
if (Lex->sp_param_fill_definition($1))
MYSQL_YYABORT;
}
| sp_param_name sp_opt_inout qualified_column_ident '%' TYPE_SYM
{
Lex->sphead->fill_spvar_using_type_reference($1, $3);
}
;
sp_opt_inout:
@@ -2406,6 +2422,20 @@ sp_decl_body_list:
}
;
qualified_column_ident:
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;
}
;
sp_decl_body:
sp_decl_idents
{
@@ -2415,10 +2445,20 @@ sp_decl_body:
sp_opt_default
{
if (Lex->sp_variable_declarations_finalize(thd, $1,
Lex->last_field[0], $4))
&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);
}
qualified_column_ident '%' TYPE_SYM
sp_opt_default
{
if (Lex->sp_variable_declarations_with_ref_finalize(thd, $1, $3, $6))
MYSQL_YYABORT;
$$.init_using_vars($1);
}
| ident_directly_assignable CONDITION_SYM FOR_SYM sp_cond
{
@@ -2875,8 +2915,25 @@ condition_information_item_name:
{ $$= Condition_information_item::RETURNED_SQLSTATE; }
;
sp_decl_ident:
IDENT_sys
| sp_decl_ident_keyword
{
$$.str= thd->strmake($1.str, $1.length);
if ($$.str == NULL)
MYSQL_YYABORT;
$$.length= $1.length;
}
;
sp_decl_ident_keyword:
keyword_directly_assignable
| keyword_sp_not_data_type
;
sp_decl_idents:
ident_directly_assignable
sp_decl_ident
{
/* NOTE: field definition is filled in sp_decl section. */
@@ -14276,6 +14333,48 @@ keyword_directly_not_assignable:
* conflicts.
*/
keyword_sp:
keyword_sp_data_type
| keyword_sp_not_data_type
;
/*
These keywords are generally allowed as identifiers,
but not allowed as non-delimited SP variable names.
*/
keyword_sp_data_type:
BIT_SYM {}
| BOOLEAN_SYM {} /* PLSQL-R */
| BOOL_SYM {}
| CLOB {}
| DATE_SYM {} /* Oracle-R, PLSQL-R */
| DATETIME {}
| ENUM {}
| FIXED_SYM {}
| GEOMETRYCOLLECTION {}
| GEOMETRY_SYM {}
| LINESTRING {}
| MEDIUM_SYM {}
| MULTILINESTRING {}
| MULTIPOINT {}
| MULTIPOLYGON {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
| NUMBER_SYM {} /* Oracle-R, PLSQL-R */
| NVARCHAR_SYM {}
| POINT_SYM {}
| POLYGON {}
| RAW {} /* Oracle-R */
| SERIAL_SYM {}
| TEXT_SYM {}
| TIMESTAMP {}
| TIME_SYM {} /* Oracle-R */
| VARCHAR2 {} /* Oracle-R, PLSQL-R */
| YEAR_SYM {}
;
keyword_sp_not_data_type:
ACTION {}
| ADDDATE_SYM {}
| ADMIN_SYM {}
@@ -14293,10 +14392,7 @@ keyword_sp:
| AUTO_SYM {}
| AVG_ROW_LENGTH {}
| AVG_SYM {}
| BIT_SYM {}
| BLOCK_SYM {}
| BOOL_SYM {}
| BOOLEAN_SYM {}
| BTREE_SYM {}
| CASCADED {}
| CATALOG_NAME_SYM {}
@@ -14305,7 +14401,6 @@ keyword_sp:
| CIPHER_SYM {}
| CLIENT_SYM {}
| CLASS_ORIGIN_SYM {}
| CLOB {}
| COALESCE {}
| CODE_SYM {}
| COLLATION_SYM {}
@@ -14334,8 +14429,6 @@ keyword_sp:
| CURSOR_NAME_SYM {}
| DATA_SYM {}
| DATAFILE_SYM {}
| DATETIME {}
| DATE_SYM {}
| DAY_SYM {}
| DECODE_SYM {}
| DEFINER_SYM {}
@@ -14350,7 +14443,6 @@ keyword_sp:
| DUPLICATE_SYM {}
| DYNAMIC_SYM {}
| ENDS_SYM {}
| ENUM {}
| ENGINE_SYM {}
| ENGINES_SYM {}
| ERROR_SYM {}
@@ -14371,11 +14463,8 @@ keyword_sp:
| FULL {}
| FILE_SYM {}
| FIRST_SYM {}
| FIXED_SYM {}
| GENERAL {}
| GENERATED_SYM {}
| GEOMETRY_SYM {}
| GEOMETRYCOLLECTION {}
| GET_FORMAT {}
| GRANTS {}
| GLOBAL_SYM {}
@@ -14404,7 +14493,6 @@ keyword_sp:
| LEAVES {}
| LESS_SYM {}
| LEVEL_SYM {}
| LINESTRING {}
| LIST_SYM {}
| LOCAL_SYM {}
| LOCKS_SYM {}
@@ -14438,7 +14526,6 @@ keyword_sp:
| MAX_STATEMENT_TIME_SYM {}
| MAX_UPDATES_PER_HOUR {}
| MAX_USER_CONNECTIONS_SYM {}
| MEDIUM_SYM {}
| MEMORY_SYM {}
| MERGE_SYM {}
| MESSAGE_TEXT_SYM {}
@@ -14449,24 +14536,17 @@ keyword_sp:
| MODIFY_SYM {}
| MODE_SYM {}
| MONTH_SYM {}
| MULTILINESTRING {}
| MULTIPOINT {}
| MULTIPOLYGON {}
| MUTEX_SYM {}
| MYSQL_SYM {}
| MYSQL_ERRNO_SYM {}
| NAME_SYM {}
| NAMES_SYM {}
| NATIONAL_SYM {}
| NCHAR_SYM {}
| NEXT_SYM {}
| NEW_SYM {}
| NO_WAIT_SYM {}
| NODEGROUP_SYM {}
| NONE_SYM {}
| NOTFOUND_SYM {}
| NUMBER_SYM {}
| NVARCHAR_SYM {}
| OFFSET_SYM {}
| OLD_PASSWORD_SYM {}
| ONE_SYM {}
@@ -14482,8 +14562,6 @@ keyword_sp:
| PHASE_SYM {}
| PLUGIN_SYM {}
| PLUGINS_SYM {}
| POINT_SYM {}
| POLYGON {}
| PRESERVE_SYM {}
| PREV_SYM {}
| PRIVILEGES {}
@@ -14495,7 +14573,6 @@ keyword_sp:
| QUARTER_SYM {}
| QUERY_SYM {}
| QUICK {}
| RAW {}
| READ_ONLY_SYM {}
| REBUILD_SYM {}
| RECOVER_SYM {}
@@ -14528,7 +14605,6 @@ keyword_sp:
| SCHEDULE_SYM {}
| SCHEMA_NAME_SYM {}
| SECOND_SYM {}
| SERIAL_SYM {}
| SERIALIZABLE_SYM {}
| SESSION_SYM {}
| SIMPLE_SYM {}
@@ -14563,15 +14639,12 @@ keyword_sp:
| TABLESPACE {}
| TEMPORARY {}
| TEMPTABLE_SYM {}
| TEXT_SYM {}
| THAN_SYM {}
| TRANSACTION_SYM {}
| TRANSACTIONAL_SYM {}
| TRIGGERS_SYM {}
| TIMESTAMP {}
| TIMESTAMP_ADD {}
| TIMESTAMP_DIFF {}
| TIME_SYM {}
| TYPES_SYM {}
| TYPE_SYM {}
| UDF_RETURNS_SYM {}
@@ -14584,7 +14657,6 @@ keyword_sp:
| UNTIL_SYM {}
| USER_SYM {}
| USE_FRM {}
| VARCHAR2 {}
| VARIABLES {}
| VIEW_SYM {}
| VIRTUAL_SYM {}
@@ -14596,7 +14668,6 @@ keyword_sp:
| WORK_SYM {}
| X509_SYM {}
| XML_SYM {}
| YEAR_SYM {}
| VIA_SYM {}
;

View File

@@ -647,6 +647,11 @@ public:
{
vars= conds= hndlrs= curs= 0;
}
void init_using_vars(uint nvars)
{
vars= nvars;
conds= hndlrs= curs= 0;
}
void join(const Lex_spblock_st &b1, const Lex_spblock_st &b2)
{
vars= b1.vars + b2.vars;

View File

@@ -8202,3 +8202,14 @@ LEX_CSTRING *fk_option_name(enum_fk_option opt)
};
return names + opt;
}
Field *TABLE::find_field_by_name(const char *str) const
{
for (Field **tmp= field; *tmp; tmp++)
{
if (!my_strcasecmp(system_charset_info, (*tmp)->field_name, str))
return *tmp;
}
return NULL;
}

View File

@@ -1440,6 +1440,7 @@ public:
TABLE *tmp_table,
TMP_TABLE_PARAM *tmp_table_param,
bool with_cleanup);
Field *find_field_by_name(const char *str) const;
};