diff options
author | Alexander Barkov <bar@mariadb.org> | 2017-09-23 09:37:18 +0400 |
---|---|---|
committer | Alexander Barkov <bar@mariadb.org> | 2017-09-23 09:37:18 +0400 |
commit | 41da3ca9cc4ccebd2a8b8e387a8c7eb139105796 (patch) | |
tree | 33167929257604a758636499cb49e91ee4e7031a | |
parent | 4e1e5a32668bc717e0049961e789dd29883cc66c (diff) | |
parent | b652430f0094e2baa475b6ea3a3d87da24202f76 (diff) | |
download | mariadb-git-41da3ca9cc4ccebd2a8b8e387a8c7eb139105796.tar.gz |
Merge remote-tracking branch 'origin/bb-10.2-ext' into 10.3
-rw-r--r-- | CMakeLists.txt | 17 | ||||
-rw-r--r-- | client/mysql.cc | 6 | ||||
-rw-r--r-- | cmake/make_dist.cmake.in | 3 | ||||
-rw-r--r-- | include/source_revision.h.in | 1 | ||||
-rw-r--r-- | mysql-test/r/case.result | 48 | ||||
-rw-r--r-- | mysql-test/r/func_debug.result | 14 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/r/func_decode.result | 146 | ||||
-rw-r--r-- | mysql-test/suite/compat/oracle/t/func_decode.test | 77 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/inc/sysvars_server.inc | 2 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/sysvars_server_embedded.result | 12 | ||||
-rw-r--r-- | mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result | 12 | ||||
-rw-r--r-- | mysql-test/t/case.test | 28 | ||||
-rw-r--r-- | sql/item.h | 17 | ||||
-rw-r--r-- | sql/item_cmpfunc.cc | 403 | ||||
-rw-r--r-- | sql/item_cmpfunc.h | 221 | ||||
-rw-r--r-- | sql/item_create.cc | 29 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 34 | ||||
-rw-r--r-- | sql/sql_yacc_ora.yy | 62 | ||||
-rw-r--r-- | sql/sys_vars.cc | 8 |
19 files changed, 864 insertions, 276 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b2da1b7d2a..aefc6711fbc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -442,6 +442,23 @@ CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/include/mysql_version.h.in ${CMAKE_BINARY_DIR}/include/mysql_version.h ) CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/sql/sql_builtin.cc.in ${CMAKE_BINARY_DIR}/sql/sql_builtin.cc) + +FIND_PACKAGE(Git) +IF(GIT_EXECUTABLE) + EXECUTE_PROCESS( + COMMAND ${GIT_EXECUTABLE} rev-parse HEAD + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_VARIABLE OUT RESULT_VARIABLE RES) + IF(RES EQUAL 0) + STRING(REGEX REPLACE "\n$" "" SOURCE_REVISION "${OUT}") + ENDIF() +ENDIF() +IF(SOURCE_REVISION OR + (NOT EXISTS ${PROJECT_SOURCE_DIR}/include/source_revision.h)) + CONFIGURE_FILE(${PROJECT_SOURCE_DIR}/include/source_revision.h.in + ${PROJECT_BINARY_DIR}/include/source_revision.h ) +ENDIF() + CONFIGURE_FILE( ${CMAKE_SOURCE_DIR}/cmake/info_macros.cmake.in ${CMAKE_BINARY_DIR}/info_macros.cmake @ONLY) diff --git a/client/mysql.cc b/client/mysql.cc index 94269ccdaf7..b938fa575e8 100644 --- a/client/mysql.cc +++ b/client/mysql.cc @@ -40,7 +40,7 @@ #include "my_readline.h" #include <signal.h> #include <violite.h> - +#include <source_revision.h> #if defined(USE_LIBEDIT_INTERFACE) && defined(HAVE_LOCALE_H) #include <locale.h> #endif @@ -1719,8 +1719,8 @@ static void usage(int version) my_progname, VER, MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE, readline, rl_library_version); #else - printf("%s Ver %s Distrib %s, for %s (%s)\n", my_progname, VER, - MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE); + printf("%s Ver %s Distrib %s, for %s (%s), source revision %s\n", my_progname, VER, + MYSQL_SERVER_VERSION, SYSTEM_TYPE, MACHINE_TYPE,SOURCE_REVISION); #endif if (version) diff --git a/cmake/make_dist.cmake.in b/cmake/make_dist.cmake.in index 3cc93d10fb8..6bd71cc7653 100644 --- a/cmake/make_dist.cmake.in +++ b/cmake/make_dist.cmake.in @@ -52,6 +52,9 @@ IF(GIT_EXECUTABLE) ENDIF() ENDIF() +CONFIGURE_FILE(${CMAKE_BINARY_DIR}/include/source_revision.h + ${PACKAGE_DIR}/include/source_revision.h COPYONLY) + IF(NOT GIT_EXECUTABLE) MESSAGE(STATUS "git not found or source dir is not a repo, use CPack") diff --git a/include/source_revision.h.in b/include/source_revision.h.in new file mode 100644 index 00000000000..5b657d0940e --- /dev/null +++ b/include/source_revision.h.in @@ -0,0 +1 @@ +#cmakedefine SOURCE_REVISION "@SOURCE_REVISION@" diff --git a/mysql-test/r/case.result b/mysql-test/r/case.result index 3214f52e765..ee2db76e517 100644 --- a/mysql-test/r/case.result +++ b/mysql-test/r/case.result @@ -447,3 +447,51 @@ EXECUTE stmt; good was_bad_now_good one one DEALLOCATE PREPARE stmt; +# +# MDEV-13864 Change Item_func_case to store the predicant in args[0] +# +SET NAMES latin1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1); +INSERT INTO t1 VALUES ('a'),('b'),('c'); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' and (case 'a' when 'a' then `test`.`t1`.`a` else 'a' end) = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' and (case 'a' when 'a' then 'a' else `test`.`t1`.`a` end) = 'a' +ALTER TABLE t1 MODIFY a VARBINARY(10); +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; +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 `test`.`t1`.`a` AS `a` from `test`.`t1` where `test`.`t1`.`a` = 'a' +DROP TABLE t1; diff --git a/mysql-test/r/func_debug.result b/mysql-test/r/func_debug.result index f55bfca80f5..f1453344e0e 100644 --- a/mysql-test/r/func_debug.result +++ b/mysql-test/r/func_debug.result @@ -1629,8 +1629,8 @@ WHEN -9223372036854775808 THEN 'one' c NULL Warnings: -Note 1105 DBUG: [0] arg=0 handler=0 (bigint) -Note 1105 DBUG: [1] arg=2 handler=1 (decimal) +Note 1105 DBUG: [0] arg=1 handler=0 (bigint) +Note 1105 DBUG: [1] arg=3 handler=1 (decimal) DROP TABLE t1; # # MDEV-11555 CASE with a mixture of TIME and DATETIME returns a wrong result @@ -1648,10 +1648,10 @@ CASE TIME'10:20:30' good was_bad_now_good one one Warnings: -Note 1105 DBUG: [0] arg=0 handler=0 (time) -Note 1105 DBUG: [1] arg=2 handler=0 (time) -Note 1105 DBUG: [0] arg=0 handler=0 (time) -Note 1105 DBUG: [1] arg=2 handler=0 (time) -Note 1105 DBUG: [2] arg=4 handler=2 (datetime) +Note 1105 DBUG: [0] arg=1 handler=0 (time) +Note 1105 DBUG: [1] arg=3 handler=0 (time) +Note 1105 DBUG: [0] arg=1 handler=0 (time) +Note 1105 DBUG: [1] arg=3 handler=0 (time) +Note 1105 DBUG: [2] arg=5 handler=2 (datetime) SET SESSION debug_dbug="-d,Predicant_to_list_comparator"; SET SESSION debug_dbug="-d,Item_func_in"; diff --git a/mysql-test/suite/compat/oracle/r/func_decode.result b/mysql-test/suite/compat/oracle/r/func_decode.result index c4bfb713e61..b49bad93627 100644 --- a/mysql-test/suite/compat/oracle/r/func_decode.result +++ b/mysql-test/suite/compat/oracle/r/func_decode.result @@ -28,6 +28,150 @@ 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 case 12 when 10 then 'x10' when 11 then 'x11' else 'def' end AS "DECODE(12,10,'x10',11,'x11','def')" +Note 1003 select decode_oracle(12,10,'x10',11,'x11','def') AS "DECODE(12,10,'x10',11,'x11','def')" CREATE TABLE decode (decode int); DROP TABLE decode; +# +# MDEV-13863 sql_mode=ORACLE: DECODE does not treat two NULLs as equivalent +# +SELECT DECODE(10); +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 +SELECT DECODE(10,10); +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 +SELECT DECODE_ORACLE(10); +ERROR 42000: Incorrect parameter count in the call to native function 'DECODE_ORACLE' +SELECT DECODE_ORACLE(10,10); +ERROR 42000: Incorrect parameter count in the call to native function 'DECODE_ORACLE' +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')" +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')" +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')" +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')" +CREATE TABLE t1 (a INT); +CREATE VIEW v1 AS +SELECT +DECODE(a,1,'x1',NULL,'xNULL') AS d1, +DECODE(a,1,'x1',NULL,'xNULL','xELSE') AS d2, +DECODE_ORACLE(a,1,'x1',NULL,'xNULL') AS d3, +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 +DROP VIEW v1; +DROP TABLE t1; +SELECT DECODE(TIME'10:20:31','10:20:31','then1','10:20:32','then2','def'); +DECODE(TIME'10:20:31','10:20:31','then1','10:20:32','then2','def') +then1 +SELECT DECODE(TIME'10:20:32','10:20:31','then1','10:20:32','then2','def'); +DECODE(TIME'10:20:32','10:20:31','then1','10:20:32','then2','def') +then2 +SELECT DECODE(TIME'10:20:33','10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def'); +DECODE(TIME'10:20:33','10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def') +then3 +SELECT DECODE(NULL,TIME'10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def'); +DECODE(NULL,TIME'10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def') +then2NULL +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:31','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def'); +DECODE(TIMESTAMP'2001-01-01 10:20:31','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def') +then1 +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:32','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def'); +DECODE(TIMESTAMP'2001-01-01 10:20:32','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def') +then2 +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:33','2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def'); +DECODE(TIMESTAMP'2001-01-01 10:20:33','2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def') +then3 +SELECT DECODE(NULL,TIMESTAMP'2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def'); +DECODE(NULL,TIMESTAMP'2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def') +then2NULL +SELECT DECODE('w1','w1','then1','w2','then2','def'); +DECODE('w1','w1','then1','w2','then2','def') +then1 +SELECT DECODE('w2','w1','then1','w2','then2','def'); +DECODE('w2','w1','then1','w2','then2','def') +then2 +SELECT DECODE('w3','w1','then1',NULL,'then2NULL','w3','then3','def'); +DECODE('w3','w1','then1',NULL,'then2NULL','w3','then3','def') +then3 +SELECT DECODE(NULL,'w1','then1',NULL,'then2NULL','w3','then3','def'); +DECODE(NULL,'w1','then1',NULL,'then2NULL','w3','then3','def') +then2NULL +SELECT DECODE(1,1,'then1',2,'then2','def'); +DECODE(1,1,'then1',2,'then2','def') +then1 +SELECT DECODE(2,1,'then1',2,'then2','def'); +DECODE(2,1,'then1',2,'then2','def') +then2 +SELECT DECODE(3,1,'then1',NULL,'then2NULL',3,'then3','def'); +DECODE(3,1,'then1',NULL,'then2NULL',3,'then3','def') +then3 +SELECT DECODE(NULL,1,'then1',NULL,'then2NULL',3,'then3','def'); +DECODE(NULL,1,'then1',NULL,'then2NULL',3,'then3','def') +then2NULL +SELECT DECODE(CAST(NULL AS SIGNED),1,'then1',NULL,'then2NULL',3,'then3','def'); +DECODE(CAST(NULL AS SIGNED),1,'then1',NULL,'then2NULL',3,'then3','def') +then2NULL +SELECT DECODE(1.0,1.0,'then1',2.0,'then2','def'); +DECODE(1.0,1.0,'then1',2.0,'then2','def') +then1 +SELECT DECODE(2.0,1.0,'then1',2.0,'then2','def'); +DECODE(2.0,1.0,'then1',2.0,'then2','def') +then2 +SELECT DECODE(3.0,1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); +DECODE(3.0,1.0,'then1',NULL,'then2NULL',3.0,'then3','def') +then3 +SELECT DECODE(NULL,1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); +DECODE(NULL,1.0,'then1',NULL,'then2NULL',3.0,'then3','def') +then2NULL +SELECT DECODE(CAST(NULL AS DECIMAL),1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); +DECODE(CAST(NULL AS DECIMAL),1.0,'then1',NULL,'then2NULL',3.0,'then3','def') +then2NULL +SELECT DECODE(1e0,1e0,'then1',2e0,'then2','def'); +DECODE(1e0,1e0,'then1',2e0,'then2','def') +then1 +SELECT DECODE(2e0,1e0,'then1',2e0,'then2','def'); +DECODE(2e0,1e0,'then1',2e0,'then2','def') +then2 +SELECT DECODE(3e0,1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); +DECODE(3e0,1e0,'then1',NULL,'then2NULL',3e0,'then3','def') +then3 +SELECT DECODE(NULL,1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); +DECODE(NULL,1e0,'then1',NULL,'then2NULL',3e0,'then3','def') +then2NULL +SELECT DECODE(CAST(NULL AS DOUBLE),1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); +DECODE(CAST(NULL AS DOUBLE),1e0,'then1',NULL,'then2NULL',3e0,'then3','def') +then2NULL +SELECT DECODE(NULL,NULL,1,2) FROM DUAL; +DECODE(NULL,NULL,1,2) +1 +SELECT DECODE(NULL,10,10,NULL,1,2) FROM DUAL; +DECODE(NULL,10,10,NULL,1,2) +1 +SELECT DECODE_ORACLE(NULL,NULL,1,2) FROM DUAL; +DECODE_ORACLE(NULL,NULL,1,2) +1 +SELECT DECODE_ORACLE(NULL,10,10,NULL,1,2) FROM DUAL; +DECODE_ORACLE(NULL,10,10,NULL,1,2) +1 +CREATE OR REPLACE TABLE t1 (a VARCHAR(10) DEFAULT NULL); +INSERT INTO t1 VALUES (NULL),(1); +SELECT a, DECODE(a,NULL,1,2) FROM t1; +a DECODE(a,NULL,1,2) +NULL 1 +1 2 +DROP TABLE t1; diff --git a/mysql-test/suite/compat/oracle/t/func_decode.test b/mysql-test/suite/compat/oracle/t/func_decode.test index ae05cb2c3d1..1d49cdd2102 100644 --- a/mysql-test/suite/compat/oracle/t/func_decode.test +++ b/mysql-test/suite/compat/oracle/t/func_decode.test @@ -19,3 +19,80 @@ EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def'); CREATE TABLE decode (decode int); DROP TABLE decode; + + +--echo # +--echo # MDEV-13863 sql_mode=ORACLE: DECODE does not treat two NULLs as equivalent +--echo # + +--error ER_PARSE_ERROR +SELECT DECODE(10); +--error ER_PARSE_ERROR +SELECT DECODE(10,10); + +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT DECODE_ORACLE(10); +--error ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT +SELECT DECODE_ORACLE(10,10); + + +EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11'); +EXPLAIN EXTENDED SELECT DECODE(12,10,'x10',11,'x11','def'); +EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11'); +EXPLAIN EXTENDED SELECT DECODE_ORACLE(12,10,'x10',11,'x11','def'); + +CREATE TABLE t1 (a INT); +CREATE VIEW v1 AS + SELECT + DECODE(a,1,'x1',NULL,'xNULL') AS d1, + DECODE(a,1,'x1',NULL,'xNULL','xELSE') AS d2, + DECODE_ORACLE(a,1,'x1',NULL,'xNULL') AS d3, + DECODE_ORACLE(a,1,'x1',NULL,'xNULL','xELSE') AS d4 + FROM t1; +SHOW CREATE VIEW v1; +DROP VIEW v1; +DROP TABLE t1; + +SELECT DECODE(TIME'10:20:31','10:20:31','then1','10:20:32','then2','def'); +SELECT DECODE(TIME'10:20:32','10:20:31','then1','10:20:32','then2','def'); +SELECT DECODE(TIME'10:20:33','10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def'); +SELECT DECODE(NULL,TIME'10:20:31','then1',NULL,'then2NULL','10:20:33','then3','def'); + +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:31','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def'); +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:32','2001-01-01 10:20:31','then1','2001-01-01 10:20:32','then2','def'); +SELECT DECODE(TIMESTAMP'2001-01-01 10:20:33','2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def'); +SELECT DECODE(NULL,TIMESTAMP'2001-01-01 10:20:31','then1',NULL,'then2NULL','2001-01-01 10:20:33','then3','def'); + +SELECT DECODE('w1','w1','then1','w2','then2','def'); +SELECT DECODE('w2','w1','then1','w2','then2','def'); +SELECT DECODE('w3','w1','then1',NULL,'then2NULL','w3','then3','def'); +SELECT DECODE(NULL,'w1','then1',NULL,'then2NULL','w3','then3','def'); + +SELECT DECODE(1,1,'then1',2,'then2','def'); +SELECT DECODE(2,1,'then1',2,'then2','def'); +SELECT DECODE(3,1,'then1',NULL,'then2NULL',3,'then3','def'); +SELECT DECODE(NULL,1,'then1',NULL,'then2NULL',3,'then3','def'); +SELECT DECODE(CAST(NULL AS SIGNED),1,'then1',NULL,'then2NULL',3,'then3','def'); + +SELECT DECODE(1.0,1.0,'then1',2.0,'then2','def'); +SELECT DECODE(2.0,1.0,'then1',2.0,'then2','def'); +SELECT DECODE(3.0,1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); +SELECT DECODE(NULL,1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); +SELECT DECODE(CAST(NULL AS DECIMAL),1.0,'then1',NULL,'then2NULL',3.0,'then3','def'); + +SELECT DECODE(1e0,1e0,'then1',2e0,'then2','def'); +SELECT DECODE(2e0,1e0,'then1',2e0,'then2','def'); +SELECT DECODE(3e0,1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); +SELECT DECODE(NULL,1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); +SELECT DECODE(CAST(NULL AS DOUBLE),1e0,'then1',NULL,'then2NULL',3e0,'then3','def'); + +SELECT DECODE(NULL,NULL,1,2) FROM DUAL; +SELECT DECODE(NULL,10,10,NULL,1,2) FROM DUAL; + +SELECT DECODE_ORACLE(NULL,NULL,1,2) FROM DUAL; +SELECT DECODE_ORACLE(NULL,10,10,NULL,1,2) FROM DUAL; + +CREATE OR REPLACE TABLE t1 (a VARCHAR(10) DEFAULT NULL); +INSERT INTO t1 VALUES (NULL),(1); +SELECT a, DECODE(a,NULL,1,2) FROM t1; +DROP TABLE t1; diff --git a/mysql-test/suite/sys_vars/inc/sysvars_server.inc b/mysql-test/suite/sys_vars/inc/sysvars_server.inc index 76d35f0fd55..b5c800e858a 100644 --- a/mysql-test/suite/sys_vars/inc/sysvars_server.inc +++ b/mysql-test/suite/sys_vars/inc/sysvars_server.inc @@ -30,6 +30,7 @@ select * from information_schema.system_variables 'rand_seed2', 'system_time_zone', 'version_comment', + 'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) @@ -53,6 +54,7 @@ select VARIABLE_NAME, VARIABLE_SCOPE, VARIABLE_TYPE, VARIABLE_COMMENT, 'rand_seed2', 'system_time_zone', 'version_comment', + 'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result index cab8cc1ceb1..4aac4f0fb2f 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_embedded.result @@ -19,6 +19,7 @@ variable_name not in ( 'rand_seed2', 'system_time_zone', 'version_comment', +'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) @@ -4407,6 +4408,7 @@ where variable_name in ( 'rand_seed2', 'system_time_zone', 'version_comment', +'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) @@ -4561,6 +4563,16 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME VERSION_SOURCE_REVISION +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Source control revision id for MariaDB source code +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY YES +COMMAND_LINE_ARGUMENT NULL VARIABLE_NAME VERSION_SSL_LIBRARY VARIABLE_SCOPE GLOBAL VARIABLE_TYPE VARCHAR diff --git a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result index 232dc7a58f6..e3da35c735c 100644 --- a/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result +++ b/mysql-test/suite/sys_vars/r/sysvars_server_notembedded.result @@ -19,6 +19,7 @@ variable_name not in ( 'rand_seed2', 'system_time_zone', 'version_comment', +'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) @@ -5303,6 +5304,7 @@ where variable_name in ( 'rand_seed2', 'system_time_zone', 'version_comment', +'version_source_revision', 'version_compile_machine', 'version_compile_os', 'version_malloc_library', 'version_ssl_library', 'version' ) @@ -5457,6 +5459,16 @@ NUMERIC_BLOCK_SIZE NULL ENUM_VALUE_LIST NULL READ_ONLY YES COMMAND_LINE_ARGUMENT NULL +VARIABLE_NAME VERSION_SOURCE_REVISION +VARIABLE_SCOPE GLOBAL +VARIABLE_TYPE VARCHAR +VARIABLE_COMMENT Source control revision id for MariaDB source code +NUMERIC_MIN_VALUE NULL +NUMERIC_MAX_VALUE NULL +NUMERIC_BLOCK_SIZE NULL +ENUM_VALUE_LIST NULL +READ_ONLY YES +COMMAND_LINE_ARGUMENT NULL VARIABLE_NAME VERSION_SSL_LIBRARY VARIABLE_SCOPE GLOBAL VARIABLE_TYPE VARCHAR diff --git a/mysql-test/t/case.test b/mysql-test/t/case.test index 87f12cd9e38..52db7588855 100644 --- a/mysql-test/t/case.test +++ b/mysql-test/t/case.test @@ -330,3 +330,31 @@ PREPARE stmt FROM "SELECT EXECUTE stmt; EXECUTE stmt; DEALLOCATE PREPARE stmt; + +--echo # +--echo # MDEV-13864 Change Item_func_case to store the predicant in args[0] +--echo # + +SET NAMES latin1; +CREATE TABLE t1 (a VARCHAR(10) CHARACTER SET latin1); +INSERT INTO t1 VALUES ('a'),('b'),('c'); + +# should propagate the predicant and the WHEN arguments (they are in comparison and use ANY_SUBST) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; + +# should not propagate the THEN and the ELSE arguments (they are not in comparison and use IDENTITY_SUBST) +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; + +ALTER TABLE t1 MODIFY a VARBINARY(10); + +# with VARBINARY it should propagate all arguments +# as IDENTITY_SUBST for VARBINARY allows substitution +# of even those arguments that are not in comparison +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE a WHEN 'a' THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN a THEN 'a' ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN a ELSE 'a' END='a'; +EXPLAIN EXTENDED SELECT * FROM t1 WHERE a='a' AND CASE 'a' WHEN 'a' THEN 'a' ELSE a END='a'; + +DROP TABLE t1; diff --git a/sql/item.h b/sql/item.h index 4a2b10c0912..2dba7890d3b 100644 --- a/sql/item.h +++ b/sql/item.h @@ -56,8 +56,23 @@ struct st_value C_MODE_END +class Value: public st_value +{ +public: + bool is_null() const { return m_type == DYN_COL_NULL; } + bool is_longlong() const + { + return m_type == DYN_COL_UINT || m_type == DYN_COL_INT; + } + bool is_double() const { return m_type == DYN_COL_DOUBLE; } + bool is_temporal() const { return m_type == DYN_COL_DATETIME; } + bool is_string() const { return m_type == DYN_COL_STRING; } + bool is_decimal() const { return m_type == DYN_COL_DECIMAL; } +}; + + template<size_t buffer_size> -class ValueBuffer: public st_value +class ValueBuffer: public Value { char buffer[buffer_size]; void reset_buffer() diff --git a/sql/item_cmpfunc.cc b/sql/item_cmpfunc.cc index d6cd57770cb..3ec382b0541 100644 --- a/sql/item_cmpfunc.cc +++ b/sql/item_cmpfunc.cc @@ -2814,27 +2814,6 @@ Item_func_nullif::is_null() } -Item_func_case::Item_func_case(THD *thd, List<Item> &list, - Item *first_expr_arg, Item *else_expr_arg): - Item_func_case_expression(thd), - Predicant_to_list_comparator(thd, list.elements/*QQ*/), - first_expr_num(-1), else_expr_num(-1), - m_found_types(0) -{ - ncases= list.elements; - if (first_expr_arg) - { - first_expr_num= list.elements; - list.push_back(first_expr_arg, thd->mem_root); - } - if (else_expr_arg) - { - else_expr_num= list.elements; - list.push_back(else_expr_arg, thd->mem_root); - } - set_arguments(thd, list); -} - /** Find and return matching items for CASE or ELSE item if all compares are failed or NULL if ELSE item isn't defined. @@ -2856,27 +2835,37 @@ Item_func_case::Item_func_case(THD *thd, List<Item> &list, failed */ -Item *Item_func_case::find_item(String *str) +Item *Item_func_case_searched::find_item() { - if (first_expr_num == -1) + uint count= when_count(); + for (uint i= 0 ; i < count ; i++) { - for (uint i=0 ; i < ncases ; i+=2) - { - // No expression between CASE and the first WHEN - if (args[i]->val_bool()) - return args[i+1]; - continue; - } + if (args[2 * i]->val_bool()) + return args[2 * i + 1]; } - else - { - /* Compare every WHEN argument with it and return the first match */ - uint idx; - if (!Predicant_to_list_comparator::cmp(this, &idx, NULL)) - return args[idx + 1]; - } - // No, WHEN clauses all missed, return ELSE expression - return else_expr_num != -1 ? args[else_expr_num] : 0; + Item **pos= Item_func_case_searched::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_case_simple::find_item() +{ + /* Compare every WHEN argument with it and return the first match */ + uint idx; + if (!Predicant_to_list_comparator::cmp(this, &idx, NULL)) + return args[idx + 1]; + Item **pos= Item_func_case_simple::else_expr_addr(); + return pos ? pos[0] : 0; +} + + +Item *Item_func_decode_oracle::find_item() +{ + uint idx; + if (!Predicant_to_list_comparator::cmp_nulls_equal(this, &idx)) + return args[idx + 1]; + Item **pos= Item_func_decode_oracle::else_expr_addr(); + return pos ? pos[0] : 0; } @@ -2884,7 +2873,7 @@ String *Item_func_case::str_op(String *str) { DBUG_ASSERT(fixed == 1); String *res; - Item *item=find_item(str); + Item *item= find_item(); if (!item) { @@ -2901,9 +2890,7 @@ String *Item_func_case::str_op(String *str) longlong Item_func_case::int_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); longlong res; if (!item) @@ -2919,9 +2906,7 @@ longlong Item_func_case::int_op() double Item_func_case::real_op() { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff,sizeof(buff),default_charset()); - Item *item=find_item(&dummy_str); + Item *item= find_item(); double res; if (!item) @@ -2938,9 +2923,7 @@ double Item_func_case::real_op() my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); my_decimal *res; if (!item) @@ -2958,9 +2941,7 @@ my_decimal *Item_func_case::decimal_op(my_decimal *decimal_value) bool Item_func_case::date_op(MYSQL_TIME *ltime, uint fuzzydate) { DBUG_ASSERT(fixed == 1); - char buff[MAX_FIELD_WIDTH]; - String dummy_str(buff, sizeof(buff), default_charset()); - Item *item= find_item(&dummy_str); + Item *item= find_item(); if (!item) return (null_value= true); return (null_value= item->get_date_with_conversion(ltime, fuzzydate)); @@ -2975,10 +2956,15 @@ bool Item_func_case::fix_fields(THD *thd, Item **ref) */ uchar buff[MAX_FIELD_WIDTH*2+sizeof(String)*2+sizeof(String*)*2+sizeof(double)*2+sizeof(longlong)*2]; - if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(ncases+1)))) + if (!(arg_buffer= (Item**) thd->alloc(sizeof(Item*)*(arg_count)))) return TRUE; bool res= Item_func::fix_fields(thd, ref); + + Item **pos= else_expr_addr(); + if (!pos || pos[0]->maybe_null) + maybe_null= 1; + /* Call check_stack_overrun after fix_fields to be sure that stack variable is not optimized away @@ -3011,15 +2997,20 @@ static void change_item_tree_if_needed(THD *thd, } -bool Item_func_case::prepare_predicant_and_values(THD *thd, uint *found_types) +bool Item_func_case_simple::prepare_predicant_and_values(THD *thd, + uint *found_types, + bool nulls_equal) { bool have_null= false; uint type_cnt; Type_handler_hybrid_field_type tmp; - add_predicant(this, (uint) first_expr_num); - for (uint i= 0 ; i < ncases / 2; i++) + uint ncases= when_count(); + add_predicant(this, 0); + for (uint i= 0 ; i < ncases; i++) { - if (add_value_skip_null("case..when", this, i * 2, &have_null)) + if (nulls_equal ? + add_value("case..when", this, i * 2 + 1) : + add_value_skip_null("case..when", this, i * 2 + 1, &have_null)) return true; } all_values_added(&tmp, &type_cnt, &m_found_types); @@ -3030,131 +3021,163 @@ bool Item_func_case::prepare_predicant_and_values(THD *thd, uint *found_types) } -void Item_func_case::fix_length_and_dec() +void Item_func_case_searched::fix_length_and_dec() { - Item **agg= arg_buffer; - uint nagg; THD *thd= current_thd; + Item **else_ptr= Item_func_case_searched::else_expr_addr(); + aggregate_then_and_else_arguments(thd, &args[1], when_count(), else_ptr); +} - m_found_types= 0; - if (else_expr_num == -1 || args[else_expr_num]->maybe_null) - maybe_null= 1; - /* - Aggregate all THEN and ELSE expression types - and collations when string result - */ - - for (nagg= 0 ; nagg < ncases/2 ; nagg++) - agg[nagg]= args[nagg*2+1]; +void Item_func_case_simple::fix_length_and_dec() +{ + THD *thd= current_thd; + Item **else_ptr= Item_func_case_simple::else_expr_addr(); + if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + aggregate_switch_and_when_arguments(thd, false); +} + + +void Item_func_decode_oracle::fix_length_and_dec() +{ + THD *thd= current_thd; + Item **else_ptr= Item_func_decode_oracle::else_expr_addr(); + if (!aggregate_then_and_else_arguments(thd, &args[2], when_count(), else_ptr)) + aggregate_switch_and_when_arguments(thd, true); +} + + +/* + Aggregate all THEN and ELSE expression types + and collations when string result - if (else_expr_num != -1) - agg[nagg++]= args[else_expr_num]; + @param THD - current thd + @param them_expr - the pointer to the leftmost THEN argument in args[] + @param count - the number or THEN..ELSE pairs + @param else_epxr - the pointer to the ELSE arguments in args[] + (or NULL is there is not ELSE) +*/ +bool Item_func_case::aggregate_then_and_else_arguments(THD *thd, + Item **then_expr, + uint count, + Item **else_expr) +{ + Item **agg= arg_buffer; + uint nagg; + + for (nagg= 0 ; nagg < count ; nagg++) + agg[nagg]= then_expr[nagg * 2]; + + if (else_expr) + agg[nagg++]= *else_expr; if (aggregate_for_result(func_name(), agg, nagg, true)) - return; + return true; if (fix_attributes(agg, nagg)) - return; + return true; /* - Copy all modified THEN and ELSE items back to args[] array. + Copy all modified THEN and ELSE items back to then_expr[] array. Some of the items might have been changed to Item_func_conv_charset. */ - for (nagg= 0 ; nagg < ncases / 2 ; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg]); + for (nagg= 0 ; nagg < count ; nagg++) + change_item_tree_if_needed(thd, &then_expr[nagg * 2], agg[nagg]); - if (else_expr_num != -1) - change_item_tree_if_needed(thd, &args[else_expr_num], agg[nagg++]); - - /* - Aggregate first expression and all WHEN expression types - and collations when string comparison - */ - if (first_expr_num != -1) - { - if (prepare_predicant_and_values(thd, &m_found_types)) - { - /* - If Predicant_to_list_comparator() fails to prepare components, - it must put an error into the diagnostics area. This is needed - to make fix_fields() catches such errors. - */ - DBUG_ASSERT(thd->is_error()); - return; - } + if (else_expr) + change_item_tree_if_needed(thd, else_expr, agg[nagg++]); + return false; +} - agg[0]= args[first_expr_num]; +/* + Aggregate the predicant expression and all WHEN expression types + and collations when string comparison +*/ +bool Item_func_case_simple::aggregate_switch_and_when_arguments(THD *thd, + bool nulls_eq) +{ + Item **agg= arg_buffer; + uint nagg; + uint ncases= when_count(); + m_found_types= 0; + if (prepare_predicant_and_values(thd, &m_found_types, nulls_eq)) + { /* - As the first expression and WHEN expressions - are intermixed in args[] array THEN and ELSE items, - extract the first expression and all WHEN expressions into - a temporary array, to process them easier. + If Predicant_to_list_comparator() fails to prepare components, + it must put an error into the diagnostics area. This is needed + to make fix_fields() catches such errors. */ - for (nagg= 0; nagg < ncases/2 ; nagg++) - agg[nagg+1]= args[nagg*2]; - nagg++; - if (!(m_found_types= collect_cmp_types(agg, nagg))) - return; + DBUG_ASSERT(thd->is_error()); + return true; + } - if (m_found_types & (1U << STRING_RESULT)) - { - /* - If we'll do string comparison, we also need to aggregate - character set and collation for first/WHEN items and - install converters for some of them to cmp_collation when necessary. - This is done because cmp_item compatators cannot compare - strings in two different character sets. - Some examples when we install converters: + /* + As the predicant expression and WHEN expressions + are intermixed in args[] array THEN and ELSE items, + extract the first expression and all WHEN expressions into + a temporary array, to process them easier. + */ + agg[0]= args[0]; // The predicant + for (nagg= 0; nagg < ncases ; nagg++) + agg[nagg+1]= args[nagg * 2 + 1]; + nagg++; + if (!(m_found_types= collect_cmp_types(agg, nagg))) + return true; - 1. Converter installed for the first expression: + if (m_found_types & (1U << STRING_RESULT)) + { + /* + If we'll do string comparison, we also need to aggregate + character set and collation for first/WHEN items and + install converters for some of them to cmp_collation when necessary. + This is done because cmp_item compatators cannot compare + strings in two different character sets. + Some examples when we install converters: - CASE latin1_item WHEN utf16_item THEN ... END + 1. Converter installed for the first expression: - is replaced to: + CASE latin1_item WHEN utf16_item THEN ... END - CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END + is replaced to: - 2. Converter installed for the left WHEN item: + CASE CONVERT(latin1_item USING utf16) WHEN utf16_item THEN ... END - CASE utf16_item WHEN latin1_item THEN ... END + 2. Converter installed for the left WHEN item: - is replaced to: + CASE utf16_item WHEN latin1_item THEN ... END - CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END - */ - if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg)) - return; - /* - Now copy first expression and all WHEN expressions back to args[] - arrray, because some of the items might have been changed to converters - (e.g. Item_func_conv_charset, or Item_string for constants). - */ - change_item_tree_if_needed(thd, &args[first_expr_num], agg[0]); + is replaced to: - for (nagg= 0; nagg < ncases / 2; nagg++) - change_item_tree_if_needed(thd, &args[nagg * 2], agg[nagg + 1]); - - } + CASE utf16_item WHEN CONVERT(latin1_item USING utf16) THEN ... END + */ + if (agg_arg_charsets_for_comparison(cmp_collation, agg, nagg)) + return true; + /* + Now copy first expression and all WHEN expressions back to args[] + arrray, because some of the items might have been changed to converters + (e.g. Item_func_conv_charset, or Item_string for constants). + */ + change_item_tree_if_needed(thd, &args[0], agg[0]); - if (make_unique_cmp_items(thd, cmp_collation.collation)) - return; + for (nagg= 0; nagg < ncases; nagg++) + change_item_tree_if_needed(thd, &args[nagg * 2 + 1], agg[nagg + 1]); } + + if (make_unique_cmp_items(thd, cmp_collation.collation)) + return true; + + return false; } -Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) +Item* Item_func_case_simple::propagate_equal_fields(THD *thd, + const Context &ctx, + COND_EQUAL *cond) { const Type_handler *first_expr_cmp_handler; - if (first_expr_num == -1) - { - // None of the arguments are in a comparison context - Item_args::propagate_equal_fields(thd, Context_identity(), cond); - return this; - } - first_expr_cmp_handler= args[first_expr_num]->type_handler_for_comparison(); + first_expr_cmp_handler= args[0]->type_handler_for_comparison(); for (uint i= 0; i < arg_count; i++) { /* @@ -3164,7 +3187,7 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ CASE ... THEN y1 ... THEN y2 ... THEN y3 ... ELSE y4 END */ Item *new_item= 0; - if ((int) i == first_expr_num) // Then CASE (the switch) argument + if (i == 0) // Then CASE (the switch) argument { /* Cannot replace the CASE (the switch) argument if @@ -3201,7 +3224,7 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ cmp_collation.collation), cond); } - else if ((i % 2) == 0) // WHEN arguments + else if ((i % 2) == 1 && i != arg_count - 1) // WHEN arguments { /* These arguments are in comparison. @@ -3230,33 +3253,51 @@ Item* Item_func_case::propagate_equal_fields(THD *thd, const Context &ctx, COND_ } -/** - @todo - Fix this so that it prints the whole CASE expression -*/ - -void Item_func_case::print(String *str, enum_query_type query_type) +void Item_func_case::print_when_then_arguments(String *str, + enum_query_type query_type, + Item **items, uint count) { - str->append(STRING_WITH_LEN("case ")); - if (first_expr_num != -1) - { - args[first_expr_num]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - for (uint i=0 ; i < ncases ; i+=2) + for (uint i=0 ; i < count ; i++) { str->append(STRING_WITH_LEN("when ")); - args[i]->print_parenthesised(str, query_type, precedence()); + items[i * 2]->print_parenthesised(str, query_type, precedence()); str->append(STRING_WITH_LEN(" then ")); - args[i+1]->print_parenthesised(str, query_type, precedence()); - str->append(' '); - } - if (else_expr_num != -1) - { - str->append(STRING_WITH_LEN("else ")); - args[else_expr_num]->print_parenthesised(str, query_type, precedence()); + items[i * 2 + 1]->print_parenthesised(str, query_type, precedence()); str->append(' '); } +} + + +void Item_func_case::print_else_argument(String *str, + enum_query_type query_type, + Item *item) +{ + str->append(STRING_WITH_LEN("else ")); + item->print_parenthesised(str, query_type, precedence()); + str->append(' '); +} + + +void Item_func_case_searched::print(String *str, enum_query_type query_type) +{ + Item **pos; + str->append(STRING_WITH_LEN("case ")); + print_when_then_arguments(str, query_type, &args[0], when_count()); + if ((pos= Item_func_case_searched::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); + str->append(STRING_WITH_LEN("end")); +} + + +void Item_func_case_simple::print(String *str, enum_query_type query_type) +{ + Item **pos; + str->append(STRING_WITH_LEN("case ")); + args[0]->print_parenthesised(str, query_type, precedence()); + str->append(' '); + print_when_then_arguments(str, query_type, &args[1], when_count()); + if ((pos= Item_func_case_simple::else_expr_addr())) + print_else_argument(str, query_type, pos[0]); str->append(STRING_WITH_LEN("end")); } @@ -3944,6 +3985,14 @@ void cmp_item_decimal::store_value(Item *item) } +int cmp_item_decimal::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_decimal()); + return my_decimal_cmp(&value, &val->m_decimal); +} + + int cmp_item_decimal::cmp(Item *arg) { my_decimal tmp_buf, *tmp= arg->val_decimal(&tmp_buf); @@ -3975,6 +4024,14 @@ void cmp_item_temporal::store_value_internal(Item *item, } +int cmp_item_datetime::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + int cmp_item_datetime::cmp(Item *arg) { const bool rc= value != arg->val_datetime_packed(); @@ -3982,6 +4039,14 @@ int cmp_item_datetime::cmp(Item *arg) } +int cmp_item_time::cmp_not_null(const Value *val) +{ + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_temporal()); + return value != pack_time(&val->value.m_time); +} + + int cmp_item_time::cmp(Item *arg) { const bool rc= value != arg->val_time_packed(); diff --git a/sql/item_cmpfunc.h b/sql/item_cmpfunc.h index c7272f1f2cc..0756899f47c 100644 --- a/sql/item_cmpfunc.h +++ b/sql/item_cmpfunc.h @@ -1475,6 +1475,7 @@ public: "stored argument's value <> item's value" */ virtual int cmp(Item *item)= 0; + virtual int cmp_not_null(const Value *value)= 0; // for optimized IN with row virtual int compare(cmp_item *item)= 0; virtual cmp_item *make_same()= 0; @@ -1519,6 +1520,12 @@ public: value_res= item->val_str(&value); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_string()); + return sortcmp(value_res, &val->m_string, cmp_charset) != 0; + } int cmp(Item *arg) { char buff[STRING_BUFFER_USUAL_SIZE]; @@ -1555,6 +1562,12 @@ public: value= item->val_int(); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_longlong()); + return value != val->value.m_longlong; + } int cmp(Item *arg) { const bool rc= value != arg->val_int(); @@ -1599,6 +1612,7 @@ public: { store_value_internal(item, MYSQL_TYPE_DATETIME); } + int cmp_not_null(const Value *val); int cmp(Item *arg); cmp_item *make_same(); }; @@ -1614,6 +1628,7 @@ public: { store_value_internal(item, MYSQL_TYPE_TIME); } + int cmp_not_null(const Value *val); int cmp(Item *arg); cmp_item *make_same(); }; @@ -1628,6 +1643,12 @@ public: value= item->val_real(); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_double()); + return value != val->value.m_double; + } int cmp(Item *arg) { const bool rc= value != arg->val_real(); @@ -1649,6 +1670,7 @@ public: cmp_item_decimal() {} /* Remove gcc warning */ void store_value(Item *item); int cmp(Item *arg); + int cmp_not_null(const Value *val); int compare(cmp_item *c); cmp_item *make_same(); }; @@ -1671,6 +1693,11 @@ public: value_res= item->val_str(&value); m_null_value= item->null_value; } + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(false); + return TRUE; + } int cmp(Item *item) { // Should never be called @@ -1837,7 +1864,24 @@ class Predicant_to_list_comparator return UNKNOWN; return in_item->cmp(args->arguments()[m_comparators[i].m_arg_index]); } - + int cmp_args_nulls_equal(Item_args *args, uint i) + { + Predicant_to_value_comparator *cmp= + &m_comparators[m_comparators[i].m_handler_index]; + cmp_item *in_item= cmp->m_cmp_item; + DBUG_ASSERT(in_item); + Item *predicant= args->arguments()[m_predicant_index]; + Item *arg= args->arguments()[m_comparators[i].m_arg_index]; + ValueBuffer<MAX_FIELD_WIDTH> val; + if (m_comparators[i].m_handler_index == i) + in_item->store_value(predicant); + m_comparators[i].m_handler->Item_save_in_value(arg, &val); + if (predicant->null_value && val.is_null()) + return FALSE; // Two nulls are equal + if (predicant->null_value || val.is_null()) + return UNKNOWN; + return in_item->cmp_not_null(&val); + } /** Predicant_to_value_comparator - a comparator for one pair (pred,valueN). See comments above. @@ -2009,78 +2053,172 @@ public: } return true; // Not found } - + /* + Same as above, but treats two NULLs as equal, e.g. as in DECODE_ORACLE(). + */ + bool cmp_nulls_equal(Item_args *args, uint *idx) + { + for (uint i= 0 ; i < m_comparator_count ; i++) + { + DBUG_ASSERT(m_comparators[i].m_handler != NULL); + if (cmp_args_nulls_equal(args, i) == FALSE) + { + *idx= m_comparators[i].m_arg_index; + return false; // Found a matching value + } + } + return true; // Not found + } }; /* The class Item_func_case is the CASE ... WHEN ... THEN ... END function implementation. - - When there is no expression between CASE and the first WHEN - (the CASE expression) then this function simple checks all WHEN expressions - one after another. When some WHEN expression evaluated to TRUE then the - value of the corresponding THEN expression is returned. - - When the CASE expression is specified then it is compared to each WHEN - expression individually. When an equal WHEN expression is found - corresponding THEN expression is returned. - In order to do correct comparisons several comparators are used. One for - each result type. Different result types that are used in particular - CASE ... END expression are collected in the fix_length_and_dec() member - function and only comparators for there result types are used. */ -class Item_func_case :public Item_func_case_expression, - public Predicant_to_list_comparator +class Item_func_case :public Item_func_case_expression { - int first_expr_num, else_expr_num; +protected: String tmp_value; - uint ncases; DTCollation cmp_collation; Item **arg_buffer; - uint m_found_types; - bool prepare_predicant_and_values(THD *thd, uint *found_types); -public: - Item_func_case(THD *thd, List<Item> &list, Item *first_expr_arg, - Item *else_expr_arg); + bool aggregate_then_and_else_arguments(THD *thd, + Item **items, uint count, + Item **else_expr); + virtual Item **else_expr_addr() const= 0; + virtual Item *find_item()= 0; + void print_when_then_arguments(String *str, enum_query_type query_type, + Item **items, uint count); + void print_else_argument(String *str, enum_query_type query_type, Item *item); +public: + Item_func_case(THD *thd, List<Item> &list) + :Item_func_case_expression(thd, list) + { } double real_op(); longlong int_op(); String *str_op(String *); my_decimal *decimal_op(my_decimal *); bool date_op(MYSQL_TIME *ltime, uint fuzzydate); bool fix_fields(THD *thd, Item **ref); - void fix_length_and_dec(); table_map not_null_tables() const { return 0; } const char *func_name() const { return "case"; } enum precedence precedence() const { return BETWEEN_PRECEDENCE; } - virtual void print(String *str, enum_query_type query_type); - Item *find_item(String *str); CHARSET_INFO *compare_collation() const { return cmp_collation.collation; } + bool need_parentheses_in_default() { return true; } + Item *build_clone(THD *thd, MEM_ROOT *mem_root) + { + Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); + if (clone) + clone->arg_buffer= 0; + return clone; + } +}; + + +/* + CASE WHEN cond THEN res [WHEN cond THEN res...] [ELSE res] END + + Searched CASE checks all WHEN expressions one after another. + When some WHEN expression evaluated to TRUE then the + value of the corresponding THEN expression is returned. +*/ +class Item_func_case_searched: public Item_func_case +{ + uint when_count() const { return arg_count / 2; } + bool with_else() const { return arg_count % 2; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } +public: + Item_func_case_searched(THD *thd, List<Item> &list) + :Item_func_case(thd, list) + { + DBUG_ASSERT(arg_count >= 2); + } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond) + { + // None of the arguments are in a comparison context + Item_args::propagate_equal_fields(thd, Context_identity(), cond); + return this; + } + Item *find_item(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_case_searched>(thd, mem_root, this); } +}; + + +/* + CASE pred WHEN value THEN res [WHEN value THEN res...] [ELSE res] END + + When the predicant expression is specified then it is compared to each WHEN + expression individually. When an equal WHEN expression is found + the corresponding THEN expression is returned. + In order to do correct comparisons several comparators are used. One for + each result type. Different result types that are used in particular + CASE ... END expression are collected in the fix_length_and_dec() member + function and only comparators for there result types are used. +*/ +class Item_func_case_simple: public Item_func_case, + public Predicant_to_list_comparator +{ +protected: + uint m_found_types; + uint when_count() const { return (arg_count - 1) / 2; } + bool with_else() const { return arg_count % 2 == 0; } + Item **else_expr_addr() const { return with_else() ? &args[arg_count - 1] : 0; } + bool aggregate_switch_and_when_arguments(THD *thd, bool nulls_equal); + bool prepare_predicant_and_values(THD *thd, uint *found_types, + bool nulls_equal); +public: + Item_func_case_simple(THD *thd, List<Item> &list) + :Item_func_case(thd, list), + Predicant_to_list_comparator(thd, arg_count), + m_found_types(0) + { + DBUG_ASSERT(arg_count >= 3); + } void cleanup() { - DBUG_ENTER("Item_func_case::cleanup"); + DBUG_ENTER("Item_func_case_simple::cleanup"); Item_func::cleanup(); Predicant_to_list_comparator::cleanup(); DBUG_VOID_RETURN; } - Item* propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); - bool need_parentheses_in_default() { return true; } - Item *get_copy(THD *thd, MEM_ROOT *mem_root) - { return get_item_copy<Item_func_case>(thd, mem_root, this); } + void print(String *str, enum_query_type query_type); + void fix_length_and_dec(); + Item *propagate_equal_fields(THD *thd, const Context &ctx, COND_EQUAL *cond); + Item *find_item(); Item *build_clone(THD *thd, MEM_ROOT *mem_root) { - Item_func_case *clone= (Item_func_case *) Item_func::build_clone(thd, mem_root); - if (clone) - { - clone->arg_buffer= 0; - if (clone->Predicant_to_list_comparator::init_clone(thd, ncases)) - return NULL; - } + Item_func_case_simple *clone= (Item_func_case_simple *) + Item_func_case::build_clone(thd, mem_root); + uint ncases= when_count(); + if (clone && clone->Predicant_to_list_comparator::init_clone(thd, ncases)) + return NULL; return clone; } + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_case_simple>(thd, mem_root, this); } }; + +class Item_func_decode_oracle: public Item_func_case_simple +{ +public: + Item_func_decode_oracle(THD *thd, List<Item> &list) + :Item_func_case_simple(thd, list) + { } + const char *func_name() const { return "decode_oracle"; } + void print(String *str, enum_query_type query_type) + { Item_func::print(str, query_type); } + void fix_length_and_dec(); + Item *find_item(); + Item *get_copy(THD *thd, MEM_ROOT *mem_root) + { return get_item_copy<Item_func_decode_oracle>(thd, mem_root, this); } +}; + + /* The Item_func_in class implements in_expr IN (<in value list>) @@ -2260,6 +2398,11 @@ public: bool alloc_comparators(THD *thd, uint n); bool prepare_comparators(THD *, Item **args, uint arg_count); int cmp(Item *arg); + int cmp_not_null(const Value *val) + { + DBUG_ASSERT(false); + return TRUE; + } int compare(cmp_item *arg); cmp_item *make_same(); void store_value_by_template(THD *thd, cmp_item *tmpl, Item *); diff --git a/sql/item_create.cc b/sql/item_create.cc index 1ac85b2f0b2..8bde2dcc53a 100644 --- a/sql/item_create.cc +++ b/sql/item_create.cc @@ -623,6 +623,19 @@ protected: }; +class Create_func_decode_oracle : public Create_native_func +{ +public: + virtual Item *create_native(THD *thd, LEX_CSTRING *name, List<Item> *item_list); + + static Create_func_decode_oracle s_singleton; + +protected: + Create_func_decode_oracle() {} + virtual ~Create_func_decode_oracle() {} +}; + + class Create_func_concat_ws : public Create_native_func { public: @@ -3894,6 +3907,21 @@ Create_func_decode_histogram::create_2_arg(THD *thd, Item *arg1, Item *arg2) return new (thd->mem_root) Item_func_decode_histogram(thd, arg1, arg2); } +Create_func_decode_oracle Create_func_decode_oracle::s_singleton; + +Item* +Create_func_decode_oracle::create_native(THD *thd, LEX_CSTRING *name, + List<Item> *item_list) +{ + uint arg_count= item_list ? item_list->elements : 0; + if (arg_count < 3) + { + my_error(ER_WRONG_PARAMCOUNT_TO_NATIVE_FCT, MYF(0), name->str); + return NULL; + } + return new (thd->mem_root) Item_func_decode_oracle(thd, *item_list); +} + Create_func_concat_ws Create_func_concat_ws::s_singleton; Item* @@ -6851,6 +6879,7 @@ static Native_func_registry func_array[] = { { C_STRING_WITH_LEN("DAYOFYEAR") }, BUILDER(Create_func_dayofyear)}, { { C_STRING_WITH_LEN("DEGREES") }, BUILDER(Create_func_degrees)}, { { C_STRING_WITH_LEN("DECODE_HISTOGRAM") }, BUILDER(Create_func_decode_histogram)}, + { { C_STRING_WITH_LEN("DECODE_ORACLE") }, BUILDER(Create_func_decode_oracle)}, { { C_STRING_WITH_LEN("DES_DECRYPT") }, BUILDER(Create_func_des_decrypt)}, { { C_STRING_WITH_LEN("DES_ENCRYPT") }, BUILDER(Create_func_des_encrypt)}, { { C_STRING_WITH_LEN("DIMENSION") }, GEOM_BUILDER(Create_func_dimension)}, diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 766d22e20e3..5543a3e2597 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1718,7 +1718,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident temporal_literal - simple_ident expr opt_expr opt_else sum_expr in_sum_expr + simple_ident expr sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr @@ -1746,7 +1746,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NUM_literal %type <item_list> - expr_list opt_udf_expr_list udf_expr_list when_list + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list %type <sp_cursor_stmt> @@ -9431,10 +9431,15 @@ column_default_non_parenthesized_expr: if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } - | CASE_SYM opt_expr when_list opt_else END + | CASE_SYM when_list_opt_else END { - $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); - if ($$ == NULL) + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr ',' cast_type ')' @@ -10856,16 +10861,6 @@ ident_list: } ; -opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; - -opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; - when_list: WHEN_SYM expr THEN_SYM expr { @@ -10883,6 +10878,15 @@ when_list: } ; +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + /* Equivalent to <table reference> in the SQL:2003 standard. */ /* Warning - may return NULL in case of incomplete SELECT */ table_ref: diff --git a/sql/sql_yacc_ora.yy b/sql/sql_yacc_ora.yy index ad37c511e1e..1a3ce0ab372 100644 --- a/sql/sql_yacc_ora.yy +++ b/sql/sql_yacc_ora.yy @@ -1134,7 +1134,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); %type <item> literal text_literal insert_ident order_ident temporal_literal - simple_ident expr opt_expr opt_else sum_expr in_sum_expr + simple_ident expr sum_expr in_sum_expr variable variable_aux bool_pri predicate bit_expr parenthesized_expr table_wild simple_expr column_default_non_parenthesized_expr udf_expr @@ -1164,7 +1164,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, ulong *yystacksize); NUM_literal %type <item_list> - expr_list opt_udf_expr_list udf_expr_list when_list + expr_list opt_udf_expr_list udf_expr_list when_list when_list_opt_else ident_list ident_list_arg opt_expr_list decode_when_list @@ -9441,10 +9441,15 @@ column_default_non_parenthesized_expr: if (!($$= $5.create_typecast_item(thd, $3, Lex->charset))) MYSQL_YYABORT; } - | CASE_SYM opt_expr when_list opt_else END + | CASE_SYM when_list_opt_else END { - $$= new (thd->mem_root) Item_func_case(thd, *$3, $2, $4); - if ($$ == NULL) + if (!($$= new(thd->mem_root) Item_func_case_searched(thd, *$2))) + MYSQL_YYABORT; + } + | CASE_SYM expr when_list_opt_else END + { + $3->push_front($2, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_case_simple(thd, *$3))) MYSQL_YYABORT; } | CONVERT_SYM '(' expr ',' cast_type ')' @@ -9460,32 +9465,8 @@ column_default_non_parenthesized_expr: } | DECODE_SYM '(' expr ',' decode_when_list ')' { - if (($5->elements % 2) == 0) - { - // No default expression - $$= new (thd->mem_root) Item_func_case(thd, *$5, $3, NULL); - } - else - { - /* - There is a default expression at the end of the list $5. - Create a new list without the default expression. - */ - List<Item> tmp; - List_iterator_fast<Item> it(*$5); - for (uint i= 0; i < $5->elements - 1; i++) // copy all but last - { - Item *item= it++; - tmp.push_back(item); - } - /* - Now the new list "tmp" contains only WHEN-THEN pairs, - The default expression is pointed by the iterator "it" - and will be returned by the next call for it++ below. - */ - $$= new (thd->mem_root) Item_func_case(thd, tmp, $3, it++); - } - if ($$ == NULL) + $5->push_front($3, thd->mem_root); + if (!($$= new (thd->mem_root) Item_func_decode_oracle(thd, *$5))) MYSQL_YYABORT; } | DEFAULT '(' simple_ident ')' @@ -10908,16 +10889,6 @@ ident_list: } ; -opt_expr: - /* empty */ { $$= NULL; } - | expr { $$= $1; } - ; - -opt_else: - /* empty */ { $$= NULL; } - | ELSE expr { $$= $2; } - ; - when_list: WHEN_SYM expr THEN_SYM expr { @@ -10936,6 +10907,15 @@ when_list: ; +when_list_opt_else: + when_list + | when_list ELSE expr + { + $1->push_back($3, thd->mem_root); + $$= $1; + } + ; + decode_when_list: expr ',' expr { diff --git a/sql/sys_vars.cc b/sql/sys_vars.cc index 1f94f970e45..bb5a65afd09 100644 --- a/sql/sys_vars.cc +++ b/sql/sys_vars.cc @@ -3552,6 +3552,14 @@ static Sys_var_charptr Sys_version_compile_os( CMD_LINE_HELP_ONLY, IN_SYSTEM_CHARSET, DEFAULT(SYSTEM_TYPE)); +#include <source_revision.h> +static char *server_version_source_revision; +static Sys_var_charptr Sys_version_source_revision( + "version_source_revision", "Source control revision id for MariaDB source code", + READ_ONLY GLOBAL_VAR(server_version_source_revision), + CMD_LINE_HELP_ONLY, + IN_SYSTEM_CHARSET, DEFAULT(SOURCE_REVISION)); + static char *guess_malloc_library() { if (strcmp(MALLOC_LIBRARY, "system") == 0) |