MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE

The crash happened with an indexed virtual column whose
value is evaluated using a function that has a different meaning
in sql_mode='' vs sql_mode=ORACLE:

- DECODE()
- LTRIM()
- RTRIM()
- LPAD()
- RPAD()
- REPLACE()
- SUBSTR()

For example:

CREATE TABLE t1 (
  b VARCHAR(1),
  g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,
  KEY g(g)
);

So far we had replacement XXX_ORACLE() functions for all mentioned function,
e.g. SUBSTR_ORACLE() for SUBSTR(). So it was possible to correctly re-parse
SUBSTR_ORACLE() even in sql_mode=''.

But it was not possible to re-parse the MariaDB version of SUBSTR()
after switching to sql_mode=ORACLE. It was erroneously mis-interpreted
as SUBSTR_ORACLE().

As a result, this combination worked fine:

SET sql_mode=ORACLE;
CREATE TABLE t1 ... g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL, ...;
INSERT ...
FLUSH TABLES;
SET sql_mode='';
INSERT ...

But the other way around it crashed:

SET sql_mode='';
CREATE TABLE t1 ... g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL, ...;
INSERT ...
FLUSH TABLES;
SET sql_mode=ORACLE;
INSERT ...

At CREATE time, SUBSTR was instantiated as Item_func_substr and printed
in the FRM file as substr(). At re-open time with sql_mode=ORACLE, "substr()"
was erroneously instantiated as Item_func_substr_oracle.

Fix:

The fix proposes a symmetric solution. It provides a way to re-parse reliably
all sql_mode dependent functions to their original CREATE TABLE time meaning,
no matter what the open-time sql_mode is.

We take advantage of the same idea we previously used to resolve sql_mode
dependent data types.

Now all sql_mode dependent functions are printed by SHOW using a schema
qualifier when the current sql_mode differs from the function sql_mode:

SET sql_mode='';
CREATE TABLE t1 ... SUBSTR(a,b,c) ..;
SET sql_mode=ORACLE;
SHOW CREATE TABLE t1;   ->   mariadb_schema.substr(a,b,c)

SET sql_mode=ORACLE;
CREATE TABLE t2 ... SUBSTR(a,b,c) ..;
SET sql_mode='';
SHOW CREATE TABLE t1;   ->   oracle_schema.substr(a,b,c)

Old replacement names like substr_oracle() are still understood for
backward compatibility and used in FRM files (for downgrade compatibility),
but they are not printed by SHOW any more.
This commit is contained in:
Alexander Barkov 2022-04-04 14:50:21 +04:00
parent 228b7e4db5
commit 2b6d241ee4
34 changed files with 3754 additions and 126 deletions

View file

@ -324,7 +324,8 @@ enum my_lex_states
MY_LEX_IDENT_OR_KEYWORD,
MY_LEX_IDENT_OR_HEX, MY_LEX_IDENT_OR_BIN, MY_LEX_IDENT_OR_NCHAR,
MY_LEX_STRING_OR_DELIMITER, MY_LEX_MINUS_OR_COMMENT, MY_LEX_PLACEHOLDER,
MY_LEX_COMMA
MY_LEX_COMMA,
MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC
};
struct charset_info_st;

View file

@ -500,21 +500,21 @@ SELECT @@global.rpad(); -- Unknown system variable 'rpad'
--------
SELECT @@global.adddate(); -- Unknown system variable 'adddate'
--------
SELECT @@global.substr(); -- Unknown system variable 'substr'
SELECT @@global.substr(); -- ..syntax.. near 'substr()' at line 1
--------
SELECT @@global.substring(); -- Unknown system variable 'substring'
SELECT @@global.substring(); -- ..syntax.. near 'substring()' at line 1
--------
SELECT @@global.trim_oracle(); -- Unknown system variable 'trim_oracle'
--------
SELECT @@global.ascii(); -- Unknown system variable 'ascii'
--------
SELECT @@global.replace(); -- Unknown system variable 'replace'
SELECT @@global.replace(); -- ..syntax.. near 'replace()' at line 1
--------
SELECT @@global.weight_string(); -- Unknown system variable 'weight_string'
--------
SELECT @@global.char(); -- Unknown system variable 'char'
--------
SELECT @@global.trim(); -- Unknown system variable 'trim'
SELECT @@global.trim(); -- ..syntax.. near 'trim()' at line 1
--------
SELECT @@global.year(); -- Unknown system variable 'year'
--------
@ -732,21 +732,21 @@ CREATE FUNCTION test.rpad() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.adddate() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.substr() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
CREATE FUNCTION test.substr() RETURNS OOPS; -- ..syntax.. near 'substr() RETURNS OOPS'
--------
CREATE FUNCTION test.substring() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
CREATE FUNCTION test.substring() RETURNS OOPS; -- ..syntax.. near 'substring() RETURNS OOP
--------
CREATE FUNCTION test.trim_oracle() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.ascii() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.replace() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
CREATE FUNCTION test.replace() RETURNS OOPS; -- ..syntax.. near 'replace() RETURNS OOPS'
--------
CREATE FUNCTION test.weight_string() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.char() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------
CREATE FUNCTION test.trim() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
CREATE FUNCTION test.trim() RETURNS OOPS; -- ..syntax.. near 'trim() RETURNS OOPS' at
--------
CREATE FUNCTION test.year() RETURNS OOPS; -- ..syntax.. near 'OOPS' at line 1
--------

View file

@ -3,12 +3,12 @@ EXPLAIN EXTENDED SELECT 'a'||'b'||'c';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(concat_operator_oracle('a','b'),'c') AS "'a'||'b'||'c'"
Note 1003 select concat(concat('a','b'),'c') AS "'a'||'b'||'c'"
EXPLAIN EXTENDED SELECT CONCAT('a'||'b'||'c');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(concat_operator_oracle(concat_operator_oracle('a','b'),'c')) AS "CONCAT('a'||'b'||'c')"
Note 1003 select concat(concat(concat('a','b'),'c')) AS "CONCAT('a'||'b'||'c')"
SELECT '' || '';
'' || ''
@ -211,14 +211,14 @@ SET sql_mode=ORACLE;
CREATE VIEW v1 AS SELECT 'foo'||NULL||'bar' AS test;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select concat_operator_oracle(concat_operator_oracle('foo',NULL),'bar') AS "test" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select concat(concat('foo',NULL),'bar') AS "test" latin1 latin1_swedish_ci
SELECT * FROM v1;
test
foobar
SET sql_mode=DEFAULT;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select concat_operator_oracle(concat_operator_oracle('foo',NULL),'bar') AS `test` latin1 latin1_swedish_ci
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select oracle_schema.concat(oracle_schema.concat('foo',NULL),'bar') AS `test` latin1 latin1_swedish_ci
SELECT * FROM v1;
test
foobar
@ -234,7 +234,7 @@ NULL
SET sql_mode=ORACLE;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select concat('foo',NULL,'bar') AS "test" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select mariadb_schema.concat('foo',NULL,'bar') AS "test" latin1 latin1_swedish_ci
SELECT * FROM v1;
test
NULL
@ -268,12 +268,12 @@ EXPLAIN EXTENDED SELECT -1<<1||1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select -1 << concat_operator_oracle(1,1) AS "a"
Note 1003 select -1 << concat(1,1) AS "a"
EXPLAIN EXTENDED SELECT -1||0<<1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(-1,0) << 1 AS "a"
Note 1003 select concat(-1,0) << 1 AS "a"
SELECT -1+1||1 AS a FROM DUAL;
a
01
@ -284,12 +284,12 @@ EXPLAIN EXTENDED SELECT -1+1||1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(-1 + 1,1) AS "a"
Note 1003 select concat(-1 + 1,1) AS "a"
EXPLAIN EXTENDED SELECT -1||0+1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(-1,0) + 1 AS "a"
Note 1003 select concat(-1,0) + 1 AS "a"
SELECT 1*1||-1 AS a FROM DUAL;
a
1-1
@ -300,12 +300,12 @@ EXPLAIN EXTENDED SELECT 1*1||-1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(1 * 1,-1) AS "a"
Note 1003 select concat(1 * 1,-1) AS "a"
EXPLAIN EXTENDED SELECT 1||1*-1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(1,1 * -1) AS "a"
Note 1003 select concat(1,1 * -1) AS "a"
SELECT -1^1||1 AS a FROM DUAL;
a
184467440737095516141
@ -316,12 +316,12 @@ EXPLAIN EXTENDED SELECT -1^1||1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(-1 ^ 1,1) AS "a"
Note 1003 select concat(-1 ^ 1,1) AS "a"
EXPLAIN EXTENDED SELECT -1||0^1 AS a FROM DUAL;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select concat_operator_oracle(-1,0 ^ 1) AS "a"
Note 1003 select concat(-1,0 ^ 1) AS "a"
#
# MDEV-17359 Concatenation operator || in like expression failed in sql_mode=ORACLE
#
@ -332,7 +332,7 @@ EXPLAIN EXTENDED SELECT 'abc' LIKE 'a'||'%';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select 'abc' like concat_operator_oracle('a','%') AS "'abc' LIKE 'a'||'%'"
Note 1003 select 'abc' like concat('a','%') AS "'abc' LIKE 'a'||'%'"
SELECT 'x' FROM DUAL WHERE 11 LIKE 1||1;
x
x
@ -353,7 +353,7 @@ EXPLAIN EXTENDED SELECT c1 FROM t1 WHERE c1 LIKE '%'||'b' ORDER BY ord;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort
Warnings:
Note 1003 select "test"."t1"."c1" AS "c1" from "test"."t1" where "test"."t1"."c1" like <cache>(concat_operator_oracle('%','b')) order by "test"."t1"."ord"
Note 1003 select "test"."t1"."c1" AS "c1" from "test"."t1" where "test"."t1"."c1" like <cache>(concat('%','b')) order by "test"."t1"."ord"
SELECT c1 FROM t1 WHERE c1 LIKE c2||'%'||'c' ORDER BY ord;
c1
abc
@ -361,7 +361,7 @@ EXPLAIN EXTENDED SELECT c1 FROM t1 WHERE c1 LIKE c2||'%'||'c' ORDER BY ord;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where; Using filesort
Warnings:
Note 1003 select "test"."t1"."c1" AS "c1" from "test"."t1" where "test"."t1"."c1" like concat_operator_oracle(concat_operator_oracle("test"."t1"."c2",'%'),'c') order by "test"."t1"."ord"
Note 1003 select "test"."t1"."c1" AS "c1" from "test"."t1" where "test"."t1"."c1" like concat(concat("test"."t1"."c2",'%'),'c') order by "test"."t1"."ord"
SELECT 'x' FROM t1 WHERE c1||c2 LIKE 'aa%';
x
x
@ -369,7 +369,7 @@ EXPLAIN EXTENDED SELECT 'x' FROM t1 WHERE c1||c2 LIKE 'aa%';
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1003 select 'x' AS "x" from "test"."t1" where concat_operator_oracle("test"."t1"."c1","test"."t1"."c2") like 'aa%'
Note 1003 select 'x' AS "x" from "test"."t1" where concat("test"."t1"."c1","test"."t1"."c2") like 'aa%'
SELECT 'x' FROM t1 WHERE c1||c2 LIKE c2||c1;
x
x
@ -377,7 +377,7 @@ EXPLAIN EXTENDED SELECT 'x' FROM t1 WHERE c1||c2 LIKE c2||c1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using where
Warnings:
Note 1003 select 'x' AS "x" from "test"."t1" where concat_operator_oracle("test"."t1"."c1","test"."t1"."c2") like concat_operator_oracle("test"."t1"."c2","test"."t1"."c1")
Note 1003 select 'x' AS "x" from "test"."t1" where concat("test"."t1"."c1","test"."t1"."c2") like concat("test"."t1"."c2","test"."t1"."c1")
CREATE VIEW v1 AS SELECT c1, c2, c1 LIKE c2||'_' FROM t1 ORDER BY ord;
SELECT * FROM v1;
c1 c2 c1 LIKE c2||'_'
@ -388,6 +388,6 @@ EXPLAIN EXTENDED SELECT * FROM v1;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 3 100.00 Using filesort
Warnings:
Note 1003 select "test"."t1"."c1" AS "c1","test"."t1"."c2" AS "c2","test"."t1"."c1" like concat_operator_oracle("test"."t1"."c2",'_') AS "c1 LIKE c2||'_'" from "test"."t1" order by "test"."t1"."ord"
Note 1003 select "test"."t1"."c1" AS "c1","test"."t1"."c2" AS "c2","test"."t1"."c1" like concat("test"."t1"."c2",'_') AS "c1 LIKE c2||'_'" from "test"."t1" order by "test"."t1"."ord"
DROP VIEW v1;
DROP TABLE t1;

