From fb67cde2370f7427b3279309daac712c369f1cf8 Mon Sep 17 00:00:00 2001 From: Michael Widenius Date: Fri, 24 Jun 2016 23:42:35 +0200 Subject: [PATCH] Use default character set for expressions - Force usage of () around complex DEFAULT expressions - Give error if DEFAULT expression contains invalid characters - Don't use const_charset_conversion for stored Item_func_sysconf expressions as the result is not constaint over different executions - Fixed Item_func_user() to not store calculated value in str_value --- mysql-test/r/default.result | 183 ++++++++++++++-------- mysql-test/suite/rpl/r/rpl_default.result | 4 +- mysql-test/suite/rpl/t/rpl_default.test | 2 +- mysql-test/t/default.test | 116 ++++++++------ sql/field.cc | 1 + sql/field.h | 4 +- sql/handler.h | 2 +- sql/item.h | 2 + sql/item_cmpfunc.h | 2 + sql/item_func.h | 7 + sql/item_strfunc.cc | 38 +++-- sql/item_strfunc.h | 8 +- sql/item_timefunc.h | 2 + sql/sql_class.cc | 15 +- sql/sql_class.h | 2 +- sql/sql_lex.cc | 2 +- sql/sql_lex.h | 42 ++--- sql/sql_show.cc | 23 ++- sql/sql_yacc.yy | 169 ++++++++++++-------- sql/table.cc | 41 +++-- sql/unireg.cc | 111 +++++++++---- 21 files changed, 508 insertions(+), 268 deletions(-) diff --git a/mysql-test/r/default.result b/mysql-test/r/default.result index b3e00c092c1..26d04cc2656 100644 --- a/mysql-test/r/default.result +++ b/mysql-test/r/default.result @@ -264,13 +264,13 @@ drop table t1; Check default expressions -create or replace table t1 (a int default 1, b int default a+1, c int default a+b) engine myisam; +create or replace table t1 (a int default 1, b int default (a+1), c int default (a+b)) engine myisam; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT '1', - `b` int(11) DEFAULT a+1, - `c` int(11) DEFAULT a+b + `b` int(11) DEFAULT (a+1), + `c` int(11) DEFAULT (a+b) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 insert into t1 values (); insert into t1 (a) values (2); @@ -299,9 +299,9 @@ show create table t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, - `b` blob DEFAULT (1), - `c` blob DEFAULT ("hello"), - `t` text DEFAULT (concat(a,b,c)) + `b` blob DEFAULT 1, + `c` blob DEFAULT "hello", + `t` text DEFAULT concat(a,b,c) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 insert into t1 (a) values (2); insert into t1 (a,b) values (10,"test1"); @@ -323,16 +323,17 @@ a > 0 drop table t1; create or replace table t1 (param_list int DEFAULT (1+1) NOT NULL); create or replace table t1 (param_list int DEFAULT 1+1 NOT NULL); +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 '+1 NOT NULL)' at line 1 create or replace table t1 (param_list blob DEFAULT "" NOT NULL); drop table t1; create table t1 (a int); insert into t1 values(-1); -alter table t1 add b int default 1, add c int default -1, add d int default 1+1, add e timestamp; +alter table t1 add b int default 1, add c int default -1, add d int default (1+1), add e timestamp; select a,b,c,d,e > 0 from t1; a b c d e > 0 -1 1 -1 2 1 insert into t1 values(10,10,10,10,0); -alter table t1 add f int default 1+1+1 null, add g int default 1+1+1+1 not null,add h int default (2+2+2+2); +alter table t1 add f int default (1+1+1) null, add g int default (1+1+1+1) not null,add h int default (2+2+2+2); select a,b,c,d,e > 0,f,g,h from t1; a b c d e > 0 f g h -1 1 -1 2 1 3 4 8 @@ -343,10 +344,10 @@ t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT '1', `c` int(11) DEFAULT '-1', - `d` int(11) DEFAULT 1+1, + `d` int(11) DEFAULT (1+1), `e` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `f` int(11) DEFAULT 1+1+1, - `g` int(11) NOT NULL DEFAULT 1+1+1+1, + `f` int(11) DEFAULT (1+1+1), + `g` int(11) NOT NULL DEFAULT (1+1+1+1), `h` int(11) DEFAULT (2+2+2+2) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 create table t2 like t1; @@ -356,10 +357,10 @@ t2 CREATE TABLE `t2` ( `a` int(11) DEFAULT NULL, `b` int(11) DEFAULT '1', `c` int(11) DEFAULT '-1', - `d` int(11) DEFAULT 1+1, + `d` int(11) DEFAULT (1+1), `e` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - `f` int(11) DEFAULT 1+1+1, - `g` int(11) NOT NULL DEFAULT 1+1+1+1, + `f` int(11) DEFAULT (1+1+1), + `g` int(11) NOT NULL DEFAULT (1+1+1+1), `h` int(11) DEFAULT (2+2+2+2) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 insert into t2 (a) values (100); @@ -367,18 +368,21 @@ select a,b,c,d,e > 0,f,g,h from t2; a b c d e > 0 f g h 100 1 -1 2 1 3 4 8 drop table t1,t2; -create table t1 (a int default 1----1); +create table t1 (a int default (1----1), b int default - 1, c int default +1, e int default (--1)); show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT 1----1 + `a` int(11) DEFAULT (1----1), + `b` int(11) DEFAULT '-1', + `c` int(11) DEFAULT '1', + `e` int(11) DEFAULT '1' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 insert into t1 values(); insert into t1 values(); select * from t1; -a -2 -2 +a b c e +2 -1 1 1 +2 -1 1 1 drop table t1; create or replace can delete a table on error @@ -395,15 +399,15 @@ create or replace table t1 (a int default 1, b int default a); create or replace table t1 (a int default 1, b int as (a)); create or replace table t1 (a int default b, b int default 1); create or replace table t1 (a int as (b), b int default 1); -create or replace table t1 (a int as (b), b int default 1+1); +create or replace table t1 (a int as (b), b int default (1+1)); create or replace table t1 (a int default 1, b int as (c), c int default (a+1)); -create or replace table t1 (a int default 1+1, b int as (c), c int default (a+1)); +create or replace table t1 (a int default (1+1), b int as (c), c int default (a+1)); create or replace table t1 (a VARCHAR(128) DEFAULT @@version); create or replace table t1 (a int not null, b int as (a)); -create or replace table t1 (a int not null, b int default a+1); +create or replace table t1 (a int not null, b int default (a+1)); create or replace table t1 (a int default a); ERROR 01000: Expression for field `a` is refering to uninitialized field `a` -create or replace table t1 (a int default b, b int default 1+1); +create or replace table t1 (a int default b, b int default (1+1)); ERROR 01000: Expression for field `a` is refering to uninitialized field `b` create or replace table t1 (a int default 1, b int as (c), c int as (a+1)); ERROR 01000: Expression for field `b` is refering to uninitialized field `c` @@ -446,16 +450,22 @@ Error handling create or replace table t1 (a bigint default xxx()); ERROR HY000: Function or expression '`xxx`' is not allowed for 'DEFAULT' of column/constraint 'a' create or replace table t1 (a bigint default (select (1))); -ERROR HY000: Function or expression 'subselect' is not allowed for 'DEFAULT' of column/constraint 'a' -create or replace table t1 (a bigint default (1,2,3))); +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 'select (1)))' at line 1 +create or replace table t1 (a bigint default (1,2,3)); +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 '2,3))' at line 1 +create or replace table t1 (a bigint default ((1,2,3))); ERROR 21000: Operand should contain 1 column(s) +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a DIV b); +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 'DIV b)' at line 1 +CREATE TABLE t1 (a INT, b INT DEFAULT -a); +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 'a)' at line 1 drop table if exists t1; Warnings: Note 1051 Unknown table 'test.t1' # # Invalid DEFAULT expressions # -CREATE TABLE t1 (a INT DEFAULT (SELECT 1)); +CREATE TABLE t1 (a INT DEFAULT ((SELECT 1))); ERROR HY000: Function or expression 'subselect' is not allowed for 'DEFAULT' of column/constraint 'a' CREATE TABLE t1 (a INT DEFAULT (EXISTS (SELECT 1))); ERROR HY000: Function or expression 'subselect' is not allowed for 'DEFAULT' of column/constraint 'a' @@ -463,14 +473,12 @@ CREATE TABLE t1 (a INT DEFAULT (1=ANY (SELECT 1))); ERROR HY000: Function or expression 'subselect' is not allowed for 'DEFAULT' of column/constraint 'a' CREATE TABLE t1 (a INT DEFAULT ROW(1,1)); ERROR 21000: Operand should contain 1 column(s) -CREATE TABLE t1 (a INT DEFAULT (1,1)); -ERROR 21000: Operand should contain 1 column(s) CREATE TABLE t1 (a INT DEFAULT ((1,1))); ERROR 21000: Operand should contain 1 column(s) CREATE TABLE t1 (a INT DEFAULT ?); -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 '?)' at line 1 +Got one of the listed errors CREATE TABLE t1 (a INT DEFAULT(?)); -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 '?))' at line 1 +Got one of the listed errors CREATE TABLE t1 (a INT DEFAULT (b), b INT DEFAULT(a)); ERROR 01000: Expression for field `a` is refering to uninitialized field `b` CREATE TABLE t1 (a INT DEFAULT @v); @@ -546,7 +554,7 @@ DEALLOCATE PREPARE stmt; PREPARE stmt FROM 'CREATE TABLE t1 (a INT DEFAULT(?+?))'; set @a=1; execute stmt using @a,@a; -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 '?+?))' at line 1 +ERROR 42000: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near '?+?)' at line 1 DEALLOCATE PREPARE stmt; # # Parenthesized Item_basic_constant @@ -920,7 +928,7 @@ SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT NULL, - `b` int(11) DEFAULT (a) + `b` int(11) DEFAULT a ) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (1, 1); INSERT INTO t1 VALUES (DEFAULT, DEFAULT); @@ -940,7 +948,7 @@ CREATE TABLE t1 (a INT DEFAULT(DEFAULT(b)), b INT DEFAULT 1); SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT (DEFAULT(b)), + `a` int(11) DEFAULT DEFAULT(b), `b` int(11) DEFAULT '1' ) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (DEFAULT, DEFAULT); @@ -953,7 +961,7 @@ SHOW CREATE TABLE t1; Table Create Table t1 CREATE TABLE `t1` ( `a` int(11) DEFAULT '1', - `b` int(11) DEFAULT (DEFAULT(a)) + `b` int(11) DEFAULT DEFAULT(a) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (DEFAULT, DEFAULT); SELECT * FROM t1; @@ -1135,7 +1143,7 @@ DROP TABLE t1; # # Check DEFAULT() function # -CREATE TABLE `t1` (`a` int(11) DEFAULT 3+3,`b` int(11) DEFAULT '1000'); +CREATE TABLE `t1` (`a` int(11) DEFAULT (3+3),`b` int(11) DEFAULT '1000'); insert into t1 values (1,1),(2,2); insert into t1 values (default,default); select * from t1; @@ -1210,7 +1218,7 @@ SELECT * FROM t1; a b 34 3 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a DIV b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a DIV b)); INSERT INTO t1 (a, b) VALUES (13, 3); SELECT * FROM t1; a b c @@ -1268,6 +1276,8 @@ CREATE TABLE t1 (a INT DEFAULT MASTER_POS_WAIT('test',100)); ERROR HY000: Function or expression 'master_pos_wait' is not allowed for 'DEFAULT' of column/constraint 'a' CREATE TABLE t1 (a INT DEFAULT MASTER_GTID_WAIT('test')); ERROR HY000: Function or expression 'master_gtid_wait' is not allowed for 'DEFAULT' of column/constraint 'a' +CREATE TABLE t1 (a VARCHAR(30), b DOUBLE DEFAULT MATCH (a) AGAINST('bbbb' IN BOOLEAN MODE)); +ERROR HY000: Function or expression 'match' is not allowed for 'DEFAULT' of column/constraint 'b' # # Temporal functions # @@ -1601,7 +1611,13 @@ a b c 0 1 2 1 1 1 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT -a); +CREATE TABLE t1 (a INT, b INT DEFAULT (-a)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT (-a) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (10, DEFAULT); SELECT * FROM t1; a b @@ -1621,13 +1637,22 @@ a b c d 1.5 2 1 2 -1.5 -1 -2 -2 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a+b, d INT DEFAULT a-b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a+b), d INT DEFAULT (a-b)); INSERT INTO t1 VALUES (2, 1, DEFAULT, DEFAULT); SELECT * FROM t1; a b c d 2 1 3 1 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a*b, d INT DEFAULT a/b, e INT DEFAULT a MOD b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a*b), d INT DEFAULT (a/b), e INT DEFAULT (a MOD b)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT (a*b), + `d` int(11) DEFAULT (a/b), + `e` int(11) DEFAULT (a MOD b) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 VALUES (7, 3, DEFAULT, DEFAULT, DEFAULT); SELECT * FROM t1; a b c d e @@ -1699,8 +1724,16 @@ CREATE TABLE t1 ( a VARCHAR(10) CHARACTER SET latin1, b VARCHAR(10) CHARACTER SET latin1 DEFAULT a COLLATE latin1_bin, c VARCHAR(10) CHARACTER SET utf8 DEFAULT CONVERT(a USING utf8), -d VARBINARY(10) DEFAULT BINARY(a) +d VARBINARY(10) DEFAULT (BINARY(a)) ); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` varchar(10) DEFAULT NULL, + `b` varchar(10) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT a, + `c` varchar(10) CHARACTER SET utf8 DEFAULT CONVERT(a USING utf8), + `d` varbinary(10) DEFAULT (BINARY(a)) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 (a) VALUES ('a'); SELECT * FROM t1; a b c d @@ -1715,31 +1748,39 @@ SELECT * FROM t1; a b 7 3 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a|b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a|b)); INSERT INTO t1 (a,b) VALUES (1,2); SELECT * FROM t1; a b c 1 2 3 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a&b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a&b)); INSERT INTO t1 (a,b) VALUES (5,4); SELECT * FROM t1; a b c 5 4 4 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a^b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a^b)); INSERT INTO t1 (a,b) VALUES (11,3); SELECT * FROM t1; a b c 11 3 8 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a&~b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a&~b)); INSERT INTO t1 (a,b) VALUES (5,1); SELECT * FROM t1; a b c 5 1 4 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a<>b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a<>b)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT NULL, + `c` int(11) DEFAULT (a<>b) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 (a,b) VALUES (5,1); SELECT * FROM t1; a b c d @@ -1918,7 +1959,13 @@ ERROR HY000: Function or expression 'load_file' is not allowed for 'DEFAULT' of # # Predicates # -CREATE TABLE t1 (a INT, b INT DEFAULT NOT a); +CREATE TABLE t1 (a INT, b INT DEFAULT (NOT a)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT (NOT a) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; a b @@ -1926,7 +1973,7 @@ NULL NULL 0 1 1 0 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, x INT DEFAULT a XOR b); +CREATE TABLE t1 (a INT, b INT, x INT DEFAULT (a XOR b)); INSERT INTO t1 (a,b) VALUES (0,0),(0,1),(1,0),(1,1); SELECT * FROM t1; a b x @@ -1935,7 +1982,14 @@ a b x 1 0 1 1 1 0 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS TRUE, c INT DEFAULT a IS NOT TRUE); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS TRUE), c INT DEFAULT (a IS NOT TRUE)); +SHOW CREATE TABLE t1; +Table Create Table +t1 CREATE TABLE `t1` ( + `a` int(11) DEFAULT NULL, + `b` int(11) DEFAULT (a IS TRUE), + `c` int(11) DEFAULT (a IS NOT TRUE) +) ENGINE=MyISAM DEFAULT CHARSET=latin1 INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; a b c @@ -1943,7 +1997,7 @@ NULL 0 1 0 0 1 1 1 0 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS FALSE, c INT DEFAULT a IS NOT FALSE); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS FALSE), c INT DEFAULT (a IS NOT FALSE)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; a b c @@ -1951,7 +2005,7 @@ NULL 0 1 0 1 0 1 0 1 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS NULL, c INT DEFAULT a IS NOT NULL); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS NULL), c INT DEFAULT (a IS NOT NULL)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; a b c @@ -1959,7 +2013,7 @@ NULL 1 0 0 0 1 1 0 1 DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS UNKNOWN, c INT DEFAULT a IS NOT UNKNOWN); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS UNKNOWN), c INT DEFAULT (a IS NOT UNKNOWN)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; a b c @@ -1968,10 +2022,10 @@ NULL 1 0 1 0 1 DROP TABLE t1; CREATE TABLE t1 (a INT, -eq INT DEFAULT a=0, equal INT DEFAULT a<=>0, -ne INT DEFAULT a<>0, -lt INT DEFAULT a<0, le INT DEFAULT a<=0, -gt INT DEFAULT a>0, ge INT DEFAULT a>=0); +eq INT DEFAULT (a=0), equal INT DEFAULT (a<=>0), +ne INT DEFAULT (a<>0), +lt INT DEFAULT (a<0), le INT DEFAULT (a<=0), +gt INT DEFAULT (a>0), ge INT DEFAULT (a>=0)); INSERT INTO t1 (a) VALUES (NULL),(-1),(0),(1); SELECT * FROM t1; a eq equal ne lt le gt ge @@ -1980,7 +2034,7 @@ NULL NULL 0 NULL NULL NULL NULL NULL 0 1 1 0 0 1 0 1 1 0 0 1 0 0 1 1 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a LIKE 'a%'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a LIKE 'a%')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'); SELECT * FROM t1; a b @@ -1988,7 +2042,7 @@ AAA 1 aaa 1 bbb 0 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a RLIKE 'a$'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a RLIKE 'a$')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'); SELECT * FROM t1; a b @@ -1996,7 +2050,7 @@ AAA 1 aaa 1 bbb 0 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a IN ('aaa','bbb')); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a IN ('aaa','bbb'))); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; a b @@ -2005,7 +2059,7 @@ aaa 1 bbb 1 ccc 0 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a NOT IN ('aaa','bbb')); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a NOT IN ('aaa','bbb'))); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; a b @@ -2014,7 +2068,7 @@ aaa 0 bbb 0 ccc 1 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a BETWEEN 'aaa' AND 'bbb'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a BETWEEN 'aaa' AND 'bbb')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; a b @@ -2023,7 +2077,7 @@ aaa 1 bbb 1 ccc 0 DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a NOT BETWEEN 'aaa' AND 'bbb'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a NOT BETWEEN 'aaa' AND 'bbb')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; a b @@ -2159,3 +2213,10 @@ SELECT c FROM t1; c test DROP TABLE t1; +# +# Collations +# +CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('ö')) CHARACTER SET koi8r COLLATE koi8r_bin; +ERROR 22007: Encountered illegal value 'ö' when converting to koi8r +CREATE OR REPLACE TABLE t1 (a char(2) default concat('A') COLLATE utf8mb4_unicode_ci); +DROP TABLE t1; diff --git a/mysql-test/suite/rpl/r/rpl_default.result b/mysql-test/suite/rpl/r/rpl_default.result index 5edd01a60b2..a1629b99bb3 100644 --- a/mysql-test/suite/rpl/r/rpl_default.result +++ b/mysql-test/suite/rpl/r/rpl_default.result @@ -1,14 +1,14 @@ include/master-slave.inc [connection master] connection master; -create table t1 (a int DEFAULT 1+1, b bigint default uuid_short(), u blob default user()); +create table t1 (a int DEFAULT (1+1), b bigint default uuid_short(), u blob default user()); insert into t1 (a) values(1); connection slave; connection slave; show create table t1; Table Create Table t1 CREATE TABLE `t1` ( - `a` int(11) DEFAULT 1+1, + `a` int(11) DEFAULT (1+1), `b` bigint(20) DEFAULT uuid_short(), `u` blob DEFAULT user() ) ENGINE=MyISAM DEFAULT CHARSET=latin1 diff --git a/mysql-test/suite/rpl/t/rpl_default.test b/mysql-test/suite/rpl/t/rpl_default.test index 03f24418f77..9adf15c93ac 100644 --- a/mysql-test/suite/rpl/t/rpl_default.test +++ b/mysql-test/suite/rpl/t/rpl_default.test @@ -9,7 +9,7 @@ connection master; -create table t1 (a int DEFAULT 1+1, b bigint default uuid_short(), u blob default user()); +create table t1 (a int DEFAULT (1+1), b bigint default uuid_short(), u blob default user()); insert into t1 (a) values(1); let $b=query_get_value(select * from t1, b, 1); diff --git a/mysql-test/t/default.test b/mysql-test/t/default.test index 7969016c2ee..f56b559991a 100644 --- a/mysql-test/t/default.test +++ b/mysql-test/t/default.test @@ -207,7 +207,7 @@ drop table t1; --echo Check default expressions --echo -create or replace table t1 (a int default 1, b int default a+1, c int default a+b) engine myisam; +create or replace table t1 (a int default 1, b int default (a+1), c int default (a+b)) engine myisam; show create table t1; insert into t1 values (); insert into t1 (a) values (2); @@ -238,16 +238,18 @@ select a > 0 from t1; drop table t1; create or replace table t1 (param_list int DEFAULT (1+1) NOT NULL); +--error ER_PARSE_ERROR create or replace table t1 (param_list int DEFAULT 1+1 NOT NULL); create or replace table t1 (param_list blob DEFAULT "" NOT NULL); + drop table t1; create table t1 (a int); insert into t1 values(-1); -alter table t1 add b int default 1, add c int default -1, add d int default 1+1, add e timestamp; +alter table t1 add b int default 1, add c int default -1, add d int default (1+1), add e timestamp; select a,b,c,d,e > 0 from t1; insert into t1 values(10,10,10,10,0); -alter table t1 add f int default 1+1+1 null, add g int default 1+1+1+1 not null,add h int default (2+2+2+2); +alter table t1 add f int default (1+1+1) null, add g int default (1+1+1+1) not null,add h int default (2+2+2+2); select a,b,c,d,e > 0,f,g,h from t1; show create table t1; @@ -257,7 +259,7 @@ insert into t2 (a) values (100); select a,b,c,d,e > 0,f,g,h from t2; drop table t1,t2; -create table t1 (a int default 1----1); +create table t1 (a int default (1----1), b int default - 1, c int default +1, e int default (--1)); show create table t1; insert into t1 values(); insert into t1 values(); @@ -281,18 +283,18 @@ create or replace table t1 (a int default 1, b int default a); create or replace table t1 (a int default 1, b int as (a)); create or replace table t1 (a int default b, b int default 1); create or replace table t1 (a int as (b), b int default 1); -create or replace table t1 (a int as (b), b int default 1+1); +create or replace table t1 (a int as (b), b int default (1+1)); create or replace table t1 (a int default 1, b int as (c), c int default (a+1)); -create or replace table t1 (a int default 1+1, b int as (c), c int default (a+1)); +create or replace table t1 (a int default (1+1), b int as (c), c int default (a+1)); create or replace table t1 (a VARCHAR(128) DEFAULT @@version); create or replace table t1 (a int not null, b int as (a)); -create or replace table t1 (a int not null, b int default a+1); +create or replace table t1 (a int not null, b int default (a+1)); --error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD create or replace table t1 (a int default a); --error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD -create or replace table t1 (a int default b, b int default 1+1); +create or replace table t1 (a int default b, b int default (1+1)); --error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD create or replace table t1 (a int default 1, b int as (c), c int as (a+1)); --error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD @@ -323,10 +325,16 @@ drop table t1; --error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED create or replace table t1 (a bigint default xxx()); ---error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED +--error ER_PARSE_ERROR create or replace table t1 (a bigint default (select (1))); +--error ER_PARSE_ERROR +create or replace table t1 (a bigint default (1,2,3)); --error ER_OPERAND_COLUMNS -create or replace table t1 (a bigint default (1,2,3))); +create or replace table t1 (a bigint default ((1,2,3))); +--error ER_PARSE_ERROR +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a DIV b); +--error ER_PARSE_ERROR +CREATE TABLE t1 (a INT, b INT DEFAULT -a); # Cleanup drop table if exists t1; @@ -336,7 +344,7 @@ drop table if exists t1; --echo # --error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED -CREATE TABLE t1 (a INT DEFAULT (SELECT 1)); +CREATE TABLE t1 (a INT DEFAULT ((SELECT 1))); --error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED CREATE TABLE t1 (a INT DEFAULT (EXISTS (SELECT 1))); @@ -347,15 +355,12 @@ CREATE TABLE t1 (a INT DEFAULT (1=ANY (SELECT 1))); --error ER_OPERAND_COLUMNS CREATE TABLE t1 (a INT DEFAULT ROW(1,1)); ---error ER_OPERAND_COLUMNS -CREATE TABLE t1 (a INT DEFAULT (1,1)); - --error ER_OPERAND_COLUMNS CREATE TABLE t1 (a INT DEFAULT ((1,1))); ---error ER_PARSE_ERROR +--error ER_PARSE_ERROR,2031 CREATE TABLE t1 (a INT DEFAULT ?); ---error ER_PARSE_ERROR +--error ER_PARSE_ERROR,2031 CREATE TABLE t1 (a INT DEFAULT(?)); --error ER_EXPRESSION_REFERS_TO_UNINIT_FIELD @@ -864,7 +869,7 @@ DROP TABLE t1; --echo # Check DEFAULT() function --echo # -CREATE TABLE `t1` (`a` int(11) DEFAULT 3+3,`b` int(11) DEFAULT '1000'); +CREATE TABLE `t1` (`a` int(11) DEFAULT (3+3),`b` int(11) DEFAULT '1000'); insert into t1 values (1,1),(2,2); insert into t1 values (default,default); select * from t1; @@ -919,12 +924,6 @@ INSERT INTO t1 VALUES (PI(), DEFAULT, DEFAULT); SELECT * FROM t1; DROP TABLE t1; -# QQ: this sets "b" to (-1), which looks wrong -#CREATE TABLE t1 (a VARCHAR(30), b DOUBLE DEFAULT MATCH (a) AGAINST('bbbb' IN BOOLEAN MODE)); -#INSERT INTO t1 VALUES ('aaaa bbbb cccc dddd', DEFAULT); -#SELECT * FROM t1; -#DROP TABLE t1; - --echo # --echo # INT result functions --echo # @@ -934,7 +933,7 @@ INSERT INTO t1 (a) VALUES (34); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a DIV b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a DIV b)); INSERT INTO t1 (a, b) VALUES (13, 3); SELECT * FROM t1; DROP TABLE t1; @@ -994,6 +993,9 @@ CREATE TABLE t1 (a INT DEFAULT MASTER_POS_WAIT('test',100)); --error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED CREATE TABLE t1 (a INT DEFAULT MASTER_GTID_WAIT('test')); +--error ER_VIRTUAL_COLUMN_FUNCTION_IS_NOT_ALLOWED +CREATE TABLE t1 (a VARCHAR(30), b DOUBLE DEFAULT MATCH (a) AGAINST('bbbb' IN BOOLEAN MODE)); + --echo # --echo # Temporal functions --echo # @@ -1283,8 +1285,8 @@ INSERT INTO t1 VALUES (1, 1, DEFAULT); SELECT * FROM t1; DROP TABLE t1; - -CREATE TABLE t1 (a INT, b INT DEFAULT -a); +CREATE TABLE t1 (a INT, b INT DEFAULT (-a)); +SHOW CREATE TABLE t1; INSERT INTO t1 VALUES (10, DEFAULT); SELECT * FROM t1; DROP TABLE t1; @@ -1300,12 +1302,13 @@ INSERT INTO t1 VALUES (-1.5, DEFAULT, DEFAULT, DEFAULT); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a+b, d INT DEFAULT a-b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a+b), d INT DEFAULT (a-b)); INSERT INTO t1 VALUES (2, 1, DEFAULT, DEFAULT); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a*b, d INT DEFAULT a/b, e INT DEFAULT a MOD b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a*b), d INT DEFAULT (a/b), e INT DEFAULT (a MOD b)); +SHOW CREATE TABLE t1; INSERT INTO t1 VALUES (7, 3, DEFAULT, DEFAULT, DEFAULT); SELECT * FROM t1; DROP TABLE t1; @@ -1362,8 +1365,9 @@ CREATE TABLE t1 ( a VARCHAR(10) CHARACTER SET latin1, b VARCHAR(10) CHARACTER SET latin1 DEFAULT a COLLATE latin1_bin, c VARCHAR(10) CHARACTER SET utf8 DEFAULT CONVERT(a USING utf8), - d VARBINARY(10) DEFAULT BINARY(a) + d VARBINARY(10) DEFAULT (BINARY(a)) ); +SHOW CREATE TABLE t1; INSERT INTO t1 (a) VALUES ('a'); SELECT * FROM t1; DROP TABLE t1; @@ -1377,23 +1381,24 @@ CREATE TABLE t1 (a INT, b INT DEFAULT BIT_COUNT(a)); INSERT INTO t1 (a) VALUES (7); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a|b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a|b)); INSERT INTO t1 (a,b) VALUES (1,2); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a&b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a&b)); INSERT INTO t1 (a,b) VALUES (5,4); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a^b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a^b)); INSERT INTO t1 (a,b) VALUES (11,3); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a&~b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a&~b)); INSERT INTO t1 (a,b) VALUES (5,1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, c INT DEFAULT a<>b); +CREATE TABLE t1 (a INT, b INT, c INT DEFAULT (a<>b)); +SHOW CREATE TABLE t1; INSERT INTO t1 (a,b) VALUES (5,1); SELECT * FROM t1; DROP TABLE t1; @@ -1543,71 +1548,73 @@ CREATE TABLE t1 (a VARCHAR(30), b BLOB DEFAULT LOAD_FILE(a)); --echo # Predicates --echo # -CREATE TABLE t1 (a INT, b INT DEFAULT NOT a); +CREATE TABLE t1 (a INT, b INT DEFAULT (NOT a)); +SHOW CREATE TABLE t1; INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT, x INT DEFAULT a XOR b); +CREATE TABLE t1 (a INT, b INT, x INT DEFAULT (a XOR b)); INSERT INTO t1 (a,b) VALUES (0,0),(0,1),(1,0),(1,1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS TRUE, c INT DEFAULT a IS NOT TRUE); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS TRUE), c INT DEFAULT (a IS NOT TRUE)); +SHOW CREATE TABLE t1; INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS FALSE, c INT DEFAULT a IS NOT FALSE); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS FALSE), c INT DEFAULT (a IS NOT FALSE)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS NULL, c INT DEFAULT a IS NOT NULL); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS NULL), c INT DEFAULT (a IS NOT NULL)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a INT, b INT DEFAULT a IS UNKNOWN, c INT DEFAULT a IS NOT UNKNOWN); +CREATE TABLE t1 (a INT, b INT DEFAULT (a IS UNKNOWN), c INT DEFAULT (a IS NOT UNKNOWN)); INSERT INTO t1 (a) VALUES (NULL),(0),(1); SELECT * FROM t1; DROP TABLE t1; CREATE TABLE t1 (a INT, - eq INT DEFAULT a=0, equal INT DEFAULT a<=>0, - ne INT DEFAULT a<>0, - lt INT DEFAULT a<0, le INT DEFAULT a<=0, - gt INT DEFAULT a>0, ge INT DEFAULT a>=0); + eq INT DEFAULT (a=0), equal INT DEFAULT (a<=>0), + ne INT DEFAULT (a<>0), + lt INT DEFAULT (a<0), le INT DEFAULT (a<=0), + gt INT DEFAULT (a>0), ge INT DEFAULT (a>=0)); INSERT INTO t1 (a) VALUES (NULL),(-1),(0),(1); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a LIKE 'a%'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a LIKE 'a%')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a RLIKE 'a$'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a RLIKE 'a$')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a IN ('aaa','bbb')); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a IN ('aaa','bbb'))); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a NOT IN ('aaa','bbb')); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a NOT IN ('aaa','bbb'))); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a BETWEEN 'aaa' AND 'bbb'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a BETWEEN 'aaa' AND 'bbb')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; DROP TABLE t1; -CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT a NOT BETWEEN 'aaa' AND 'bbb'); +CREATE TABLE t1 (a VARCHAR(10), b INT DEFAULT (a NOT BETWEEN 'aaa' AND 'bbb')); INSERT INTO t1 (a) VALUES ('AAA'),('aaa'),('bbb'),('ccc'); SELECT * FROM t1; DROP TABLE t1; @@ -1716,3 +1723,12 @@ CREATE TABLE t1 ( INSERT INTO t1 (a) VALUES ('test'); SELECT c FROM t1; DROP TABLE t1; + +--echo # +--echo # Collations +--echo # + +--error ER_BAD_DATA +CREATE TABLE t1 (a VARCHAR(20) CHARACTER SET latin1 DEFAULT CONCAT('ö')) CHARACTER SET koi8r COLLATE koi8r_bin; +CREATE OR REPLACE TABLE t1 (a char(2) default concat('A') COLLATE utf8mb4_unicode_ci); +DROP TABLE t1; diff --git a/sql/field.cc b/sql/field.cc index 8ad4dbb876e..8b315d38ddc 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -10575,6 +10575,7 @@ Column_definition::Column_definition(THD *thd, Field *old_field, default_value->expr_str.length= res->length(); default_value->expr_item= new (thd->mem_root) Item_string(thd, pos, res->length(), charset); + default_value->utf8= 0; } } } diff --git a/sql/field.h b/sql/field.h index a7e2af197f3..1f75d96a9de 100644 --- a/sql/field.h +++ b/sql/field.h @@ -395,6 +395,7 @@ enum Derivation /* The length of the header part for each virtual column in the .frm file */ #define FRM_VCOL_OLD_HEADER_SIZE(b) (3 + MY_TEST(b)) +#define FRM_VCOL_NEW_BASE_SIZE 16 #define FRM_VCOL_NEW_HEADER_SIZE 7 class Count_distinct_field; @@ -571,6 +572,7 @@ public: /* Flag indicating that the field is physically stored in the database */ bool stored_in_db; bool non_deterministic; + bool utf8; /* Already in utf8 */ /* The expression to compute the value of the virtual column */ Item *expr_item; /* Text representation of the defining expression */ @@ -580,7 +582,7 @@ public: Virtual_column_info() : field_type((enum enum_field_types)MYSQL_TYPE_VIRTUAL), in_partitioning_expr(FALSE), stored_in_db(FALSE), non_deterministic(FALSE), - expr_item(NULL) + utf8(TRUE), expr_item(NULL) { expr_str.str= name.str= NULL; name.length= 0; diff --git a/sql/handler.h b/sql/handler.h index ef8feacc8d3..6e16df99827 100644 --- a/sql/handler.h +++ b/sql/handler.h @@ -1649,7 +1649,7 @@ struct Table_scope_and_contents_source_st ulong avg_row_length; ulong used_fields; ulong key_block_size; - ulong expression_lengths; + ulong expression_length; ulong field_check_constraints; /* number of pages to sample during diff --git a/sql/item.h b/sql/item.h index 8aa64eb5f3b..0a526e09bed 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1377,6 +1377,8 @@ public: virtual void set_result_field(Field *field) {} virtual bool is_result_field() { return 0; } virtual bool is_bool_type() { return false; } + /* This is to handle printing of default values */ + virtual bool need_parentheses_in_default() { return false; } virtual void save_in_result_field(bool no_conversions) {} /* set value of aggregate function in case of no rows for grouping were found diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index 5789186dbe8..33e061072da 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -197,6 +197,7 @@ public: virtual CHARSET_INFO *compare_collation() const { return NULL; } void fix_length_and_dec() { decimals=0; max_length=1; } uint decimal_precision() const { return 1; } + bool need_parentheses_in_default() { return true; } }; @@ -1488,6 +1489,7 @@ public: CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } void cleanup(); Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); + bool need_parentheses_in_default() { return true; } }; /* diff --git a/sql/item_func.h b/sql/item_func.h index f975d8c2806..71b65103f04 100644 --- a/sql/item_func.h +++ b/sql/item_func.h @@ -572,6 +572,7 @@ class Item_num_op :public Item_func_numhybrid print_op(str, query_type); } void fix_length_and_dec(); + bool need_parentheses_in_default() { return true; } }; @@ -639,6 +640,7 @@ public: } virtual void print(String *str, enum_query_type query_type); uint decimal_precision() const { return args[0]->decimal_precision(); } + bool need_parentheses_in_default() { return true; } }; @@ -680,6 +682,7 @@ public: void fix_length_and_dec() {} const char *func_name() const { return "decimal_typecast"; } virtual void print(String *str, enum_query_type query_type); + bool need_parentheses_in_default() { return true; } }; @@ -697,6 +700,7 @@ public: void fix_length_and_dec() { maybe_null= 1; } const char *func_name() const { return "double_typecast"; } virtual void print(String *str, enum_query_type query_type); + bool need_parentheses_in_default() { return true; } }; @@ -780,6 +784,7 @@ public: bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *arg) { return FALSE;} + bool need_parentheses_in_default() { return true; } }; @@ -811,6 +816,7 @@ public: uint decimal_precision() const { return args[0]->decimal_precision(); } bool check_partition_func_processor(uchar *int_arg) {return FALSE;} bool check_vcol_func_processor(uchar *arg) { return FALSE;} + bool need_parentheses_in_default() { return true; } }; @@ -1256,6 +1262,7 @@ public: { print_op(str, query_type); } + bool need_parentheses_in_default() { return true; } }; class Item_func_bit_or :public Item_func_bit diff --git a/sql/item_strfunc.cc b/sql/item_strfunc.cc index 497a1b9a80b..b1d59c6170a 100644 --- a/sql/item_strfunc.cc +++ b/sql/item_strfunc.cc @@ -2342,6 +2342,18 @@ void Item_func_decode::crypto_transform(String *res) sql_crypt.decode((char*) res->ptr(),res->length()); } +Item *Item_func_sysconst::safe_charset_converter(THD *thd, + CHARSET_INFO *tocs) +{ + /* + In default, virtual functions or constraint expressions, the value + of a sysconst is not constant + */ + if (thd->in_stored_expression) + return Item_str_func::safe_charset_converter(thd, tocs); + return const_charset_converter(thd, tocs, true, fully_qualified_func_name()); +} + String *Item_func_database::val_str(String *str) { @@ -2366,11 +2378,14 @@ String *Item_func_database::val_str(String *str) bool Item_func_user::init(THD *thd, const char *user, const char *host) { + DBUG_ENTER("Item_func_user::init"); DBUG_ASSERT(fixed == 1); /* Check if we have already calculated the value for this thread */ if (thd->query_id == last_query_id) - return FALSE; + DBUG_RETURN(FALSE); + DBUG_PRINT("enter", ("user: '%s' host: '%s'", user,host)); + last_query_id= thd->query_id; null_value= 0; @@ -2380,28 +2395,29 @@ bool Item_func_user::init(THD *thd, const char *user, const char *host) CHARSET_INFO *cs= system_charset_info; size_t res_length= (strlen(user)+strlen(host)+2) * cs->mbmaxlen; - if (str_value.alloc((uint) res_length)) + if (cached_value.alloc((uint) res_length)) { null_value=1; - return TRUE; + DBUG_RETURN(TRUE); } - str_value.set_charset(cs); - res_length=cs->cset->snprintf(cs, (char*)str_value.ptr(), (uint) res_length, + cached_value.set_charset(cs); + res_length=cs->cset->snprintf(cs, (char*)cached_value.ptr(), + (uint) res_length, "%s@%s", user, host); - str_value.length((uint) res_length); - str_value.mark_as_const(); + cached_value.length((uint) res_length); + cached_value.mark_as_const(); } else - str_value.set("", 0, system_charset_info); - return FALSE; + cached_value.set("", 0, system_charset_info); + DBUG_RETURN(FALSE); } String *Item_func_user::val_str(String *str) { THD *thd= current_thd; init(thd, thd->main_security_ctx.user, thd->main_security_ctx.host_or_ip); - return null_value ? 0 : &str_value; + return null_value ? 0 : &cached_value; } String *Item_func_current_user::val_str(String *str) @@ -2410,7 +2426,7 @@ String *Item_func_current_user::val_str(String *str) Security_context *ctx= (context->security_ctx ? context->security_ctx : thd->security_ctx); init(thd, ctx->priv_user, ctx->priv_host); - return null_value ? 0 : &str_value; + return null_value ? 0 : &cached_value; } diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index aa7fabbdb21..e484e41d300 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -562,10 +562,7 @@ class Item_func_sysconst :public Item_str_func public: Item_func_sysconst(THD *thd): Item_str_func(thd) { collation.set(system_charset_info,DERIVATION_SYSCONST); } - Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs) - { - return const_charset_converter(thd, tocs, true, fully_qualified_func_name()); - } + Item *safe_charset_converter(THD *thd, CHARSET_INFO *tocs); /* Used to create correct Item name in new converted item in safe_charset_converter, return string representation of this function @@ -599,6 +596,7 @@ class Item_func_user :public Item_func_sysconst { protected: query_id_t last_query_id; + String cached_value; bool init(THD *thd, const char *user, const char *host); public: @@ -906,6 +904,7 @@ public: } virtual void print(String *str, enum_query_type query_type); const char *func_name() const { return "cast_as_binary"; } + bool need_parentheses_in_default() { return true; } }; @@ -1058,6 +1057,7 @@ public: /* this function is transparent for view updating */ return args[0]->field_for_view_update(); } + bool need_parentheses_in_default() { return true; } }; diff --git a/sql/item_timefunc.h b/sql/item_timefunc.h index ee6394dac37..e0624688f05 100644 --- a/sql/item_timefunc.h +++ b/sql/item_timefunc.h @@ -854,6 +854,7 @@ public: bool get_date(MYSQL_TIME *res, ulonglong fuzzy_date); bool eq(const Item *item, bool binary_cmp) const; void print(String *str, enum_query_type query_type); + bool need_parentheses_in_default() { return true; } }; @@ -931,6 +932,7 @@ public: String *val_str(String *a); void fix_length_and_dec(); void print(String *str, enum_query_type query_type); + bool need_parentheses_in_default() { return true; } }; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index f216845ad10..251f3eadfd8 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -870,7 +870,7 @@ THD::THD(my_thread_id id, bool is_wsrep_applier) is_fatal_sub_stmt_error(false), rand_used(0), time_zone_used(0), - in_lock_tables(0), + in_lock_tables(0), in_stored_expression(0), bootstrap(0), derived_tables_processing(FALSE), waiting_on_group_commit(FALSE), has_waiter(FALSE), @@ -1417,7 +1417,7 @@ void THD::init(void) TL_WRITE); tx_isolation= (enum_tx_isolation) variables.tx_isolation; tx_read_only= variables.tx_read_only; - update_charset(); + update_charset(); // plugin_thd_var() changed character sets reset_current_stmt_binlog_format_row(); reset_binlog_local_stmt_filter(); set_status_var_init(); @@ -2311,12 +2311,19 @@ bool THD::convert_string(LEX_STRING *to, CHARSET_INFO *to_cs, { DBUG_ENTER("THD::convert_string"); size_t new_length= to_cs->mbmaxlen * from_length; - uint dummy_errors; + uint errors; if (alloc_lex_string(to, new_length + 1)) DBUG_RETURN(true); // EOM to->length= copy_and_convert((char*) to->str, new_length, to_cs, - from, from_length, from_cs, &dummy_errors); + from, from_length, from_cs, &errors); to->str[to->length]= 0; // Safety + if (errors && in_stored_expression) + { + my_error(ER_BAD_DATA, MYF(0), + ErrConvString(from, from_length, from_cs).ptr(), + to_cs->csname); + DBUG_RETURN(true); + } DBUG_RETURN(false); } diff --git a/sql/sql_class.h b/sql/sql_class.h index 20af4ea243a..31390866533 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -2840,7 +2840,7 @@ public: bool query_start_sec_part_used; /* for IS NULL => = last_insert_id() fix in remove_eq_conds() */ bool substitute_null_with_insert_id; - bool in_lock_tables; + bool in_lock_tables, in_stored_expression; bool bootstrap, cleanup_done, free_connection_done; /** is set if some thread specific value(s) used in a statement. */ diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc index 5d8caf6d2ab..e30f1eeeaaa 100644 --- a/sql/sql_lex.cc +++ b/sql/sql_lex.cc @@ -1286,7 +1286,7 @@ int MYSQLlex(YYSTYPE *yylval, THD *thd) { const char *cpp_tok_start, *m_tok_end, *tok_start; /* - To be able to handle "DEFAULT 1 NOT NULL" which classes with + To be able to handle "DEFAULT 1 NOT NULL" which clashes with "DEFAULT 1 NOT IN (..)" we must combine NOT NULL to one lex token NOT_NULL_SYM. We also have to ensure that NOT NULL IS are still separate tokens to ensure that NOT NULL IS TRUE is evaluated as diff --git a/sql/sql_lex.h b/sql/sql_lex.h index dad31620804..bca650105a9 100644 --- a/sql/sql_lex.h +++ b/sql/sql_lex.h @@ -2077,12 +2077,6 @@ public: return m_tok_start; } - /** Get the token start position, in the pre-processed buffer. */ - const char *get_cpp_tok_start() - { - return m_cpp_tok_start; - } - void set_cpp_tok_start(const char *pos) { m_cpp_tok_start= pos; @@ -2094,12 +2088,6 @@ public: return m_tok_end; } - /** Get the token end position, in the pre-processed buffer. */ - const char *get_cpp_tok_end() - { - return m_cpp_tok_end; - } - /** Get the previous token start position, in the raw buffer. */ const char *get_tok_start_prev() { @@ -2112,12 +2100,6 @@ public: return m_ptr; } - /** Get the current stream pointer, in the pre-processed buffer. */ - const char *get_cpp_ptr() - { - return m_cpp_ptr; - } - /** Get the length of the current token, in the raw buffer. */ uint yyLength() { @@ -2129,6 +2111,30 @@ public: return (uint) ((m_ptr - m_tok_start) - 1); } + /** Get the previus token start position, in the pre-processed buffer. */ + const char *get_cpp_start_prev() + { + return m_cpp_tok_start_prev; + } + + /** Get the token start position, in the pre-processed buffer. */ + const char *get_cpp_tok_start() + { + return m_cpp_tok_start; + } + + /** Get the token end position, in the pre-processed buffer. */ + const char *get_cpp_tok_end() + { + return m_cpp_tok_end; + } + + /** Get the current stream pointer, in the pre-processed buffer. */ + const char *get_cpp_ptr() + { + return m_cpp_ptr; + } + /** Get the utf8-body string. */ const char *get_body_utf8_str() { diff --git a/sql/sql_show.cc b/sql/sql_show.cc index 2cc57f0ea8c..5176bcbbc00 100644 --- a/sql/sql_show.cc +++ b/sql/sql_show.cc @@ -1636,9 +1636,18 @@ static bool get_field_default_value(THD *thd, Field *field, String *def_value, { if (field->default_value) { - def_value->set(field->default_value->expr_str.str, - field->default_value->expr_str.length, - system_charset_info); + if (field->default_value->expr_item->need_parentheses_in_default()) + { + def_value->set_charset(&my_charset_utf8mb4_general_ci); + def_value->append('('); + def_value->append(field->default_value->expr_str.str, + field->default_value->expr_str.length); + def_value->append(')'); + } + else + def_value->set(field->default_value->expr_str.str, + field->default_value->expr_str.length, + &my_charset_utf8mb4_general_ci); } else if (has_now_default) { @@ -1892,7 +1901,7 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, packet->append(STRING_WITH_LEN(" AS (")); packet->append(field->vcol_info->expr_str.str, field->vcol_info->expr_str.length, - system_charset_info); + &my_charset_utf8mb4_general_ci); packet->append(STRING_WITH_LEN(")")); if (field->vcol_info->stored_in_db) packet->append(STRING_WITH_LEN(" PERSISTENT")); @@ -1933,7 +1942,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, { packet->append(STRING_WITH_LEN(" CHECK (")); packet->append(field->check_constraint->expr_str.str, - field->check_constraint->expr_str.length); + field->check_constraint->expr_str.length, + &my_charset_utf8mb4_general_ci); packet->append(STRING_WITH_LEN(")")); } @@ -2041,7 +2051,8 @@ int show_create_table(THD *thd, TABLE_LIST *table_list, String *packet, } packet->append(STRING_WITH_LEN(" CHECK (")); packet->append(check->expr_str.str, - check->expr_str.length); + check->expr_str.length, + &my_charset_utf8mb4_general_ci); packet->append(STRING_WITH_LEN(")")); } } diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 42100d6b4cf..3d0cbfb4c16 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -936,7 +936,7 @@ bool LEX::set_bincmp(CHARSET_INFO *cs, bool bin) MYSQL_YYABORT; \ } while(0) -Virtual_column_info *add_virtual_expression(THD *thd, char *txt, +Virtual_column_info *add_virtual_expression(THD *thd, const char *txt, size_t size, Item *expr) { CHARSET_INFO *cs= thd->charset(); @@ -958,6 +958,7 @@ Virtual_column_info *add_virtual_expression(THD *thd, char *txt, v->expr_str.str= (char* ) thd->strmake(txt, size); v->expr_str.length= size; v->expr_item= expr; + v->utf8= 0; /* connection charset */ return v; } @@ -1854,9 +1855,9 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); simple_ident expr opt_expr opt_else sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr - table_wild simple_expr udf_expr + table_wild simple_expr column_default_non_parenthesized_expr udf_expr expr_or_default set_expr_or_default - geometry_function + geometry_function signed_literal opt_escape sp_opt_default simple_ident_nospvar simple_ident_q @@ -1957,6 +1958,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type select_outvar %type opt_check_constraint check_constraint virtual_column_func + column_default_expr %type analyze_stmt_command @@ -2021,7 +2023,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); definer_opt no_definer definer get_diagnostics parse_vcol_expr vcol_opt_specifier vcol_opt_attribute vcol_opt_attribute_list vcol_attribute - explainable_command + explainable_command opt_impossible_action END_OF_INPUT %type call sp_proc_stmts sp_proc_stmts1 sp_proc_stmt @@ -6420,6 +6422,26 @@ virtual_column_func: } ; +column_default_expr: + '(' virtual_column_func ')' { $$= $2; } + | remember_name column_default_non_parenthesized_expr opt_impossible_action remember_end + { + if (!($$= add_virtual_expression(thd, $1, (uint) ($4- $1), $2))) + MYSQL_YYABORT; + } + | signed_literal + { + if (!($$= add_virtual_expression(thd, "literal", 6, $1))) + MYSQL_YYABORT; + } + ; + +/* This is to force remember_end to look at next token */ +opt_impossible_action: + IMPOSSIBLE_ACTION {} + | /* empty */ {} + + field_type: int_type opt_field_length field_options { $$.set($1, $2); } | real_type opt_precision field_options { $$.set($1, $2); } @@ -6713,7 +6735,7 @@ opt_attribute_list: attribute: NULL_SYM { Lex->last_field->flags&= ~ NOT_NULL_FLAG; } | NOT_NULL_SYM { Lex->last_field->flags|= NOT_NULL_FLAG; } - | DEFAULT virtual_column_func { Lex->last_field->default_value= $2; } + | DEFAULT column_default_expr { Lex->last_field->default_value= $2; } | ON UPDATE_SYM NOW_SYM opt_default_time_precision { Item *item= new (thd->mem_root) Item_func_now_local(thd, $4); @@ -9490,34 +9512,17 @@ dyncall_create_list: } ; -simple_expr: +column_default_non_parenthesized_expr: simple_ident | function_call_keyword | function_call_nonkeyword | function_call_generic | function_call_conflict - | simple_expr COLLATE_SYM ident_or_text %prec NEG - { - Item *i1= new (thd->mem_root) Item_string(thd, $3.str, - $3.length, - thd->charset()); - if (i1 == NULL) - MYSQL_YYABORT; - $$= new (thd->mem_root) Item_func_set_collation(thd, $1, i1); - if ($$ == NULL) - MYSQL_YYABORT; - } | literal | param_marker { $$= $1; } | variable | sum_expr | window_func_expr - | simple_expr OR_OR_SYM simple_expr - { - $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); - if ($$ == NULL) - MYSQL_YYABORT; - } | NOT_NULL_SYM { /* Replace NOT NULL with NULL */ @@ -9525,43 +9530,6 @@ simple_expr: if ($$ == NULL) MYSQL_YYABORT; } - | '+' simple_expr %prec NEG - { - $$= $2; - } - | '-' simple_expr %prec NEG - { - $$= $2->neg(thd); - if ($$ == NULL) - MYSQL_YYABORT; - } - | '~' simple_expr %prec NEG - { - $$= new (thd->mem_root) Item_func_bit_neg(thd, $2); - if ($$ == NULL) - MYSQL_YYABORT; - } - | not2 simple_expr %prec NEG - { - $$= negate_expression(thd, $2); - if ($$ == NULL) - MYSQL_YYABORT; - } - | '(' subselect ')' - { - $$= new (thd->mem_root) Item_singlerow_subselect(thd, $2); - if ($$ == NULL) - MYSQL_YYABORT; - } - | '(' expr ')' - { $$= $2; } - | '(' expr ',' expr_list ')' - { - $4->push_front($2, thd->mem_root); - $$= new (thd->mem_root) Item_row(thd, *$4); - if ($$ == NULL) - MYSQL_YYABORT; - } | ROW_SYM '(' expr ',' expr_list ')' { $5->push_front($3, thd->mem_root); @@ -9609,13 +9577,6 @@ simple_expr: Select->add_ftfunc_to_list(thd, i1); $$= i1; } - | BINARY simple_expr %prec NEG - { - $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL, - &my_charset_bin); - if ($$ == NULL) - MYSQL_YYABORT; - } | CAST_SYM '(' expr AS cast_type ')' { LEX *lex= Lex; @@ -9664,6 +9625,71 @@ simple_expr: if ($$ == NULL) MYSQL_YYABORT; } + ; + +simple_expr: + column_default_non_parenthesized_expr + | simple_expr COLLATE_SYM ident_or_text %prec NEG + { + Item *i1= new (thd->mem_root) Item_string(thd, $3.str, + $3.length, + thd->charset()); + if (i1 == NULL) + MYSQL_YYABORT; + $$= new (thd->mem_root) Item_func_set_collation(thd, $1, i1); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '(' subselect ')' + { + $$= new (thd->mem_root) Item_singlerow_subselect(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '(' expr ')' + { $$= $2; } + | '(' expr ',' expr_list ')' + { + $4->push_front($2, thd->mem_root); + $$= new (thd->mem_root) Item_row(thd, *$4); + if ($$ == NULL) + MYSQL_YYABORT; + } + | BINARY simple_expr %prec NEG + { + $$= create_func_cast(thd, $2, ITEM_CAST_CHAR, NULL, NULL, + &my_charset_bin); + if ($$ == NULL) + MYSQL_YYABORT; + } + | simple_expr OR_OR_SYM simple_expr + { + $$= new (thd->mem_root) Item_func_concat(thd, $1, $3); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '+' simple_expr %prec NEG + { + $$= $2; + } + | '-' simple_expr %prec NEG + { + $$= $2->neg(thd); + if ($$ == NULL) + MYSQL_YYABORT; + } + | '~' simple_expr %prec NEG + { + $$= new (thd->mem_root) Item_func_bit_neg(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } + | not2 simple_expr %prec NEG + { + $$= negate_expression(thd, $2); + if ($$ == NULL) + MYSQL_YYABORT; + } | INTERVAL_SYM expr interval '+' expr %prec INTERVAL_SYM /* we cannot put interval before - */ { @@ -14035,6 +14061,15 @@ param_marker: } ; +signed_literal: + '+' NUM_literal { $$ = $2; } + | '-' NUM_literal + { + $2->max_length++; + $$= $2->neg(thd); + } + ; + literal: text_literal { $$ = $1; } | NUM_literal { $$ = $1; } diff --git a/sql/table.cc b/sql/table.cc index ef30e2bf529..45d4ec1ab16 100644 --- a/sql/table.cc +++ b/sql/table.cc @@ -1653,6 +1653,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_info_length))) goto err; vcol_info->expr_str.length= vcol_info_length; + vcol_info->utf8= 0; vcol_screen_pos+= vcol_info_length + MYSQL57_GCOL_HEADER_SIZE;; share->virtual_fields++; vcol_info_length= 0; @@ -1692,6 +1693,7 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, if (opt_interval_id) interval_nr= (uint) vcol_screen_pos[3]; vcol_info->expr_str.length= vcol_expr_length; + vcol_info->utf8= 0; vcol_screen_pos+= vcol_info_length; share->virtual_fields++; } @@ -2158,12 +2160,16 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, /* Handle virtual expressions */ if (vcol_screen_length && share->frm_version >= FRM_VER_EXPRESSSIONS) { + uchar *vcol_screen_end= vcol_screen_pos + vcol_screen_length; + + /* Skip header */ + vcol_screen_pos+= FRM_VCOL_NEW_BASE_SIZE; + /* Read virtual columns, default values and check constraints See pack_expression() for how data is stored */ - for (uchar *vcol_screen_end= vcol_screen_pos + vcol_screen_length ; - vcol_screen_pos < vcol_screen_end ; ) + while (vcol_screen_pos < vcol_screen_end) { Virtual_column_info *vcol_info; uint field_nr= uint2korr(vcol_screen_pos); @@ -2193,8 +2199,11 @@ int TABLE_SHARE::init_from_binary_frm_image(THD *thd, bool write, vcol_info->name= name; /* The following can only be true for check_constraints */ - if (field_nr != UINT_MAX32) + if (field_nr != UINT_MAX16) + { + DBUG_ASSERT(field_nr < share->fields); reg_field= share->field[field_nr]; + } vcol_info->expr_str.str= expr; vcol_info->expr_str.length= expr_length; @@ -2554,8 +2563,11 @@ static bool fix_vcol_expr(THD *thd, thd->where= "virtual column function"; /* Fix fields referenced to by the virtual column function */ + thd->in_stored_expression= 1; if (!func_expr->fixed) error= func_expr->fix_fields(thd, &vcol->expr_item); + thd->in_stored_expression= 0; + if (unlikely(error)) { DBUG_PRINT("info", @@ -2659,7 +2671,7 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, { char *vcol_expr_str; int str_len; - CHARSET_INFO *old_character_set_client; + CHARSET_INFO *save_character_set_client, *save_collation; Query_arena *backup_stmt_arena_ptr; Query_arena backup_arena; Query_arena *vcol_arena= 0; @@ -2669,10 +2681,12 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, LEX_STRING *vcol_expr= &vcol->expr_str; LEX *old_lex= thd->lex; LEX lex; + bool error; DBUG_ENTER("unpack_vcol_info_from_frm"); DBUG_ASSERT(vcol_expr); - old_character_set_client= thd->variables.character_set_client; + save_character_set_client= thd->variables.character_set_client; + save_collation= thd->variables.collation_connection; backup_stmt_arena_ptr= thd->stmt_arena; /* @@ -2730,10 +2744,17 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, /* Step 3: Use the parser to build an Item object from vcol_expr_str. */ - if (parse_sql(thd, &parser_state, NULL)) + if (vcol->utf8) { - goto err; + thd->update_charset(&my_charset_utf8mb4_general_ci, + table->s->table_charset); } + thd->in_stored_expression= 1; + error= parse_sql(thd, &parser_state, NULL); + thd->in_stored_expression= 0; + if (error) + goto err; + /* mark if expression will be stored in the table. This is also used by fix_vcol_expr() to mark if we are using non deterministic functions. @@ -2741,6 +2762,7 @@ Virtual_column_info *unpack_vcol_info_from_frm(THD *thd, vcol_storage.vcol_info->stored_in_db= vcol->stored_in_db; vcol_storage.vcol_info->non_deterministic= vcol->non_deterministic; vcol_storage.vcol_info->name= vcol->name; + vcol_storage.vcol_info->utf8= vcol->utf8; /* Validate the Item tree. */ if (!fix_vcol_expr(thd, table, field, vcol_storage.vcol_info)) { @@ -2756,7 +2778,8 @@ end: if (vcol_arena) thd->restore_active_arena(vcol_arena, &backup_arena); end_lex_with_single_table(thd, table, old_lex); - thd->variables.character_set_client= old_character_set_client; + if (vcol->utf8) + thd->update_charset(save_character_set_client, save_collation); DBUG_RETURN(vcol_info); } @@ -3612,7 +3635,7 @@ void prepare_frm_header(THD *thd, uint reclength, uchar *fileinfo, /* header */ fileinfo[0]=(uchar) 254; fileinfo[1]= 1; - fileinfo[2]= (create_info->expression_lengths == 0 ? FRM_VER_TRUE_VARCHAR : + fileinfo[2]= (create_info->expression_length == 0 ? FRM_VER_TRUE_VARCHAR : FRM_VER_EXPRESSSIONS); DBUG_ASSERT(ha_storage_engine_is_enabled(create_info->db_type)); diff --git a/sql/unireg.cc b/sql/unireg.cc index 463822e52e8..33a54268da8 100644 --- a/sql/unireg.cc +++ b/sql/unireg.cc @@ -42,10 +42,12 @@ static uint pack_keys(uchar *,uint, KEY *, ulong); static bool pack_header(THD *, uchar *, List &, HA_CREATE_INFO *info, ulong, handler *); static uint get_interval_id(uint *,List &, Create_field *); -static bool pack_fields(uchar **, List &, ulong); +static bool pack_fields(uchar **, List &, HA_CREATE_INFO*, + ulong); static void pack_constraints(uchar **buff, List *constr); static size_t packed_fields_length(List &); -static size_t packed_constraints_length(List *constr); +static size_t packed_constraints_length(THD *thd, HA_CREATE_INFO* info, + List *constr); static bool make_empty_rec(THD *, uchar *, uint, List &, uint, ulong); /* @@ -108,7 +110,7 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, LEX_STRING str_db_type; uint reclength, key_info_length, i; ulong key_buff_length; - ulong filepos, data_offset, expr_length; + ulong filepos, data_offset; uint options_len; uint gis_extra2_len= 0; uchar fileinfo[FRM_HEADER_SIZE],forminfo[FRM_FORMINFO_SIZE]; @@ -130,12 +132,6 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, DBUG_RETURN(frm); reclength= uint2korr(forminfo+266); - expr_length= packed_constraints_length(create_info->constraint_list); - - /* Correct expression length stored by pack_header */ - expr_length+= uint2korr(forminfo+286); - int2store(forminfo+286, expr_length); - create_info->expression_lengths= expr_length; /* Calculate extra data segment length */ str_db_type= *hton_name(create_info->db_type); @@ -227,9 +223,10 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, filepos= frm.length; frm.length+= FRM_FORMINFO_SIZE; // forminfo frm.length+= packed_fields_length(create_fields); - frm.length+= expr_length; + frm.length+= create_info->expression_length; - if (frm.length > FRM_MAX_SIZE || expr_length > UINT_MAX32) + if (frm.length > FRM_MAX_SIZE || + create_info->expression_length > UINT_MAX32) { my_error(ER_TABLE_DEFINITION_TOO_BIG, MYF(0), table); DBUG_RETURN(frm); @@ -336,9 +333,8 @@ LEX_CUSTRING build_frm_image(THD *thd, const char *table, memcpy(frm_ptr + filepos, forminfo, 288); pos= frm_ptr + filepos + 288; - if (pack_fields(&pos, create_fields, data_offset)) + if (pack_fields(&pos, create_fields, create_info, data_offset)) goto err; - pack_constraints(&pos, create_info->constraint_list); { /* @@ -502,23 +498,57 @@ static uint pack_keys(uchar *keybuff, uint key_count, KEY *keyinfo, /** Calculate and check length of stored expression (virtual, def, check) + Convert string to utf8, if it isn't already + + @param thd Thread handler. Used for memory allocation @param v_col Virtual expression. Can be 0 + @param CREATE INFO For characterset @param length Sum total lengths here Note to make calls easier, one can call this with v_col == 0 + + @return 0 ok + @return 1 error (out of memory or wrong characters in expression) */ -static void add_expr_length(Virtual_column_info *v_col, size_t *length) +static bool add_expr_length(THD *thd, Virtual_column_info **v_col_ptr, + HA_CREATE_INFO *info, + size_t *length) { + Virtual_column_info *v_col= *v_col_ptr; if (!v_col) - return; + return 0; + + /* + Convert string to utf8 for storage. + */ + if (!v_col->utf8) + { + /* + We have to create a new Virtual_column_info as for alter table, + the current one may be shared with the original tables + */ + Virtual_column_info *new_vcol= new (thd->mem_root) Virtual_column_info(); + LEX_STRING to; + if (thd->copy_with_error(&my_charset_utf8mb4_general_ci, + &to, + thd->variables.character_set_client, + v_col->expr_str.str, v_col->expr_str.length)) + return 1; + *new_vcol= *v_col; + new_vcol->expr_str= to; + new_vcol->utf8= 1; + *v_col_ptr= new_vcol; + v_col= new_vcol; + } + /* Sum up the length of the expression string, it's optional name and the header. */ (*length)+= (FRM_VCOL_NEW_HEADER_SIZE + v_col->name.length + v_col->expr_str.length); - return; + return 0; } @@ -577,10 +607,15 @@ static bool pack_header(THD *thd, uchar *forminfo, totlength= 0L; reclength= data_offset; no_empty=int_count=int_parts=int_length=time_stamp_pos=null_fields=0; - com_length=expression_length=0; + com_length= 0; n_length=2L; create_info->field_check_constraints= 0; + expression_length= packed_constraints_length(thd, create_info, + create_info->constraint_list); + if (!expression_length && create_info->constraint_list->elements) + DBUG_RETURN(1); // Wrong characterset + /* Check fields */ List_iterator it(create_fields); Create_field *field; @@ -590,10 +625,16 @@ static bool pack_header(THD *thd, uchar *forminfo, ER_TOO_LONG_FIELD_COMMENT, field->field_name)) DBUG_RETURN(1); - add_expr_length(field->vcol_info, &expression_length); + if (add_expr_length(thd, &field->vcol_info, create_info, + &expression_length)) + DBUG_RETURN(1); if (field->has_default_expression()) - add_expr_length(field->default_value, &expression_length); - add_expr_length(field->check_constraint, &expression_length); + if (add_expr_length(thd, &field->default_value, create_info, + &expression_length)) + DBUG_RETURN(1); + if (add_expr_length(thd, &field->check_constraint, create_info, + &expression_length)) + DBUG_RETURN(1); totlength+= field->length; com_length+= field->comment.length; @@ -677,6 +718,13 @@ static bool pack_header(THD *thd, uchar *forminfo, my_error(ER_TOO_BIG_ROWSIZE, MYF(0), static_cast(file->max_record_length())); DBUG_RETURN(1); } + + if (expression_length) + { + expression_length+= FRM_VCOL_NEW_BASE_SIZE; + create_info->expression_length= expression_length; + } + /* Hack to avoid bugs with small static rows in MySQL */ reclength=MY_MAX(file->min_record_length(table_options),reclength); if ((ulong) create_fields.elements*FCOMP+FRM_FORMINFO_SIZE+ @@ -760,10 +808,6 @@ static size_t packed_fields_length(List &create_fields) length++; } - add_expr_length(field->vcol_info, &length); - if (field->has_default_expression()) - add_expr_length(field->default_value, &length); - add_expr_length(field->check_constraint, &length); length+= FCOMP; length+= strlen(field->field_name)+1; length+= field->comment.length; @@ -773,13 +817,16 @@ static size_t packed_fields_length(List &create_fields) } -static size_t packed_constraints_length(List *constr) +static size_t packed_constraints_length(THD *thd, HA_CREATE_INFO *info, + List *constr) { List_iterator it(*constr); size_t length= 0; Virtual_column_info *check; + while ((check= it++)) - add_expr_length(check, &length); + if (add_expr_length(thd, it.ref(), info, &length)) + return 0; return length; } @@ -795,11 +842,11 @@ static void pack_constraints(uchar **buff, List *constr) /* Save fields, fieldnames and intervals */ static bool pack_fields(uchar **buff_arg, List &create_fields, + HA_CREATE_INFO *create_info, ulong data_offset) { uchar *buff= *buff_arg; uint int_count, comment_length= 0; - bool has_expressions= 0; Create_field *field; DBUG_ENTER("pack_fields"); @@ -835,9 +882,6 @@ static bool pack_fields(uchar **buff_arg, List &create_fields, { buff[11]= buff[14]= 0; // Numerical } - if (field->vcol_info || field->has_default_expression() || - field->check_constraint) - has_expressions= 1; int2store(buff+15, field->comment.length); comment_length+= field->comment.length; @@ -922,8 +966,12 @@ static bool pack_fields(uchar **buff_arg, List &create_fields, } } - if (has_expressions) + if (create_info->expression_length) { + /* Store header for packed fields (extra space for future) */ + bzero(buff, FRM_VCOL_NEW_BASE_SIZE); + buff+= FRM_VCOL_NEW_BASE_SIZE; + /* Store expressions */ it.rewind(); for (uint field_nr=0 ; (field= it++) ; field_nr++) @@ -936,6 +984,7 @@ static bool pack_fields(uchar **buff_arg, List &create_fields, if (field->check_constraint) pack_expression(&buff, field->check_constraint, field_nr, 3); } + pack_constraints(&buff, create_info->constraint_list); } *buff_arg= buff; DBUG_RETURN(0);