View file

@ -28,7 +28,7 @@ EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select decode_oracle(12,10,'x10',11,'x11','def') AS "DECODE(12,10,'x10',11,'x11','def')"
Note 1003 select decode(12,10,'x10',11,'x11','def') AS "DECODE(12,10,'x10',11,'x11','def')"
CREATE TABLE decode (decode int);
DROP TABLE decode;
#
@ -46,22 +46,22 @@ EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select decode_oracle(12,10,'x10',11,'x11') AS "DECODE(12,10,'x10',11,'x11')"
Note 1003 select decode(12,10,'x10',11,'x11') AS "DECODE(12,10,'x10',11,'x11')"
EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select decode_oracle(12,10,'x10',11,'x11','def') AS "DECODE(12,10,'x10',11,'x11','def')"
Note 1003 select decode(12,10,'x10',11,'x11','def') AS "DECODE(12,10,'x10',11,'x11','def')"
EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select decode_oracle(12,10,'x10',11,'x11') AS "DECODE_ORACLE(12,10,'x10',11,'x11')"
Note 1003 select decode(12,10,'x10',11,'x11') AS "DECODE_ORACLE(12,10,'x10',11,'x11')"
EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11','def');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select decode_oracle(12,10,'x10',11,'x11','def') AS "DECODE_ORACLE(12,10,'x10',11,'x11','def')"
Note 1003 select decode(12,10,'x10',11,'x11','def') AS "DECODE_ORACLE(12,10,'x10',11,'x11','def')"
CREATE TABLE t1 (a INT);
CREATE VIEW v1 AS
SELECT
@ -72,7 +72,7 @@ DECODE_ORACLE(a,1,'x1',NULL,'xNULL','xELSE') AS d4
FROM t1;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select decode_oracle("t1"."a",1,'x1',NULL,'xNULL') AS "d1",decode_oracle("t1"."a",1,'x1',NULL,'xNULL','xELSE') AS "d2",decode_oracle("t1"."a",1,'x1',NULL,'xNULL') AS "d3",decode_oracle("t1"."a",1,'x1',NULL,'xNULL','xELSE') AS "d4" from "t1" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select decode("t1"."a",1,'x1',NULL,'xNULL') AS "d1",decode("t1"."a",1,'x1',NULL,'xNULL','xELSE') AS "d2",decode("t1"."a",1,'x1',NULL,'xNULL') AS "d3",decode("t1"."a",1,'x1',NULL,'xNULL','xELSE') AS "d4" from "t1" latin1 latin1_swedish_ci
DROP VIEW v1;
DROP TABLE t1;
SELECT DECODE(TIME'10:20:31','10:20:31','then1','10:20:32','then2','def');

View file

@ -44,11 +44,11 @@ EXPLAIN EXTENDED SELECT RPAD('a',0,'.'), LPAD('a',0,'.'), LPAD(c1,c2,c3), LPAD(c
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE t1 ALL NULL NULL NULL NULL 7 100.00 Using filesort
Warnings:
Note 1003 select rpad_oracle('a',0,'.') AS "RPAD('a',0,'.')",lpad_oracle('a',0,'.') AS "LPAD('a',0,'.')",lpad_oracle("test"."t1"."c1","test"."t1"."c2","test"."t1"."c3") AS "LPAD(c1,c2,c3)",lpad_oracle("test"."t1"."c1","test"."t1"."c2") AS "LPAD(c1,c2)",rpad_oracle("test"."t1"."c1","test"."t1"."c2","test"."t1"."c3") AS "RPAD(c1,c2,c3)",rpad_oracle("test"."t1"."c1","test"."t1"."c2") AS "RPAD(c1,c2)" from "test"."t1" order by "test"."t1"."ord"
Note 1003 select rpad('a',0,'.') AS "RPAD('a',0,'.')",lpad('a',0,'.') AS "LPAD('a',0,'.')",lpad("test"."t1"."c1","test"."t1"."c2","test"."t1"."c3") AS "LPAD(c1,c2,c3)",lpad("test"."t1"."c1","test"."t1"."c2") AS "LPAD(c1,c2)",rpad("test"."t1"."c1","test"."t1"."c2","test"."t1"."c3") AS "RPAD(c1,c2,c3)",rpad("test"."t1"."c1","test"."t1"."c2") AS "RPAD(c1,c2)" from "test"."t1" order by "test"."t1"."ord"
CREATE VIEW v1 AS SELECT RPAD('a',0,'.') AS "C1", LPAD('a',0,'.') AS "C2", LPAD(c1,c2,c3) AS "C3", LPAD(c1,c2) AS "C4", RPAD(c1,c2,c3) AS "C5", RPAD(c1,c2) AS "C6" FROM t1 ORDER BY ord;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select rpad_oracle('a',0,'.') AS "C1",lpad_oracle('a',0,'.') AS "C2",lpad_oracle("t1"."c1","t1"."c2","t1"."c3") AS "C3",lpad_oracle("t1"."c1","t1"."c2") AS "C4",rpad_oracle("t1"."c1","t1"."c2","t1"."c3") AS "C5",rpad_oracle("t1"."c1","t1"."c2") AS "C6" from "t1" order by "t1"."ord" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select rpad('a',0,'.') AS "C1",lpad('a',0,'.') AS "C2",lpad("t1"."c1","t1"."c2","t1"."c3") AS "C3",lpad("t1"."c1","t1"."c2") AS "C4",rpad("t1"."c1","t1"."c2","t1"."c3") AS "C5",rpad("t1"."c1","t1"."c2") AS "C6" from "t1" order by "t1"."ord" latin1 latin1_swedish_ci
SELECT * FROM v1;
C1 C2 C3 C4 C5 C6
NULL NULL NULL a NULL a

File diff suppressed because it is too large Load diff

View file

@ -21,11 +21,11 @@ EXPLAIN EXTENDED SELECT REPLACE('ab','a',null) ;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select replace_oracle('ab','a',NULL) AS "REPLACE('ab','a',null)"
Note 1003 select replace('ab','a',NULL) AS "REPLACE('ab','a',null)"
CREATE VIEW v1 AS SELECT REPLACE('ab','a',null) ;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select replace_oracle('ab','a',NULL) AS "REPLACE('ab','a',null)" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select replace('ab','a',NULL) AS "REPLACE('ab','a',null)" latin1 latin1_swedish_ci
SELECT * FROM v1;
REPLACE('ab','a',null)
b

View file

@ -76,11 +76,11 @@ EXPLAIN EXTENDED SELECT SUBSTR('abc',2,1) ;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select substr_oracle('abc',2,1) AS "SUBSTR('abc',2,1)"
Note 1003 select substr('abc',2,1) AS "SUBSTR('abc',2,1)"
CREATE VIEW v1 AS SELECT SUBSTR('abc',2,1) ;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select substr_oracle('abc',2,1) AS "SUBSTR('abc',2,1)" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select substr('abc',2,1) AS "SUBSTR('abc',2,1)" latin1 latin1_swedish_ci
SELECT * FROM v1;
SUBSTR('abc',2,1)
b

View file

@ -116,13 +116,13 @@ TRIM(TRAILING 'a' FROM 'abc') ;
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select trim_oracle('abc') AS "TRIM('abc')",trim_oracle(both 'a' from 'abc') AS "TRIM(BOTH 'a' FROM 'abc')",trim_oracle(leading 'a' from 'abc') AS "TRIM(LEADING 'a' FROM 'abc')",trim_oracle(trailing 'a' from 'abc') AS "TRIM(TRAILING 'a' FROM 'abc')"
Note 1003 select trim('abc') AS "TRIM('abc')",trim(both 'a' from 'abc') AS "TRIM(BOTH 'a' FROM 'abc')",trim(leading 'a' from 'abc') AS "TRIM(LEADING 'a' FROM 'abc')",trim(trailing 'a' from 'abc') AS "TRIM(TRAILING 'a' FROM 'abc')"
EXPLAIN EXTENDED SELECT RTRIM('abc'),
LTRIM('abc');
id select_type table type possible_keys key key_len ref rows filtered Extra
1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL No tables used
Warnings:
Note 1003 select rtrim_oracle('abc') AS "RTRIM('abc')",ltrim_oracle('abc') AS "LTRIM('abc')"
Note 1003 select rtrim('abc') AS "RTRIM('abc')",ltrim('abc') AS "LTRIM('abc')"
CREATE VIEW v1 AS SELECT ord,TRIM('abc'),RTRIM('abc'),LTRIM('abc'),
'['||c1||']',
TRIM(LEADING 'a' FROM c1),
@ -133,7 +133,7 @@ RTRIM(c1)
FROM t1 ORDER BY ord ;
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE VIEW "v1" AS select "t1"."ord" AS "ord",trim_oracle('abc') AS "TRIM('abc')",rtrim_oracle('abc') AS "RTRIM('abc')",ltrim_oracle('abc') AS "LTRIM('abc')",concat_operator_oracle(concat_operator_oracle('[',"t1"."c1"),']') AS "'['||c1||']'",trim_oracle(leading 'a' from "t1"."c1") AS "TRIM(LEADING 'a' FROM c1)",trim_oracle(trailing 'a' from "t1"."c1") AS "TRIM(TRAILING 'a' FROM c1)",trim_oracle(both 'a' from "t1"."c1") AS "TRIM(BOTH 'a' FROM c1)",ltrim_oracle("t1"."c1") AS "LTRIM(c1)",rtrim_oracle("t1"."c1") AS "RTRIM(c1)" from "t1" order by "t1"."ord" latin1 latin1_swedish_ci
v1 CREATE VIEW "v1" AS select "t1"."ord" AS "ord",trim('abc') AS "TRIM('abc')",rtrim('abc') AS "RTRIM('abc')",ltrim('abc') AS "LTRIM('abc')",concat(concat('[',"t1"."c1"),']') AS "'['||c1||']'",trim(leading 'a' from "t1"."c1") AS "TRIM(LEADING 'a' FROM c1)",trim(trailing 'a' from "t1"."c1") AS "TRIM(TRAILING 'a' FROM c1)",trim(both 'a' from "t1"."c1") AS "TRIM(BOTH 'a' FROM c1)",ltrim("t1"."c1") AS "LTRIM(c1)",rtrim("t1"."c1") AS "RTRIM(c1)" from "t1" order by "t1"."ord" latin1 latin1_swedish_ci
SELECT * FROM v1;
ord TRIM('abc') RTRIM('abc') LTRIM('abc') '['||c1||']' TRIM(LEADING 'a' FROM c1) TRIM(TRAILING 'a' FROM c1) TRIM(BOTH 'a' FROM c1) LTRIM(c1) RTRIM(c1)
1 abc abc abc [abc] bc abc bc abc abc

View file

@ -0,0 +1,112 @@
#
# Start of 10.4 tests
#
#
# MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE
#
SET sql_mode=DEFAULT;
CREATE TABLE t1 (
a0 VARCHAR(64) NOT NULL DEFAULT LTRIM(now()),
a1 VARCHAR(64) AS (LTRIM(a0)) PERSISTENT,
b0 VARCHAR(64) NOT NULL DEFAULT LPAD(now(),10),
b1 VARCHAR(64) AS (LPAD(b0,10)) PERSISTENT
);
CREATE VIEW v1 AS SELECT
LTRIM(now()) AS a0,
LPAD(now(),10) AS b0;
SET sql_mode=ORACLE;
CREATE TABLE t2 (
a0 VARCHAR(64) NOT NULL DEFAULT LTRIM(now()),
a1 VARCHAR(64) AS (LTRIM(a0)) PERSISTENT,
b0 VARCHAR(64) NOT NULL DEFAULT LPAD(now(),10),
b1 VARCHAR(64) AS (LPAD(b0,10)) PERSISTENT
);
CREATE VIEW v2 AS SELECT
LTRIM(now()) AS a0,
LPAD(now(),10) AS b0;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t1` (
`a0` varchar(64) NOT NULL DEFAULT ltrim(current_timestamp()),
`a1` varchar(64) GENERATED ALWAYS AS (ltrim(`a0`)) STORED,
`b0` varchar(64) NOT NULL DEFAULT lpad(current_timestamp(),10),
`b1` varchar(64) GENERATED ALWAYS AS (lpad(`b0`,10)) STORED
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `t2` (
`a0` varchar(64) NOT NULL DEFAULT ltrim_oracle(current_timestamp()),
`a1` varchar(64) GENERATED ALWAYS AS (ltrim_oracle(`a0`)) STORED,
`b0` varchar(64) NOT NULL DEFAULT lpad_oracle(current_timestamp(),10),
`b1` varchar(64) GENERATED ALWAYS AS (lpad_oracle(`b0`,10)) STORED
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci;
/*!40101 SET character_set_client = @saved_cs_client */;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
/*!50001 CREATE VIEW `v1` AS SELECT
1 AS `a0`,
1 AS `b0` */;
SET character_set_client = @saved_cs_client;
SET @saved_cs_client = @@character_set_client;
SET character_set_client = utf8;
/*!50001 CREATE VIEW `v2` AS SELECT
1 AS `a0`,
1 AS `b0` */;
SET character_set_client = @saved_cs_client;
/*!50001 DROP VIEW IF EXISTS `v1`*/;
/*!50001 SET @saved_cs_client = @@character_set_client */;
/*!50001 SET @saved_cs_results = @@character_set_results */;
/*!50001 SET @saved_col_connection = @@collation_connection */;
/*!50001 SET character_set_client = latin1 */;
/*!50001 SET character_set_results = latin1 */;
/*!50001 SET collation_connection = latin1_swedish_ci */;
/*!50001 CREATE ALGORITHM=UNDEFINED */
/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */
/*!50001 VIEW `v1` AS select ltrim(current_timestamp()) AS `a0`,lpad(current_timestamp(),10) AS `b0` */;
/*!50001 SET character_set_client = @saved_cs_client */;
/*!50001 SET character_set_results = @saved_cs_results */;
/*!50001 SET collation_connection = @saved_col_connection */;
/*!50001 DROP VIEW IF EXISTS `v2`*/;
/*!50001 SET @saved_cs_client = @@character_set_client */;
/*!50001 SET @saved_cs_results = @@character_set_results */;
/*!50001 SET @saved_col_connection = @@collation_connection */;
/*!50001 SET character_set_client = latin1 */;
/*!50001 SET character_set_results = latin1 */;
/*!50001 SET collation_connection = latin1_swedish_ci */;
/*!50001 CREATE ALGORITHM=UNDEFINED */
/*!50013 DEFINER=`root`@`localhost` SQL SECURITY DEFINER */
/*!50001 VIEW `v2` AS select oracle_schema.ltrim(current_timestamp()) AS `a0`,oracle_schema.lpad(current_timestamp(),10) AS `b0` */;
/*!50001 SET character_set_client = @saved_cs_client */;
/*!50001 SET character_set_results = @saved_cs_results */;
/*!50001 SET collation_connection = @saved_col_connection */;
DROP TABLE t1,t2;
DROP VIEW v1,v2;
SET sql_mode=DEFAULT;
SHOW CREATE TABLE t1;
Table Create Table
t1 CREATE TABLE `t1` (
`a0` varchar(64) NOT NULL DEFAULT ltrim(current_timestamp()),
`a1` varchar(64) GENERATED ALWAYS AS (ltrim(`a0`)) STORED,
`b0` varchar(64) NOT NULL DEFAULT lpad(current_timestamp(),10),
`b1` varchar(64) GENERATED ALWAYS AS (lpad(`b0`,10)) STORED
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SHOW CREATE TABLE t2;
Table Create Table
t2 CREATE TABLE `t2` (
`a0` varchar(64) NOT NULL DEFAULT ltrim_oracle(current_timestamp()),
`a1` varchar(64) GENERATED ALWAYS AS (ltrim_oracle(`a0`)) STORED,
`b0` varchar(64) NOT NULL DEFAULT lpad_oracle(current_timestamp(),10),
`b1` varchar(64) GENERATED ALWAYS AS (lpad_oracle(`b0`,10)) STORED
) ENGINE=MyISAM DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci
SHOW CREATE VIEW v1;
View Create View character_set_client collation_connection
v1 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v1` AS select ltrim(current_timestamp()) AS `a0`,lpad(current_timestamp(),10) AS `b0` latin1 latin1_swedish_ci
SHOW CREATE VIEW v2;
View Create View character_set_client collation_connection
v2 CREATE ALGORITHM=UNDEFINED DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW `v2` AS select oracle_schema.ltrim(current_timestamp()) AS `a0`,oracle_schema.lpad(current_timestamp(),10) AS `b0` latin1 latin1_swedish_ci
DROP TABLE t1,t2;
DROP VIEW v1, v2;
#
# End of 10.4 tests
#

View file

@ -178,9 +178,9 @@ EXECUTE IMMEDIATE 'SELECT :1 FROM DUAL' USING 10;
# Testing erroneous and diallowed prepare source
#
EXECUTE IMMEDIATE _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat_operator_oracle'
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat'
PREPARE stmt FROM _latin1'SELECT 1 AS c FROM ' || _latin2 'DUAL';
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat_operator_oracle'
ERROR HY000: Illegal mix of collations (latin1_swedish_ci,COERCIBLE) and (latin2_general_ci,COERCIBLE) for operation 'concat'
EXECUTE IMMEDIATE (SELECT 'SELECT 1');
ERROR 42000: EXECUTE IMMEDIATE does not support subqueries or stored functions
PREPARE stmt FROM (SELECT 'SELECT 1');

View file

@ -758,7 +758,7 @@ END;
END;
$$
CALL p1();
ERROR HY000: Illegal mix of collations (latin1_bin,EXPLICIT) and (latin1_swedish_ci,EXPLICIT) for operation 'concat_operator_oracle'
ERROR HY000: Illegal mix of collations (latin1_bin,EXPLICIT) and (latin1_swedish_ci,EXPLICIT) for operation 'concat'
DROP PROCEDURE p1;
#
# Non-existing field

View file

@ -0,0 +1,54 @@
#
# MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE
#
FLUSH TABLES;
SET sql_mode='';
CREATE TABLE t (d INT,b VARCHAR(1),c CHAR(1),g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b),KEY g(g)) ENGINE=InnoDB;
INSERT INTO t VALUES (0);
ERROR 21S01: Column count doesn't match value count at row 1
SET sql_mode='ORACLE';
INSERT INTO t SET c=REPEAT (1,0);
Warnings:
Warning 1364 Field 'b' doesn't have a default value
ALTER TABLE t CHANGE COLUMN a b INT;
ERROR 42S22: Unknown column 'a' in 't'
DELETE FROM t;
SET sql_mode='';
FLUSH TABLES;
INSERT INTO t SET c='0';
Warnings:
Warning 1364 Field 'b' doesn't have a default value
DROP TABLE t;
FLUSH TABLES;
SET sql_mode='';
CREATE TABLE t (a INT(1),d INT(1),b VARCHAR(1),c CHAR(1),vadc INT(1) GENERATED ALWAYS AS ( (a + length (d))) STORED,vbc CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,vbidxc CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b (1),a,d),KEY d (d),KEY a (a),KEY c_renamed (c (1),b (1)),KEY b (b (1),c (1),a),KEY vbidxc (vbidxc),KEY a_2 (a,vbidxc),KEY vbidxc_2 (vbidxc,d)) DEFAULT CHARSET=latin1 ENGINE=InnoDB;
INSERT INTO t VALUES (0,0,1,0,1,0,1,0,0);
ERROR 21S01: Column count doesn't match value count at row 1
SET SESSION sql_mode='ORACLE';
INSERT INTO t SET c=REPEAT (1,0);
Warnings:
Warning 1364 Field 'a' doesn't have a default value
Warning 1364 Field 'd' doesn't have a default value
Warning 1364 Field 'b' doesn't have a default value
ALTER TABLE t CHANGE COLUMN a b CHAR(1);
ERROR 42S21: Duplicate column name 'b'
DELETE FROM t;
SET SESSION sql_mode=DEFAULT;
DROP TABLE t;
SET sql_mode='';
CREATE TABLE t1 (d INT,b VARCHAR(1),c CHAR(1),g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b),KEY g(g)) ENGINE=InnoDB;
INSERT INTO t1 VALUES (0);
ERROR 21S01: Column count doesn't match value count at row 1
SET sql_mode='ORACLE';
INSERT INTO t1 SET c=REPEAT (1,0);
Warnings:
Warning 1364 Field 'b' doesn't have a default value
ALTER TABLE t1 CHANGE COLUMN a b INT;
ERROR 42S22: Unknown column 'a' in 't1'
DELETE FROM t1;
SET sql_mode='';
FLUSH TABLES;
INSERT INTO t1 SET c='0';
Warnings:
Warning 1364 Field 'b' doesn't have a default value
DROP TABLE t1;

View file

@ -0,0 +1,247 @@
--let $MYSQLD_DATADIR= `select @@datadir`
--echo #
--echo # MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE
--echo #
#
# Testing that the error message for DECODE preserves
# the exact letter case as typed by the user
#
SET sql_mode=DEFAULT;
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT decode_oracle(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT DECODE_ORACLE(1);
SET sql_mode=ORACLE;
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT decode_oracle(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT DECODE_ORACLE(1);
SET sql_mode=DEFAULT;
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT decode(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT DECODE(1);
SET sql_mode=ORACLE;
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT decode(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT DECODE(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT mariadb_schema.decode(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT mariadb_schema.DECODE(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT mariadb_schema.decode_oracle(1);
--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT
SELECT mariadb_schema.DECODE_ORACLE(1);
#
# Testing that REPLACE, SUBSTR, TRIM print the exact name
# as typed by the user in "Function .. is not defined"
#
SET sql_mode=DEFAULT;
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.TRIM(1);
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.trim(1);
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.TRIM();
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.TRIM('a','b');
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.TRIM('a','b','c','d');
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.SUBSTR('a',1,2);
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.substr('a',1,2);
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.SUBSTRING('a',1,2);
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.substring('a',1,2);
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.REPLACE('a','b','c');
--error ER_FUNC_INEXISTENT_NAME_COLLISION
SELECT unknown.replace('a','b','c');
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.REPLACE();
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.REPLACE('a');
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.REPLACE('a','b');
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.REPLACE('a','b','c','d');
#
# Testing EXPLAIN EXTENDED SELECT
#
SET sql_mode=DEFAULT;
DELIMITER $$;
CREATE PROCEDURE p1(sqlmode TEXT, qualifier TEXT, expr TEXT)
BEGIN
DECLARE query TEXT DEFAULT 'SELECT $(QUALIFIER)$(EXPR)';
DECLARE errmsg TEXT DEFAULT NULL;
DECLARE CONTINUE HANDLER FOR 1064, 1128, 1305, 1582, 1630
BEGIN
GET DIAGNOSTICS CONDITION 1 errmsg = MESSAGE_TEXT;
END;
SET sql_mode=sqlmode;
SET query=REPLACE(query, '$(QUALIFIER)', qualifier);
SET query=REPLACE(query, '$(EXPR)', expr);
SET query= CONCAT('EXPLAIN EXTENDED ', query);
SELECT CONCAT('sql_mode=''',sqlmode,'''', ' ',
'qualifier=''',qualifier,'''') AS `----------`;
SELECT query;
EXECUTE IMMEDIATE query;
IF errmsg IS NOT NULL THEN
SELECT CONCAT('ERROR: ', errmsg) AS errmsg;
ELSE
SHOW WARNINGS;
END IF;
END;
$$
CREATE PROCEDURE p2(sqlmode TEXT, expr TEXT)
BEGIN
CALL p1(sqlmode, '', expr);
CALL p1(sqlmode, 'unknown_schema.', expr);
CALL p1(sqlmode, 'mariadb_schema.', expr);
CALL p1(sqlmode, 'maxdb_schema.', expr);
CALL p1(sqlmode, 'oracle_schema.', expr);
END;
$$
CREATE PROCEDURE p3(expr TEXT)
BEGIN
CALL p2('', expr);
CALL p2('ORACLE', expr);
END;
$$
DELIMITER ;$$
CALL p3('CONCAT(''a'')');
# MariaDB style
CALL p3('DECODE(''1'',''2'')');
# Oracle style
CALL p3('DECODE(1,1,10)');
CALL p3('LTRIM(''a'')');
CALL p3('RTRIM(''a'')');
CALL p3('LPAD(''a'',3)');
CALL p3('LPAD(''a'',3, '' '')');
CALL p3('RPAD(''a'',3)');
CALL p3('RPAD(''a'',3, '' '')');
CALL p3('REPLACE()');
CALL p3('REPLACE(''a'',''b'')');
CALL p3('REPLACE(''a'',''b'',''c'',''d'')');
CALL p3('REPLACE(''a'',''b'',''c'')');
CALL p3('SUBSTR()');
CALL p3('SUBSTR(''a'',1,2,3)');
CALL p3('SUBSTR(''a'',1,2)');
CALL p3('SUBSTR(''a'' FROM 1)');
CALL p3('SUBSTRING(''a'',1,2)');
CALL p3('SUBSTRING(''a'' FROM 1)');
CALL p3('TRIM()');
CALL p3('TRIM(1,2)');
CALL p3('TRIM(''a'')');
CALL p3('TRIM(BOTH '' '' FROM ''a'')');
# Deprecated compatibility XXX_ORACLE functions.
# These functions are implemented as simple native functions
# and have no special grammar rules in sql_yacc.yy.
# So they support the qualified syntax automatically,
# which is not absolutely required, but is not harmful.
CALL p3('CONCAT_OPERATOR_ORACLE(''a'')');
CALL p3('DECODE_ORACLE(1,1,10)');
CALL p3('LTRIM_ORACLE(''a'')');
CALL p3('RTRIM_ORACLE(''a'')');
CALL p3('LPAD_ORACLE(''a'',3)');
CALL p3('RPAD_ORACLE(''a'',3)');
CALL p3('REPLACE_ORACLE(''a'',''b'',''c'')');
CALL p3('SUBSTR_ORACLE(''a'',1,2)');
# Deprecated compatibility XXX_ORACLE variants for functions
# with a special syntax in sql_yacc.yy.
# These compatibility functions do not support qualified syntax.
# One should use a qualified variant without the _ORACLE suffix instead.
--error ER_PARSE_ERROR
SELECT oracle_schema.SUBSTR_ORACLE('a' FROM 1 FOR 2);
# Use this instead:
SELECT oracle_schema.SUBSTR('a' FROM 1 FOR 2);
--error ER_PARSE_ERROR
SELECT oracle_schema.TRIM_ORACLE(LEADING ' ' FROM 'a');
# Use this instead:
SELECT oracle_schema.TRIM(LEADING ' ' FROM 'a');
--error ER_FUNCTION_NOT_DEFINED
SELECT oracle_schema.TRIM_ORACLE('a');
# Use this instead:
SELECT oracle_schema.TRIM('a');
DROP PROCEDURE p1;
DROP PROCEDURE p2;
DROP PROCEDURE p3;
SET sql_mode='';
CREATE VIEW v1 AS SELECT
concat('a','b'),
decode('1','2'),
ltrim('1'),
rtrim('1'),
lpad('1','2', 3),
rpad('1','2', 3),
replace('1','2','3'),
substr('a',1,2),
trim(both 'a' FROM 'b');
CREATE TABLE kv (v BLOB);
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
SELECT v FROM kv WHERE v RLIKE '^(query|view_body_utf8)=' ORDER BY v;
SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1' AND TABLE_SCHEMA='test';
DROP TABLE kv;
DROP VIEW v1;
SET sql_mode='ORACLE';
CREATE VIEW v1 AS SELECT
concat('a','b'),
decode('1',2,3),
ltrim('1'),
rtrim('1'),
lpad('1','2', 3),
rpad('1','2', 3),
replace('1','2','3'),
substr('a',1,2),
trim(both 'a' FROM 'b');
CREATE TABLE kv (v BLOB);
--replace_result $MYSQLD_DATADIR MYSQLD_DATADIR
eval LOAD DATA INFILE '$MYSQLD_DATADIR/test/v1.frm' REPLACE INTO TABLE kv;
SELECT v FROM kv WHERE v RLIKE '^(query|view_body_utf8)=' ORDER BY v;
SELECT VIEW_DEFINITION FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME='v1' AND TABLE_SCHEMA='test';
DROP TABLE kv;
DROP VIEW v1;

View file

@ -0,0 +1,50 @@
# See comments in mysql-test/main/mysqldump_restore.test
--source include/not_embedded.inc
let $mysqldumpfile = $MYSQLTEST_VARDIR/tmp/mysqldump_func_qualified.sql;
--echo #
--echo # Start of 10.4 tests
--echo #
--echo #
--echo # MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE
--echo #
SET sql_mode=DEFAULT;
CREATE TABLE t1 (
a0 VARCHAR(64) NOT NULL DEFAULT LTRIM(now()),
a1 VARCHAR(64) AS (LTRIM(a0)) PERSISTENT,
b0 VARCHAR(64) NOT NULL DEFAULT LPAD(now(),10),
b1 VARCHAR(64) AS (LPAD(b0,10)) PERSISTENT
);
CREATE VIEW v1 AS SELECT
LTRIM(now()) AS a0,
LPAD(now(),10) AS b0;
SET sql_mode=ORACLE;
CREATE TABLE t2 (
a0 VARCHAR(64) NOT NULL DEFAULT LTRIM(now()),
a1 VARCHAR(64) AS (LTRIM(a0)) PERSISTENT,
b0 VARCHAR(64) NOT NULL DEFAULT LPAD(now(),10),
b1 VARCHAR(64) AS (LPAD(b0,10)) PERSISTENT
);
CREATE VIEW v2 AS SELECT
LTRIM(now()) AS a0,
LPAD(now(),10) AS b0;
--exec $MYSQL_DUMP --skip-extended-insert test --skip-comments --compact t1 t2 v1 v2
--exec $MYSQL_DUMP --skip-extended-insert test --skip-comments t1 t2 v1 v2 > $mysqldumpfile
DROP TABLE t1,t2;
DROP VIEW v1,v2;
--exec $MYSQL test < $mysqldumpfile
SET sql_mode=DEFAULT;
SHOW CREATE TABLE t1;
SHOW CREATE TABLE t2;
SHOW CREATE VIEW v1;
SHOW CREATE VIEW v2;
--remove_file $mysqldumpfile
DROP TABLE t1,t2;
DROP VIEW v1, v2;
--echo #
--echo # End of 10.4 tests
--echo #

View file

@ -0,0 +1,47 @@
--source include/have_innodb.inc
--echo #
--echo # MDEV-27744 LPAD in vcol created in ORACLE mode makes table corrupted in non-ORACLE
--echo #
FLUSH TABLES;
SET sql_mode='';
CREATE TABLE t (d INT,b VARCHAR(1),c CHAR(1),g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b),KEY g(g)) ENGINE=InnoDB;
--error ER_WRONG_VALUE_COUNT_ON_ROW
INSERT INTO t VALUES (0);
SET sql_mode='ORACLE';
INSERT INTO t SET c=REPEAT (1,0);
--error ER_BAD_FIELD_ERROR
ALTER TABLE t CHANGE COLUMN a b INT;
DELETE FROM t;
SET sql_mode='';
FLUSH TABLES;
INSERT INTO t SET c='0';
DROP TABLE t;
FLUSH TABLES;
SET sql_mode='';
CREATE TABLE t (a INT(1),d INT(1),b VARCHAR(1),c CHAR(1),vadc INT(1) GENERATED ALWAYS AS ( (a + length (d))) STORED,vbc CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,vbidxc CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b (1),a,d),KEY d (d),KEY a (a),KEY c_renamed (c (1),b (1)),KEY b (b (1),c (1),a),KEY vbidxc (vbidxc),KEY a_2 (a,vbidxc),KEY vbidxc_2 (vbidxc,d)) DEFAULT CHARSET=latin1 ENGINE=InnoDB;
--error ER_WRONG_VALUE_COUNT_ON_ROW
INSERT INTO t VALUES (0,0,1,0,1,0,1,0,0);
SET SESSION sql_mode='ORACLE';
INSERT INTO t SET c=REPEAT (1,0);
--error ER_DUP_FIELDNAME
ALTER TABLE t CHANGE COLUMN a b CHAR(1);
DELETE FROM t;
SET SESSION sql_mode=DEFAULT;
DROP TABLE t;
SET sql_mode='';
CREATE TABLE t1 (d INT,b VARCHAR(1),c CHAR(1),g CHAR(1) GENERATED ALWAYS AS (SUBSTR(b,0,0)) VIRTUAL,PRIMARY KEY(b),KEY g(g)) ENGINE=InnoDB;
--error ER_WRONG_VALUE_COUNT_ON_ROW
INSERT INTO t1 VALUES (0);
SET sql_mode='ORACLE';
INSERT INTO t1 SET c=REPEAT (1,0);
--error ER_BAD_FIELD_ERROR
ALTER TABLE t1 CHANGE COLUMN a b INT;
DELETE FROM t1;
SET sql_mode='';
FLUSH TABLES;
INSERT INTO t1 SET c='0';
DROP TABLE t1;

View file

@ -28,6 +28,7 @@
#include "field.h" /* Derivation */
#include "sql_type.h"
#include "sql_time.h"
#include "sql_schema.h"
#include "mem_root_array.h"
C_MODE_START
@ -1727,7 +1728,8 @@ public:
QT_ITEM_IDENT_SKIP_DB_NAMES |
QT_ITEM_IDENT_SKIP_TABLE_NAMES |
QT_NO_DATA_EXPANSION |
QT_TO_SYSTEM_CHARSET),
QT_TO_SYSTEM_CHARSET |
QT_FOR_FRM),
LOWEST_PRECEDENCE);
}
virtual void print(String *str, enum_query_type query_type);
@ -5301,6 +5303,14 @@ public:
return true;
return (this->*processor)(arg);
}
/*
Built-in schema, e.g. mariadb_schema, oracle_schema, maxdb_schema
*/
virtual const Schema *schema() const
{
// A function does not belong to a built-in schema by default
return NULL;
}
/*
This method is used for debug purposes to print the name of an
item to the debug log. The second use of this method is as

View file

@ -3406,7 +3406,13 @@ void Item_func_case_simple::print(String *str, enum_query_type query_type)
void Item_func_decode_oracle::print(String *str, enum_query_type query_type)
{
str->append(func_name());
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("decode_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
str->append('(');
args[0]->print(str, query_type);
for (uint i= 1, count= when_count() ; i <= count; i++)

View file

@ -2281,7 +2281,8 @@ public:
Item_func_decode_oracle(THD *thd, List<Item> &list)
:Item_func_case_simple(thd, list)
{ }
const char *func_name() const { return "decode_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
const char *func_name() const { return "decode"; }
void print(String *str, enum_query_type query_type);
bool fix_length_and_dec();
Item *find_item();

View file

@ -626,9 +626,6 @@ public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
if (thd->variables.sql_mode & MODE_ORACLE)
return Create_func_decode_oracle::s_singleton.create_native(thd, name,
item_list);
if (unlikely(!item_list || item_list->elements != 2))
{
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str);
@ -2302,9 +2299,7 @@ public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
return thd->variables.sql_mode & MODE_ORACLE ?
create_native_oracle(thd, name, item_list) :
create_native_std(thd, name, item_list);
return create_native_std(thd, name, item_list);
}
static Create_func_lpad s_singleton;
@ -2780,9 +2775,7 @@ public:
virtual Item *create_native(THD *thd, const LEX_CSTRING *name,
List<Item> *item_list)
{
return thd->variables.sql_mode & MODE_ORACLE ?
create_native_oracle(thd, name, item_list) :
create_native_std(thd, name, item_list);
return create_native_std(thd, name, item_list);
}
static Create_func_rpad s_singleton;
@ -4075,9 +4068,7 @@ Create_func_concat::create_native(THD *thd, const LEX_CSTRING *name,
return NULL;
}
return thd->variables.sql_mode & MODE_ORACLE ?
new (thd->mem_root) Item_func_concat_operator_oracle(thd, *item_list) :
new (thd->mem_root) Item_func_concat(thd, *item_list);
return new (thd->mem_root) Item_func_concat(thd, *item_list);
}
Create_func_concat_operator_oracle
@ -5915,10 +5906,7 @@ Create_func_length Create_func_length::s_singleton;
Item*
Create_func_length::create_1_arg(THD *thd, Item *arg1)
{
if (thd->variables.sql_mode & MODE_ORACLE)
return new (thd->mem_root) Item_func_char_length(thd, arg1);
else
return new (thd->mem_root) Item_func_octet_length(thd, arg1);
return new (thd->mem_root) Item_func_octet_length(thd, arg1);
}
Create_func_octet_length Create_func_octet_length::s_singleton;
@ -6136,7 +6124,7 @@ Create_func_ltrim Create_func_ltrim::s_singleton;
Item*
Create_func_ltrim::create_1_arg(THD *thd, Item *arg1)
{
return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim(thd);
return Lex_trim(TRIM_LEADING, arg1).make_item_func_trim_std(thd);
}
@ -6691,7 +6679,7 @@ Create_func_rtrim Create_func_rtrim::s_singleton;
Item*
Create_func_rtrim::create_1_arg(THD *thd, Item *arg1)
{
return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim(thd);
return Lex_trim(TRIM_TRAILING, arg1).make_item_func_trim_std(thd);
}
@ -7621,9 +7609,25 @@ const Native_func_registry func_array[] =
};
const Native_func_registry func_array_oracle_overrides[] =
{
{ { STRING_WITH_LEN("CONCAT") }, BUILDER(Create_func_concat_operator_oracle)},
{ { STRING_WITH_LEN("DECODE") }, BUILDER(Create_func_decode_oracle)},
{ { STRING_WITH_LEN("LENGTH") }, BUILDER(Create_func_char_length)},
{ { STRING_WITH_LEN("LPAD") }, BUILDER(Create_func_lpad_oracle)},
{ { STRING_WITH_LEN("LTRIM") }, BUILDER(Create_func_ltrim_oracle)},
{ { STRING_WITH_LEN("RPAD") }, BUILDER(Create_func_rpad_oracle)},
{ { STRING_WITH_LEN("RTRIM") }, BUILDER(Create_func_rtrim_oracle)},
{ {0, 0}, NULL}
};
const size_t func_array_length= sizeof(func_array) / sizeof(Native_func_registry) - 1;
Native_functions_hash native_functions_hash;
Native_functions_hash native_functions_hash_oracle;
extern "C" uchar*
get_native_fct_hash_key(const uchar *buff, size_t *length,
@ -7735,13 +7739,19 @@ Native_functions_hash::find(THD *thd, const LEX_CSTRING &name) const
int item_create_init()
{
return native_functions_hash.init(func_array, array_elements(func_array));
return
native_functions_hash.init(func_array, array_elements(func_array)) ||
native_functions_hash_oracle.init(array_elements(func_array) +
array_elements(func_array_oracle_overrides)) ||
native_functions_hash_oracle.append(func_array) ||
native_functions_hash_oracle.replace(func_array_oracle_overrides);
}
void item_create_cleanup()
{
native_functions_hash.cleanup();
native_functions_hash_oracle.cleanup();
}

View file

@ -220,6 +220,12 @@ public:
}
bool append(const Native_func_registry array[]);
bool remove(const Native_func_registry array[]);
bool replace(const Native_func_registry array[])
{
DBUG_ENTER("Native_functions_hash::replace");
remove(array);
DBUG_RETURN(append(array));
}
void cleanup();
/**
Find the native function builder associated with a given function name.
@ -231,6 +237,7 @@ public:
};
extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash;
extern MYSQL_PLUGIN_IMPORT Native_functions_hash native_functions_hash_oracle;
extern const Native_func_registry func_array[];
extern const size_t func_array_length;

View file

@ -131,6 +131,16 @@ Item_args::Item_args(THD *thd, const Item_args *other)
}
void Item_func::wrong_param_count_error(const LEX_CSTRING &schema_name,
const LEX_CSTRING &func_name)
{
DBUG_ASSERT(schema_name.length);
Database_qualified_name qname(schema_name, func_name);
my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0),
ErrConvDQName(&qname).ptr());
}
void Item_func::sync_with_sum_func_and_with_field(List<Item> &list)
{
List_iterator_fast<Item> li(list);
@ -607,13 +617,12 @@ table_map Item_func::not_null_tables() const
void Item_func::print(String *str, enum_query_type query_type)
{
str->append(func_name());
str->append('(');
print_args(str, 0, query_type);
str->append(')');
print_args_parenthesized(str, query_type);
}
void Item_func::print_args(String *str, uint from, enum_query_type query_type)
void Item_func::print_args(String *str, uint from,
enum_query_type query_type) const
{
for (uint i=from ; i < arg_count ; i++)
{

View file

@ -56,8 +56,40 @@ protected:
bool check_argument_types_can_return_date(uint start, uint end) const;
bool check_argument_types_can_return_time(uint start, uint end) const;
void print_cast_temporal(String *str, enum_query_type query_type);
void print_schema_qualified_name(String *to,
const LEX_CSTRING &schema_name,
const char *function_name) const
{
// e.g. oracle_schema.func()
to->append(schema_name);
to->append('.');
to->append(function_name);
}
void print_sql_mode_qualified_name(String *to,
enum_query_type query_type,
const char *function_name) const
{
const Schema *func_schema= schema();
if (!func_schema || func_schema == Schema::find_implied(current_thd))
to->append(function_name);
else
print_schema_qualified_name(to, func_schema->name(), function_name);
}
void print_sql_mode_qualified_name(String *to, enum_query_type query_type)
const
{
return print_sql_mode_qualified_name(to, query_type, func_name());
}
public:
// Print an error message for a builtin-schema qualified function call
static void wrong_param_count_error(const LEX_CSTRING &schema_name,
const LEX_CSTRING &func_name);
table_map not_null_tables_cache;
enum Functype { UNKNOWN_FUNC,EQ_FUNC,EQUAL_FUNC,NE_FUNC,LT_FUNC,LE_FUNC,
@ -179,7 +211,13 @@ public:
List<Item> &fields, uint flags);
virtual void print(String *str, enum_query_type query_type);
void print_op(String *str, enum_query_type query_type);
void print_args(String *str, uint from, enum_query_type query_type);
void print_args(String *str, uint from, enum_query_type query_type) const;
void print_args_parenthesized(String *str, enum_query_type query_type) const
{
str->append('(');
print_args(str, 0, query_type);
str->append(')');
}
bool is_null() {
update_null_value();
return null_value;

View file

@ -2170,13 +2170,31 @@ bool Item_func_trim::fix_length_and_dec()
void Item_func_trim::print(String *str, enum_query_type query_type)
{
LEX_CSTRING suffix= {STRING_WITH_LEN("_oracle")};
if (arg_count == 1)
{
Item_func::print(str, query_type);
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(func_name());
if (schema() == &oracle_schema_ref)
str->append(suffix);
}
else
print_sql_mode_qualified_name(str, query_type, func_name());
print_args_parenthesized(str, query_type);
return;
}
str->append(Item_func_trim::func_name());
str->append(func_name_ext());
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(Item_func_trim::func_name());
if (schema() == &oracle_schema_ref)
str->append(suffix);
}
else
print_sql_mode_qualified_name(str, query_type, Item_func_trim::func_name());
str->append('(');
str->append(mode_name());
str->append(' ');

View file

@ -261,6 +261,12 @@ public:
Item_func_concat(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
String *val_str(String *);
bool fix_length_and_dec();
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "concat"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_concat>(thd, this); }
@ -281,7 +287,18 @@ public:
:Item_func_concat(thd, a, b)
{ }
String *val_str(String *);
const char *func_name() const { return "concat_operator_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
void print(String *str, enum_query_type query_type)
{
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("concat_operator_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd)
{
return get_item_copy<Item_func_concat_operator_oracle>(thd, this);
@ -342,6 +359,12 @@ public:
String *val_str(String *to) { return val_str_internal(to, NULL); };
bool fix_length_and_dec();
String *val_str_internal(String *str, String *empty_string_for_null);
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "replace"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_replace>(thd, this); }
@ -355,7 +378,18 @@ public:
Item_func_replace_oracle(THD *thd, Item *org, Item *find, Item *replace):
Item_func_replace(thd, org, find, replace) {}
String *val_str(String *to) { return val_str_internal(to, &tmp_emtpystr); };
const char *func_name() const { return "replace_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
void print(String *str, enum_query_type query_type)
{
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("replace_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_replace_oracle>(thd, this); }
};
@ -492,8 +526,16 @@ public:
Item_func_substr(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_substr(THD *thd, Item *a, Item *b, Item *c):
Item_str_func(thd, a, b, c) {}
Item_func_substr(THD *thd, List<Item> &list)
:Item_str_func(thd, list) {}
String *val_str(String *);
bool fix_length_and_dec();
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "substr"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_substr>(thd, this); }
@ -511,13 +553,26 @@ public:
Item_func_substr(thd, a, b) {}
Item_func_substr_oracle(THD *thd, Item *a, Item *b, Item *c):
Item_func_substr(thd, a, b, c) {}
Item_func_substr_oracle(THD *thd, List<Item> &list)
:Item_func_substr(thd, list) {}
bool fix_length_and_dec()
{
bool res= Item_func_substr::fix_length_and_dec();
maybe_null= true;
return res;
}
const char *func_name() const { return "substr_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
void print(String *str, enum_query_type query_type)
{
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("substr_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_substr_oracle>(thd, this); }
};
@ -560,13 +615,13 @@ protected:
{
return trimmed_value(res, 0, res->length());
}
virtual const char *func_name_ext() const { return ""; }
public:
Item_func_trim(THD *thd, Item *a, Item *b): Item_str_func(thd, a, b) {}
Item_func_trim(THD *thd, Item *a): Item_str_func(thd, a) {}
Sql_mode_dependency value_depends_on_sql_mode() const;
String *val_str(String *);
bool fix_length_and_dec();
const Schema *schema() const { return &mariadb_schema; }
const char *func_name() const { return "trim"; }
void print(String *str, enum_query_type query_type);
virtual const char *mode_name() const { return "both"; }
@ -580,12 +635,11 @@ class Item_func_trim_oracle :public Item_func_trim
protected:
String *make_empty_result(String *str)
{ null_value= 1; return NULL; }
const char *func_name_ext() const { return "_oracle"; }
public:
Item_func_trim_oracle(THD *thd, Item *a, Item *b):
Item_func_trim(thd, a, b) {}
Item_func_trim_oracle(THD *thd, Item *a): Item_func_trim(thd, a) {}
const char *func_name() const { return "trim_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
bool fix_length_and_dec()
{
bool res= Item_func_trim::fix_length_and_dec();
@ -607,6 +661,7 @@ public:
return Item_func::value_depends_on_sql_mode();
}
String *val_str(String *);
const Schema *schema() const { return &mariadb_schema; }
const char *func_name() const { return "ltrim"; }
const char *mode_name() const { return "leading"; }
Item *get_copy(THD *thd)
@ -619,12 +674,11 @@ class Item_func_ltrim_oracle :public Item_func_ltrim
protected:
String *make_empty_result(String *str)
{ null_value= 1; return NULL; }
const char *func_name_ext() const { return "_oracle"; }
public:
Item_func_ltrim_oracle(THD *thd, Item *a, Item *b):
Item_func_ltrim(thd, a, b) {}
Item_func_ltrim_oracle(THD *thd, Item *a): Item_func_ltrim(thd, a) {}
const char *func_name() const { return "ltrim_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
bool fix_length_and_dec()
{
bool res= Item_func_ltrim::fix_length_and_dec();
@ -642,6 +696,7 @@ public:
Item_func_rtrim(THD *thd, Item *a, Item *b): Item_func_trim(thd, a, b) {}
Item_func_rtrim(THD *thd, Item *a): Item_func_trim(thd, a) {}
String *val_str(String *);
const Schema *schema() const { return &mariadb_schema; }
const char *func_name() const { return "rtrim"; }
const char *mode_name() const { return "trailing"; }
Item *get_copy(THD *thd)
@ -654,12 +709,11 @@ class Item_func_rtrim_oracle :public Item_func_rtrim
protected:
String *make_empty_result(String *str)
{ null_value= 1; return NULL; }
const char *func_name_ext() const { return "_oracle"; }
public:
Item_func_rtrim_oracle(THD *thd, Item *a, Item *b):
Item_func_rtrim(thd, a, b) {}
Item_func_rtrim_oracle(THD *thd, Item *a): Item_func_rtrim(thd, a) {}
const char *func_name() const { return "rtrim_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
bool fix_length_and_dec()
{
bool res= Item_func_rtrim::fix_length_and_dec();
@ -821,6 +875,12 @@ class Item_func_decode :public Item_func_encode
{
public:
Item_func_decode(THD *thd, Item *a, Item *seed_arg): Item_func_encode(thd, a, seed_arg) {}
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "decode"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_decode>(thd, this); }
@ -1126,6 +1186,8 @@ public:
Item_str_func(thd, arg1, arg2, arg3) {}
Item_func_pad(THD *thd, Item *arg1, Item *arg2):
Item_str_func(thd, arg1, arg2) {}
Item_func_pad(THD *thd, List<Item> &list):
Item_str_func(thd,list) {}
bool fix_length_and_dec();
};
@ -1137,7 +1199,15 @@ public:
Item_func_pad(thd, arg1, arg2, arg3) {}
Item_func_rpad(THD *thd, Item *arg1, Item *arg2):
Item_func_pad(thd, arg1, arg2) {}
Item_func_rpad(THD *thd, List<Item> &list):
Item_func_pad(thd,list) {}
String *val_str(String *);
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "rpad"; }
Sql_mode_dependency value_depends_on_sql_mode() const;
Item *get_copy(THD *thd)
@ -1154,13 +1224,26 @@ public:
Item_func_rpad(thd, arg1, arg2, arg3) {}
Item_func_rpad_oracle(THD *thd, Item *arg1, Item *arg2):
Item_func_rpad(thd, arg1, arg2) {}
Item_func_rpad_oracle(THD *thd, List<Item> &list):
Item_func_rpad(thd,list) {}
bool fix_length_and_dec()
{
bool res= Item_func_rpad::fix_length_and_dec();
maybe_null= true;
return res;
}
const char *func_name() const { return "rpad_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
void print(String *str, enum_query_type query_type)
{
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("rpad_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_rpad_oracle>(thd, this); }
};
@ -1173,7 +1256,15 @@ public:
Item_func_pad(thd, arg1, arg2, arg3) {}
Item_func_lpad(THD *thd, Item *arg1, Item *arg2):
Item_func_pad(thd, arg1, arg2) {}
Item_func_lpad(THD *thd, List<Item> &list):
Item_func_pad(thd,list) {}
String *val_str(String *);
const Schema *schema() const { return &mariadb_schema; }
void print(String *str, enum_query_type query_type)
{
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
const char *func_name() const { return "lpad"; }
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_lpad>(thd, this); }
@ -1189,13 +1280,26 @@ public:
Item_func_lpad(thd, arg1, arg2, arg3) {}
Item_func_lpad_oracle(THD *thd, Item *arg1, Item *arg2):
Item_func_lpad(thd, arg1, arg2) {}
Item_func_lpad_oracle(THD *thd, List<Item> &list):
Item_func_lpad(thd,list) {}
bool fix_length_and_dec()
{
bool res= Item_func_lpad::fix_length_and_dec();
maybe_null= true;
return res;
}
const char *func_name() const { return "lpad_oracle"; }
const Schema *schema() const { return &oracle_schema_ref; }
void print(String *str, enum_query_type query_type)
{
if (query_type & QT_FOR_FRM)
{
// 10.3 downgrade compatibility for FRM
str->append(STRING_WITH_LEN("lpad_oracle"));
}
else
print_sql_mode_qualified_name(str, query_type);
print_args_parenthesized(str, query_type);
}
Item *get_copy(THD *thd)
{ return get_item_copy<Item_func_lpad_oracle>(thd, this); }
};

View file

@ -757,6 +757,10 @@ enum enum_query_type
QT_NO_DATA_EXPANSION= (1 << 9),
// Remove wrappers added for TVC when creating or showing view
QT_NO_WRAPPERS_FOR_TVC_IN_VIEW= (1 << 11),
/// Print for FRM file. Focus on parse-back.
/// e.g. VIEW expressions and virtual column expressions
QT_FOR_FRM= (1 << 12)
};

View file

@ -942,7 +942,7 @@ bool is_lex_native_function(const LEX_CSTRING *name)
bool is_native_function(THD *thd, const LEX_CSTRING *name)
{
if (native_functions_hash.find(thd, *name))
if (mariadb_schema.find_native_function_builder(thd, *name))
return true;
if (is_lex_native_function(name))
@ -1618,7 +1618,18 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
if (lex->parsing_options.lookup_keywords_after_qualifier)
next_state= MY_LEX_IDENT_OR_KEYWORD;
else
next_state= MY_LEX_IDENT_START; // Next is ident (not keyword)
{
/*
Next is:
- A qualified func with a special syntax:
mariadb_schema.REPLACE('a','b','c')
mariadb_schema.SUSTRING('a',1,2)
mariadb_schema.TRIM('a')
- Or an identifier otherwise. No keyword lookup is done,
all keywords are treated as identifiers.
*/
next_state= MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC;
}
if (!ident_map[(uchar) yyPeek()]) // Probably ` or "
next_state= MY_LEX_START;
return((int) c);
@ -2062,7 +2073,12 @@ int Lex_input_stream::lex_one_token(YYSTYPE *yylval, THD *thd)
We should now be able to handle:
[(global | local | session) .]variable_name
*/
return scan_ident_sysvar(thd, &yylval->ident_cli);
return scan_ident_common(thd, &yylval->ident_cli,
GENERAL_KEYWORD_OR_FUNC_LPAREN);
case MY_LEX_IDENT_OR_QUALIFIED_SPECIAL_FUNC:
return scan_ident_common(thd, &yylval->ident_cli,
QUALIFIED_SPECIAL_FUNC_LPAREN);
}
}
}
@ -2084,7 +2100,64 @@ bool Lex_input_stream::get_7bit_or_8bit_ident(THD *thd, uchar *last_char)
}
int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
/*
Resolve special SQL functions that have a qualified syntax in sql_yacc.yy.
These functions are not listed in the native function registry
because of a special syntax, or a reserved keyword:
mariadb_schema.SUBSTRING('a' FROM 1 FOR 2) -- Special syntax
mariadb_schema.TRIM(BOTH ' ' FROM 'a') -- Special syntax
mariadb_schema.REPLACE('a','b','c') -- Verb keyword
*/
int Lex_input_stream::find_keyword_qualified_special_func(Lex_ident_cli_st *str,
uint length) const
{
/*
There are many other special functions, see the following grammar rules:
function_call_keyword
function_call_nonkeyword
Here we resolve only those that have a qualified syntax to handle
different behavior in different @@sql_mode settings.
Other special functions do not work in qualified context:
SELECT mariadb_schema.year(now()); -- Function year is not defined
SELECT mariadb_schema.now(); -- Function now is not defined
We don't resolve TRIM_ORACLE here, because it does not have
a qualified syntax yet. Search for "trim_operands" in sql_yacc.yy
to find more comments.
*/
static LEX_CSTRING funcs[]=
{
{STRING_WITH_LEN("SUBSTRING")},
{STRING_WITH_LEN("SUBSTR")},
{STRING_WITH_LEN("TRIM")},
{STRING_WITH_LEN("REPLACE")}
};
int tokval= find_keyword(str, length, true);
if (!tokval)
return 0;
for (size_t i= 0; i < array_elements(funcs); i++)
{
CHARSET_INFO *cs= system_charset_info;
/*
Check length equality to avoid non-ASCII variants
compared as equal to ASCII variants.
*/
if (length == funcs[i].length &&
!cs->coll->strnncollsp(cs,
(const uchar *) m_tok_start, length,
(const uchar *) funcs[i].str, funcs[i].length))
return tokval;
}
return 0;
}
int Lex_input_stream::scan_ident_common(THD *thd, Lex_ident_cli_st *str,
Ident_mode mode)
{
uchar last_char;
uint length;
@ -2098,10 +2171,41 @@ int Lex_input_stream::scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str)
next_state= MY_LEX_IDENT_SEP;
if (!(length= yyLength()))
return ABORT_SYM; // Names must be nonempty.
if ((tokval= find_keyword(str, length, 0)))
{
yyUnget(); // Put back 'c'
return tokval; // Was keyword
switch (mode) {
case GENERAL_KEYWORD_OR_FUNC_LPAREN:
/*
We can come here inside a system variable after "@@",
e.g. @@global.character_set_client.
We resolve all general purpose keywords here.
We can come here when LEX::parsing_options.lookup_keywords_after_qualifier
is true, i.e. within the "field_spec" Bison rule.
We need to resolve functions that have special rules inside sql_yacc.yy,
such as SUBSTR, REPLACE, TRIM, to make this work:
c2 varchar(4) GENERATED ALWAYS AS (mariadb_schema.substr(c1,1,4))
*/
if ((tokval= find_keyword(str, length, last_char == '(')))
{
yyUnget(); // Put back 'c'
return tokval; // Was keyword
}
break;
case QUALIFIED_SPECIAL_FUNC_LPAREN:
/*
We come here after '.' in various contexts:
SELECT @@global.character_set_client;
SELECT t1.a FROM t1;
SELECT test.f1() FROM t1;
SELECT mariadb_schema.trim('a');
*/
if (last_char == '(' &&
(tokval= find_keyword_qualified_special_func(str, length)))
{
yyUnget(); // Put back 'c'
return tokval; // Was keyword
}
break;
}
yyUnget(); // ptr points now after last token char
@ -8644,6 +8748,136 @@ bool LEX::add_grant_command(THD *thd, enum_sql_command sql_command_arg,
}
const Schema *
LEX::find_func_schema_by_name_or_error(const Lex_ident_sys &schema,
const Lex_ident_sys &func)
{
Schema *res= Schema::find_by_name(schema);
if (res)
return res;
Database_qualified_name qname(schema, func);
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), ErrConvDQName(&qname).ptr());
return NULL;
}
Item *LEX::make_item_func_substr(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
const Lex_substring_spec_st &spec)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
const Schema *schema= find_func_schema_by_name_or_error(schema_name,
func_name);
return schema ? schema->make_item_func_substr(thd, spec) : NULL;
}
Item *LEX::make_item_func_substr(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
List<Item> *item_list)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
Schema *schema;
if (item_list &&
(item_list->elements == 2 || item_list->elements == 3) &&
(schema= Schema::find_by_name(schema_name)))
{
Item_args args(thd, *item_list);
Lex_substring_spec_st spec=
Lex_substring_spec_st::init(args.arguments()[0],
args.arguments()[1],
item_list->elements == 3 ?
args.arguments()[2] : NULL);
return schema->make_item_func_substr(thd, spec);
}
return make_item_func_call_generic(thd, schema_name, func_name, item_list);
}
Item *LEX::make_item_func_replace(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
Item *org,
Item *find,
Item *replace)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
const Schema *schema= find_func_schema_by_name_or_error(schema_name,
func_name);
return schema ? schema->make_item_func_replace(thd, org, find, replace) :
NULL;
}
Item *LEX::make_item_func_replace(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
List<Item> *item_list)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
const Schema *schema;
if (item_list && item_list->elements == 3 &&
(schema= Schema::find_by_name(schema_name)))
{
Item_args args(thd, *item_list);
return schema->make_item_func_replace(thd, args.arguments()[0],
args.arguments()[1],
args.arguments()[2]);
}
return make_item_func_call_generic(thd, schema_name, func_name, item_list);
}
Item *LEX::make_item_func_trim(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
const Lex_trim_st &spec)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
const Schema *schema= find_func_schema_by_name_or_error(schema_name,
func_name);
return schema ? schema->make_item_func_trim(thd, spec) : NULL;
}
Item *LEX::make_item_func_trim(THD *thd,
const Lex_ident_cli_st &schema_name_cli,
const Lex_ident_cli_st &func_name_cli,
List<Item> *item_list)
{
Lex_ident_sys schema_name(thd, &schema_name_cli);
Lex_ident_sys func_name(thd, &func_name_cli);
if (schema_name.is_null() || func_name.is_null())
return NULL; // EOM
const Schema *schema;
if (item_list && item_list->elements == 1 &&
(schema= Schema::find_by_name(schema_name)))
{
Item_args args(thd, *item_list);
Lex_trim spec(TRIM_BOTH, args.arguments()[0]);
return schema->make_item_func_trim(thd, spec);
}
return make_item_func_call_generic(thd, schema_name, func_name, item_list);
}
bool SELECT_LEX::vers_push_field(THD *thd, TABLE_LIST *table,
const LEX_CSTRING field_name)
{
@ -8722,16 +8956,10 @@ Item *Lex_trim_st::make_item_func_trim_oracle(THD *thd) const
}
Item *Lex_trim_st::make_item_func_trim(THD *thd) const
{
return (thd->variables.sql_mode & MODE_ORACLE) ?
make_item_func_trim_oracle(thd) :
make_item_func_trim_std(thd);
}
Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
Lex_ident_cli_st *cname, List<Item> *args)
Item *LEX::make_item_func_call_generic(THD *thd,
const Lex_ident_cli_st *cdb,
const Lex_ident_cli_st *cname,
List<Item> *args)
{
Lex_ident_sys db(thd, cdb), name(thd, cname);
if (db.is_null() || name.is_null())
@ -8758,6 +8986,19 @@ Item *LEX::make_item_func_call_generic(THD *thd, Lex_ident_cli_st *cdb,
if (check_routine_name(&name))
return NULL;
return make_item_func_call_generic(thd, db, name, args);
}
Item *LEX::make_item_func_call_generic(THD *thd,
const Lex_ident_sys &db,
const Lex_ident_sys &name,
List<Item> *args)
{
const Schema *schema= Schema::find_by_name(db);
if (schema)
return schema->make_item_func_call_native(thd, name, args);
Create_qfunc *builder= find_qualified_function_builder(thd);
DBUG_ASSERT(builder);
return builder->create_with_db(thd, &db, &name, true, args);

View file

@ -2407,6 +2407,15 @@ public:
void reduce_digest_token(uint token_left, uint token_right);
private:
enum Ident_mode
{
GENERAL_KEYWORD_OR_FUNC_LPAREN,
QUALIFIED_SPECIAL_FUNC_LPAREN
};
int scan_ident_common(THD *thd, Lex_ident_cli_st *str, Ident_mode mode);
/**
Set the echo mode.
@ -2733,8 +2742,8 @@ private:
bool consume_comment(int remaining_recursions_permitted);
int lex_one_token(union YYSTYPE *yylval, THD *thd);
int find_keyword(Lex_ident_cli_st *str, uint len, bool function) const;
int find_keyword_qualified_special_func(Lex_ident_cli_st *str, uint len) const;
LEX_CSTRING get_token(uint skip, uint length);
int scan_ident_sysvar(THD *thd, Lex_ident_cli_st *str);
int scan_ident_start(THD *thd, Lex_ident_cli_st *str);
int scan_ident_middle(THD *thd, Lex_ident_cli_st *str,
CHARSET_INFO **cs, my_lex_states *);
@ -4045,8 +4054,41 @@ public:
Item *create_item_query_expression(THD *thd, st_select_lex_unit *unit);
Item *make_item_func_call_generic(THD *thd, Lex_ident_cli_st *db,
Lex_ident_cli_st *name, List<Item> *args);
static const Schema *
find_func_schema_by_name_or_error(const Lex_ident_sys &schema_name,
const Lex_ident_sys &func_name);
Item *make_item_func_replace(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
Item *org, Item *find, Item *replace);
Item *make_item_func_replace(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
List<Item> *args);
Item *make_item_func_substr(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
const Lex_substring_spec_st &spec);
Item *make_item_func_substr(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
List<Item> *args);
Item *make_item_func_trim(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
const Lex_trim_st &spec);
Item *make_item_func_trim(THD *thd,
const Lex_ident_cli_st &schema_name,
const Lex_ident_cli_st &func_name,
List<Item> *args);
Item *make_item_func_call_generic(THD *thd,
const Lex_ident_cli_st *db,
const Lex_ident_cli_st *name,
List<Item> *args);
Item *make_item_func_call_generic(THD *thd,
const Lex_ident_sys &db,
const Lex_ident_sys &name,
List<Item> *args);
Item *make_item_func_call_generic(THD *thd,
Lex_ident_cli_st *db,
Lex_ident_cli_st *pkg,

View file

@ -33,6 +33,12 @@ public:
return src;
}
Create_func *find_native_function_builder(THD *thd, const LEX_CSTRING &name)
const
{
return native_functions_hash_oracle.find(thd, name);
}
Item *make_item_func_replace(THD *thd,
Item *subj,
Item *find,
@ -64,6 +70,7 @@ Schema mariadb_schema(Lex_cstring(STRING_WITH_LEN("mariadb_schema")));
Schema_oracle oracle_schema(Lex_cstring(STRING_WITH_LEN("oracle_schema")));
Schema_maxdb maxdb_schema(Lex_cstring(STRING_WITH_LEN("maxdb_schema")));
const Schema &oracle_schema_ref= oracle_schema;
Schema *Schema::find_by_name(const LEX_CSTRING &name)
{
@ -88,6 +95,26 @@ Schema *Schema::find_implied(THD *thd)
}
Create_func *
Schema::find_native_function_builder(THD *thd, const LEX_CSTRING &name) const
{
return native_functions_hash.find(thd, name);
}
Item *Schema::make_item_func_call_native(THD *thd,
const Lex_ident_sys &name,
List<Item> *args) const
{
Create_func *builder= find_native_function_builder(thd, name);
if (builder)
return builder->create_func(thd, &name, args);
my_error(ER_FUNCTION_NOT_DEFINED, MYF(0), name.str);
return NULL;
}
Item *Schema::make_item_func_replace(THD *thd,
Item *subj,
Item *find,

View file

@ -19,6 +19,9 @@
#include "mysqld.h"
#include "lex_string.h"
class Lex_ident_sys;
class Create_func;
class Schema
{
LEX_CSTRING m_name;
@ -34,6 +37,24 @@ public:
return src;
}
/**
Find a native function builder, return an error if not found,
build an Item otherwise.
*/
Item *make_item_func_call_native(THD *thd,
const Lex_ident_sys &name,
List<Item> *args) const;
/**
Find the native function builder associated with a given function name.
@param thd The current thread
@param name The native function name
@return The native function builder associated with the name, or NULL
*/
virtual Create_func *find_native_function_builder(THD *thd,
const LEX_CSTRING &name)
const;
// Builders for native SQL function with a special syntax in sql_yacc.yy
virtual Item *make_item_func_replace(THD *thd,
Item *subj,
@ -77,5 +98,6 @@ public:
extern Schema mariadb_schema;
extern const Schema &oracle_schema_ref;
#endif // SQL_SCHEMA_H_INCLUDED

View file

@ -954,7 +954,8 @@ static int mysql_register_view(THD *thd, TABLE_LIST *view,
{
Sql_mode_save_for_frm_handling sql_mode_save(thd);
lex->unit.print(&view_query, enum_query_type(QT_VIEW_INTERNAL |
lex->unit.print(&view_query, enum_query_type(QT_FOR_FRM |
QT_VIEW_INTERNAL |
QT_ITEM_ORIGINAL_FUNC_NULLIF |
QT_NO_WRAPPERS_FOR_TVC_IN_VIEW));
lex->unit.print(&is_query, enum_query_type(QT_TO_SYSTEM_CHARSET |

View file

@ -2174,8 +2174,17 @@ END_OF_INPUT
%type <spvar> sp_param_name sp_param_name_and_type
%type <for_loop> sp_for_loop_index_and_bounds
%type <for_loop_bounds> sp_for_loop_bounds
%type <trim> trim_operands
%type <substring_spec> substring_operands
%type <trim>
trim_operands
trim_operands_regular
trim_operands_special
%type <substring_spec>
substring_operands
substring_operands_regular
substring_operands_special
%type <num> opt_sp_for_loop_direction
%type <spvar_mode> sp_opt_inout
%type <index_hint> index_hint_type
@ -10531,8 +10540,16 @@ explicit_cursor_attr:
trim_operands:
trim_operands_regular
| trim_operands_special
;
trim_operands_regular:
expr { $$.set(TRIM_BOTH, $1); }
| LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
;
trim_operands_special:
LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
| TRAILING expr FROM expr { $$.set(TRIM_TRAILING, $2, $4); }
| BOTH expr FROM expr { $$.set(TRIM_BOTH, $2, $4); }
| LEADING FROM expr { $$.set(TRIM_LEADING, $3); }
@ -10941,6 +10958,11 @@ function_call_keyword:
;
substring_operands:
substring_operands_regular
| substring_operands_special
;
substring_operands_regular:
expr ',' expr ',' expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
@ -10949,7 +10971,10 @@ substring_operands:
{
$$= Lex_substring_spec_st::init($1, $3);
}
| expr FROM expr FOR_SYM expr
;
substring_operands_special:
expr FROM expr FOR_SYM expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
}
@ -11470,7 +11495,8 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
builder= native_functions_hash.find(thd, $1);
builder= Schema::find_implied(thd)->
find_native_function_builder(thd, $1);
if (builder)
{
item= builder->create_func(thd, &$1, $4);
@ -11512,6 +11538,43 @@ function_call_generic:
if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7))))
MYSQL_YYABORT;
}
| ident_cli '.' REPLACE '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_replace(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' SUBSTRING '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' SUBSTRING '(' substring_operands_special ')'
{
if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' TRIM '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' TRIM '(' trim_operands_special ')'
{
if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
/*
We don't add a qualified syntax for TRIM_ORACLE here,
as this syntax is not absolutely required:
SELECT mariadb_schema.TRIM_ORACLE(..);
What absolutely required is only:
SELECT mariadb_schema.TRIM(..);
Adding a qualified syntax for TRIM_ORACLE would be tricky because
it is a non-reserved keyword. To avoid new shift/reduce conflicts
it would require grammar changes, like introducing a new rule
ident_step2_cli (which would include everything that ident_cli
includes but TRIM_ORACLE).
*/
;
fulltext_options:

View file

@ -1675,8 +1675,17 @@ END_OF_INPUT
%type <spvar> sp_param_name sp_param_name_and_type
%type <for_loop> sp_for_loop_index_and_bounds
%type <for_loop_bounds> sp_for_loop_bounds
%type <trim> trim_operands
%type <substring_spec> substring_operands
%type <trim>
trim_operands
trim_operands_regular
trim_operands_special
%type <substring_spec>
substring_operands
substring_operands_regular
substring_operands_special
%type <num> opt_sp_for_loop_direction
%type <spvar_mode> sp_opt_inout
%type <index_hint> index_hint_type
@ -10646,8 +10655,16 @@ explicit_cursor_attr:
trim_operands:
trim_operands_regular
| trim_operands_special
;
trim_operands_regular:
expr { $$.set(TRIM_BOTH, $1); }
| LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
;
trim_operands_special:
LEADING expr FROM expr { $$.set(TRIM_LEADING, $2, $4); }
| TRAILING expr FROM expr { $$.set(TRIM_TRAILING, $2, $4); }
| BOTH expr FROM expr { $$.set(TRIM_BOTH, $2, $4); }
| LEADING FROM expr { $$.set(TRIM_LEADING, $3); }
@ -11056,6 +11073,11 @@ function_call_keyword:
;
substring_operands:
substring_operands_regular
| substring_operands_special
;
substring_operands_regular:
expr ',' expr ',' expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
@ -11064,7 +11086,10 @@ substring_operands:
{
$$= Lex_substring_spec_st::init($1, $3);
}
| expr FROM expr FOR_SYM expr
;
substring_operands_special:
expr FROM expr FOR_SYM expr
{
$$= Lex_substring_spec_st::init($1, $3, $5);
}
@ -11585,7 +11610,8 @@ function_call_generic:
This will be revised with WL#2128 (SQL PATH)
*/
builder= native_functions_hash.find(thd, $1);
builder= Schema::find_implied(thd)->
find_native_function_builder(thd, $1);
if (builder)
{
item= builder->create_func(thd, &$1, $4);
@ -11627,6 +11653,31 @@ function_call_generic:
if (unlikely(!($$= Lex->make_item_func_call_generic(thd, &$1, &$3, &$5, $7))))
MYSQL_YYABORT;
}
| ident_cli '.' REPLACE '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_replace(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' SUBSTRING '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' SUBSTRING '(' substring_operands_special ')'
{
if (unlikely(!($$= Lex->make_item_func_substr(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' TRIM '(' opt_expr_list ')'
{
if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
| ident_cli '.' TRIM '(' trim_operands_special ')'
{
if (unlikely(!($$= Lex->make_item_func_trim(thd, $1, $3, $5))))
MYSQL_YYABORT;
}
;
fulltext_options:

View file

@ -33,6 +33,7 @@ struct TABLE;
class Type_handler;
class Field;
class Index_statistics;
struct Lex_ident_cli_st;
class THD;
@ -776,12 +777,6 @@ public:
}
Item *make_item_func_trim_std(THD *thd) const;
Item *make_item_func_trim_oracle(THD *thd) const;
/*
This method is still used to handle LTRIM and RTRIM,
while the special syntax TRIM(... BOTH|LEADING|TRAILING)
is now handled by Schema::make_item_func_trim().
*/
Item *make_item_func_trim(THD *thd) const;
};