From d7ba72ea9bfc26b6b7172b4ad6c5a1eddbc386c1 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Sat, 6 Aug 2022 22:18:11 -0400 Subject: [PATCH 01/31] Remove Darwin CMake file The file is now empty and thus serves no purpose. --- cmake/os/Darwin.cmake | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 cmake/os/Darwin.cmake diff --git a/cmake/os/Darwin.cmake b/cmake/os/Darwin.cmake deleted file mode 100644 index 21e18360dfe..00000000000 --- a/cmake/os/Darwin.cmake +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright (c) 2010, 2014, Oracle and/or its affiliates. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; version 2 of the License. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA - -# This file includes OSX specific options and quirks, related to system checks From 5d3bbc6da19c4a3c7d5fd72bc6d16dbadee68d76 Mon Sep 17 00:00:00 2001 From: Addison G Date: Tue, 2 Aug 2022 14:35:48 +1000 Subject: [PATCH 02/31] MDEV-29222 - Fix mysqld_safe script The mysqld_safe script was using bad grep options. The line that was fixed was likely meant to be 2 separate grep commands, piped into each other, with each one removing any lines that matched. The `-E` option was unneeded, as the command is not using regex. --- scripts/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index aca78f38322..9eec793c9fb 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -146,7 +146,7 @@ IF(UNIX) # FIND_PROC and CHECK_PID are used by mysqld_safe IF(CMAKE_SYSTEM_NAME MATCHES "Linux") SET (FIND_PROC - "ps wwwp $PID | grep -vE mariadbd-safe -vE mysqld_safe | grep -- $MYSQLD > /dev/null") + "ps wwwp $PID | grep -v mariadbd-safe | grep -v mysqld_safe | grep -- $MYSQLD > /dev/null") ENDIF() IF(NOT FIND_PROC AND CMAKE_SYSTEM_NAME MATCHES "SunOS") SET (FIND_PROC From d48428e99aa7435ff72e2df7da05f35363e90ec3 Mon Sep 17 00:00:00 2001 From: Rucha Deodhar Date: Mon, 1 Aug 2022 19:39:09 +0530 Subject: [PATCH 03/31] MDEV-27151: JSON_VALUE() does not parse NULL properties properly Analysis: JSON_VALUE() returns "null" string instead of NULL pointer. Fix: When the type is JSON_VALUE_NULL (which is also a scalar) set null_value to true and return 0 instead of returning string. --- mysql-test/main/func_json.result | 27 +++++++++++++++++++++++++++ mysql-test/main/func_json.test | 19 +++++++++++++++++++ sql/item_jsonfunc.cc | 7 ++++++- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index 8e3b47e322a..2c3cb2f1e72 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -1016,5 +1016,32 @@ j {"ID": "4", "Name": "Betty", "Age": 19} drop table t1; # +# MDEV-27151: JSON_VALUE() does not parse NULL properties properly +# +# +# It is correct for JSON_EXTRACT() to give null instead of "NULL" because +# it returns the json literal that is put inside json. +# Hence it should return null as in 'null' string and not SQL NULL. +# JSON_VALUE() returns the "VALUE" so it is correct for it to return SQl NULL +# +SELECT NULL; +NULL +NULL +SELECT JSON_VALUE('{"nulltest": null}', '$.nulltest'); +JSON_VALUE('{"nulltest": null}', '$.nulltest') +NULL +SELECT 1 + NULL; +1 + NULL +NULL +SELECT 1 + JSON_VALUE('{"nulltest": null}', '$.nulltest'); +1 + JSON_VALUE('{"nulltest": null}', '$.nulltest') +NULL +SELECT NULL; +NULL +NULL +SELECT JSON_EXTRACT('{"a":null, "b":10, "c":"null"}', '$.a'); +JSON_EXTRACT('{"a":null, "b":10, "c":"null"}', '$.a') +null +# # End of 10.3 tests # diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 16f323a9a56..51961c0406a 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -627,6 +627,25 @@ SELECT * FROM t1 WHERE JSON_EXTRACT(j, '$.Age')=19; drop table t1; +--echo # +--echo # MDEV-27151: JSON_VALUE() does not parse NULL properties properly +--echo # +--echo # +--echo # It is correct for JSON_EXTRACT() to give null instead of "NULL" because +--echo # it returns the json literal that is put inside json. +--echo # Hence it should return null as in 'null' string and not SQL NULL. +--echo # JSON_VALUE() returns the "VALUE" so it is correct for it to return SQl NULL +--echo # + +SELECT NULL; +SELECT JSON_VALUE('{"nulltest": null}', '$.nulltest'); +SELECT 1 + NULL; +SELECT 1 + JSON_VALUE('{"nulltest": null}', '$.nulltest'); + + +SELECT NULL; +SELECT JSON_EXTRACT('{"a":null, "b":10, "c":"null"}', '$.a'); + --echo # --echo # End of 10.3 tests --echo # diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 011cfdc8fd0..abbff8c3e92 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -619,6 +619,12 @@ continue_search: if (json_read_value(&je)) goto err_return; + if (je.value_type == JSON_VALUE_NULL) + { + null_value= 1; + return NULL; + } + if (unlikely(check_and_get_value(&je, str, &error))) { if (error) @@ -1111,7 +1117,6 @@ my_decimal *Item_func_json_extract::val_decimal(my_decimal *to) case JSON_VALUE_OBJECT: case JSON_VALUE_ARRAY: case JSON_VALUE_FALSE: - // TODO: fix: NULL should be NULL case JSON_VALUE_NULL: int2my_decimal(E_DEC_FATAL_ERROR, 0, false/*unsigned_flag*/, to); return to; From 820175115eff5db14ec0a610820a295fe225ecb6 Mon Sep 17 00:00:00 2001 From: qggcs Date: Sat, 13 Aug 2022 12:49:48 +0800 Subject: [PATCH 04/31] MDEV-29264: JSON function overflow error based on LONGTEXT field Analysis: The JSON functions(JSON_ARRAY[OBJECT|ARRAY_APPEND|ARRAY_INSERT|INSERT|SET|REPLACE]) result is truncated when the function is called based on LONGTEXT field. The overflow occurs when computing the result length due to the LONGTEXT max length is same as uint32 max length. It lead to wrong result length. Fix: Add static_cast to avoid uint32 overflow and fix the arguments used. --- mysql-test/main/func_json.result | 17 ++++++++++++++++- mysql-test/main/func_json.test | 11 +++++++++++ sql/item_jsonfunc.cc | 8 +++++--- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/mysql-test/main/func_json.result b/mysql-test/main/func_json.result index 73d32ceb13a..bfbdd99940e 100644 --- a/mysql-test/main/func_json.result +++ b/mysql-test/main/func_json.result @@ -822,7 +822,7 @@ CREATE TABLE t2 SELECT JSON_ARRAY_INSERT(fld, '$.[0]', '0') FROM t1; SHOW CREATE TABLE t2; Table Create Table t2 CREATE TABLE `t2` ( - `JSON_ARRAY_INSERT(fld, '$.[0]', '0')` varchar(25) DEFAULT NULL + `JSON_ARRAY_INSERT(fld, '$.[0]', '0')` varchar(21) DEFAULT NULL ) ENGINE=MyISAM DEFAULT CHARSET=latin1 DROP TABLE t1, t2; SET sql_mode=default; @@ -1437,5 +1437,20 @@ f DROP VIEW v; DROP TABLE t; # +# MDEV-29264 JSON functions overflow error based ON LONGTEXT field +# +CREATE TABLE t(l1 LONGTEXT, l2 LONGTEXT, l3 LONGTEXT, l4 LONGTEXT); +INSERT INTO t VALUES('k1', 'v1', 'k2', 'v2'); +SELECT JSON_ARRAY(l1, l2, l3, l4), JSON_OBJECT(l1, l2, l3, l4) from t; +JSON_ARRAY(l1, l2, l3, l4) JSON_OBJECT(l1, l2, l3, l4) +["k1", "v1", "k2", "v2"] {"k1": "v1", "k2": "v2"} +SELECT JSON_ARRAY_APPEND(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3'), JSON_ARRAY_INSERT(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3') from t; +JSON_ARRAY_APPEND(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3') JSON_ARRAY_INSERT(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3') +[["k1", "k3"], "v1", "k2", "v2"] ["k3", "k1", "v1", "k2", "v2"] +SELECT JSON_INSERT(JSON_OBJECT(l1, l2, l3, l4), '$.k3', 'v3'),JSON_SET(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2'),JSON_REPLACE(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2') from t; +JSON_INSERT(JSON_OBJECT(l1, l2, l3, l4), '$.k3', 'v3') JSON_SET(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2') JSON_REPLACE(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2') +{"k1": "v1", "k2": "v2", "k3": "v3"} {"k1": "v1", "k2": "new v2"} {"k1": "v1", "k2": "new v2"} +DROP TABLE t; +# # End of 10.5 tests # diff --git a/mysql-test/main/func_json.test b/mysql-test/main/func_json.test index 9a31aa98919..067266ee4a6 100644 --- a/mysql-test/main/func_json.test +++ b/mysql-test/main/func_json.test @@ -927,6 +927,17 @@ SELECT JSON_ARRAYAGG(a) AS f FROM v; DROP VIEW v; DROP TABLE t; + +--echo # +--echo # MDEV-29264 JSON functions overflow error based ON LONGTEXT field +--echo # +CREATE TABLE t(l1 LONGTEXT, l2 LONGTEXT, l3 LONGTEXT, l4 LONGTEXT); +INSERT INTO t VALUES('k1', 'v1', 'k2', 'v2'); +SELECT JSON_ARRAY(l1, l2, l3, l4), JSON_OBJECT(l1, l2, l3, l4) from t; +SELECT JSON_ARRAY_APPEND(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3'), JSON_ARRAY_INSERT(JSON_ARRAY(l1, l2, l3, l4), '$[0]', 'k3') from t; +SELECT JSON_INSERT(JSON_OBJECT(l1, l2, l3, l4), '$.k3', 'v3'),JSON_SET(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2'),JSON_REPLACE(JSON_OBJECT(l1, l2, l3, l4), '$.k2', 'new v2') from t; +DROP TABLE t; + --echo # --echo # End of 10.5 tests --echo # diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index 197a4b12907..8f5f1fa9637 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1738,7 +1738,7 @@ bool Item_func_json_array::fix_length_and_dec() return TRUE; for (n_arg=0 ; n_arg < arg_count ; n_arg++) - char_length+= args[n_arg]->max_char_length() + 4; + char_length+= static_cast(args[n_arg]->max_char_length()) + 4; fix_char_length_ulonglong(char_length); tmp_val.set_charset(collation.collation); @@ -1797,7 +1797,8 @@ bool Item_func_json_array_append::fix_length_and_dec() for (n_arg= 1; n_arg < arg_count; n_arg+= 2) { paths[n_arg/2].set_constant_flag(args[n_arg]->const_item()); - char_length+= args[n_arg/2+1]->max_char_length() + 4; + char_length+= + static_cast(args[n_arg+1]->max_char_length()) + 4; } fix_char_length_ulonglong(char_length); @@ -2959,7 +2960,8 @@ bool Item_func_json_insert::fix_length_and_dec() for (n_arg= 1; n_arg < arg_count; n_arg+= 2) { paths[n_arg/2].set_constant_flag(args[n_arg]->const_item()); - char_length+= args[n_arg/2+1]->max_char_length() + 4; + char_length+= + static_cast(args[n_arg+1]->max_char_length()) + 4; } fix_char_length_ulonglong(char_length); From 6d90d2ba7f86bf95823f74ca2da433193ae84b25 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sun, 14 Aug 2022 21:45:05 -0400 Subject: [PATCH 05/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index b229b7f95f7..92848892dc8 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=3 -MYSQL_VERSION_PATCH=36 +MYSQL_VERSION_PATCH=37 SERVER_MATURITY=stable From a4a42f50f05c0f48fdc85bf8891755c809622d01 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sun, 14 Aug 2022 22:19:37 -0400 Subject: [PATCH 06/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index eb444d84828..ec02fb54be8 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=4 -MYSQL_VERSION_PATCH=26 +MYSQL_VERSION_PATCH=27 SERVER_MATURITY=stable From fd1e5f91d273315d5f08c4f11abe3446793c3979 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sun, 14 Aug 2022 22:53:37 -0400 Subject: [PATCH 07/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 526e94ebc5d..41ccccdc33f 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=5 -MYSQL_VERSION_PATCH=17 +MYSQL_VERSION_PATCH=18 SERVER_MATURITY=stable From e78c2b846a99f23bd6a1903b31b24beeee4157db Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sun, 14 Aug 2022 23:38:24 -0400 Subject: [PATCH 08/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3cd7498d552..049090789eb 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=6 -MYSQL_VERSION_PATCH=9 +MYSQL_VERSION_PATCH=10 SERVER_MATURITY=stable From 58746728db1140679574ad7f724ee99ece4d87dd Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Sun, 14 Aug 2022 23:38:24 -0400 Subject: [PATCH 09/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 3cd7498d552..049090789eb 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=6 -MYSQL_VERSION_PATCH=9 +MYSQL_VERSION_PATCH=10 SERVER_MATURITY=stable From 14a99383e04f0c818c2666236e4f69e37ef8c540 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Mon, 15 Aug 2022 00:27:48 -0400 Subject: [PATCH 10/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 91f606347b6..67dda7b56f5 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=7 -MYSQL_VERSION_PATCH=5 +MYSQL_VERSION_PATCH=6 SERVER_MATURITY=stable From 1f93a30d04fc1759c7f43452634f7387d3eab690 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Mon, 15 Aug 2022 00:56:13 -0400 Subject: [PATCH 11/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 19a2bba13ac..1bee82378cb 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=8 -MYSQL_VERSION_PATCH=4 +MYSQL_VERSION_PATCH=5 SERVER_MATURITY=stable From 2447be665d405b256688450929a772c26d58e776 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomew Date: Mon, 15 Aug 2022 00:56:13 -0400 Subject: [PATCH 12/31] bump the VERSION --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 19a2bba13ac..1bee82378cb 100644 --- a/VERSION +++ b/VERSION @@ -1,4 +1,4 @@ MYSQL_VERSION_MAJOR=10 MYSQL_VERSION_MINOR=8 -MYSQL_VERSION_PATCH=4 +MYSQL_VERSION_PATCH=5 SERVER_MATURITY=stable From c2080060801342f46849423adf71515ae7a33e04 Mon Sep 17 00:00:00 2001 From: Nayuta Yanagisawa Date: Thu, 28 Jul 2022 21:24:57 +0900 Subject: [PATCH 13/31] MDEV-29008 Server crash or assertion `field' failed in spider_db_open_item_ident / group by handler ha_spider::field_exchange() returns NULL and that results in a crash or a assertion failure in spider_db_open_item_ident(). In the first place, there seems to be no need to call field_exchange() for printing an identity (column name with alias). Thus, we simply remove the call. --- .../spider/bugfix/r/mdev_29008.result | 38 ++++++++++++++++++ .../mysql-test/spider/bugfix/t/mdev_29008.cnf | 3 ++ .../spider/bugfix/t/mdev_29008.test | 39 +++++++++++++++++++ storage/spider/spd_db_conn.cc | 2 - 4 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 storage/spider/mysql-test/spider/bugfix/r/mdev_29008.result create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_29008.cnf create mode 100644 storage/spider/mysql-test/spider/bugfix/t/mdev_29008.test diff --git a/storage/spider/mysql-test/spider/bugfix/r/mdev_29008.result b/storage/spider/mysql-test/spider/bugfix/r/mdev_29008.result new file mode 100644 index 00000000000..66e33f42a7e --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/r/mdev_29008.result @@ -0,0 +1,38 @@ +# +# MDEV-29008 Server crash or assertion `field' failed in spider_db_open_item_ident / group by handler +# +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 +connection child2_1; +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +CREATE TABLE tbl_a ( +a INT, +b INT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +INSERT INTO tbl_a VALUES (1,2),(3,4); +connection master_1; +CREATE DATABASE auto_test_local; +USE auto_test_local; +CREATE TABLE tbl_a ( +a INT, +b INT +) ENGINE=Spider DEFAULT CHARSET=utf8 COMMENT='table "tbl_a", srv "s_2_1"'; +SELECT MIN(t2.a) AS f1, t1.b AS f2 FROM tbl_a AS t1 JOIN tbl_a AS t2 GROUP BY f2 ORDER BY f1, f2; +f1 f2 +1 2 +1 4 +connection master_1; +DROP DATABASE IF EXISTS auto_test_local; +connection child2_1; +DROP DATABASE IF EXISTS auto_test_remote; +for master_1 +for child2 +child2_1 +child2_2 +child2_3 +for child3 diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.cnf b/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.cnf new file mode 100644 index 00000000000..05dfd8a0bce --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.cnf @@ -0,0 +1,3 @@ +!include include/default_mysqld.cnf +!include ../my_1_1.cnf +!include ../my_2_1.cnf diff --git a/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.test b/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.test new file mode 100644 index 00000000000..28d9a9244e3 --- /dev/null +++ b/storage/spider/mysql-test/spider/bugfix/t/mdev_29008.test @@ -0,0 +1,39 @@ +--echo # +--echo # MDEV-29008 Server crash or assertion `field' failed in spider_db_open_item_ident / group by handler +--echo # + +--disable_query_log +--disable_result_log +--source ../../t/test_init.inc +--enable_result_log +--enable_query_log + +--connection child2_1 +CREATE DATABASE auto_test_remote; +USE auto_test_remote; +eval CREATE TABLE tbl_a ( + a INT, + b INT +) $CHILD2_1_ENGINE $CHILD2_1_CHARSET; +INSERT INTO tbl_a VALUES (1,2),(3,4); + +--connection master_1 +CREATE DATABASE auto_test_local; +USE auto_test_local; +eval CREATE TABLE tbl_a ( + a INT, + b INT +) $MASTER_1_ENGINE $MASTER_1_CHARSET COMMENT='table "tbl_a", srv "s_2_1"'; + +SELECT MIN(t2.a) AS f1, t1.b AS f2 FROM tbl_a AS t1 JOIN tbl_a AS t2 GROUP BY f2 ORDER BY f1, f2; + +--connection master_1 +DROP DATABASE IF EXISTS auto_test_local; +--connection child2_1 +DROP DATABASE IF EXISTS auto_test_remote; + +--disable_query_log +--disable_result_log +--source ../t/test_deinit.inc +--enable_query_log +--enable_result_log diff --git a/storage/spider/spd_db_conn.cc b/storage/spider/spd_db_conn.cc index 2569574561e..1aafa175a7b 100644 --- a/storage/spider/spd_db_conn.cc +++ b/storage/spider/spd_db_conn.cc @@ -9115,8 +9115,6 @@ int spider_db_open_item_ident( SPIDER_FIELD_HOLDER *field_holder = field_chain->field_holder; spider = field_holder->spider; share = spider->share; - field = spider->field_exchange(field); - DBUG_ASSERT(field); if ((error_num = share->dbton_share[dbton_id]-> append_column_name_with_alias(str, field->field_index, field_holder->alias->ptr(), field_holder->alias->length()))) From af552f2903b9764e3bc0634a0037c39d1a837f4f Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 16 Aug 2022 21:33:45 +0530 Subject: [PATCH 14/31] Disabling atomic.rename_trigger test case because of frequent failures --- mysql-test/suite/atomic/disabled.def | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 mysql-test/suite/atomic/disabled.def diff --git a/mysql-test/suite/atomic/disabled.def b/mysql-test/suite/atomic/disabled.def new file mode 100644 index 00000000000..6d8defe344a --- /dev/null +++ b/mysql-test/suite/atomic/disabled.def @@ -0,0 +1,13 @@ +############################################################################## +# +# List the test cases that are to be disabled temporarily. +# +# Separate the test case name and the comment with ':'. +# +# : BUG# +# +# Do not use any TAB characters for whitespace. +# +############################################################################## + +rename_trigger : MDEV-29282 atomic.rename_trigger fails occasionly From 32167225c728c19ca478bce0529d549a8d056f87 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 16 Aug 2022 17:34:38 +0530 Subject: [PATCH 15/31] MDEV-13013 InnoDB unnecessarily extends data files - While creating a new InnoDB segment, allocates the extent before allocating the inode or page allocation even though the pages are present in fragment segment. This patch does reserve the extent when InnoDB ran out of fragment pages in the tablespace. --- .../innodb/r/check_ibd_filesize,32k.rdiff | 12 +++--- .../innodb/r/check_ibd_filesize,4k.rdiff | 12 +++--- .../innodb/r/check_ibd_filesize,64k.rdiff | 12 +++--- .../innodb/r/check_ibd_filesize,8k.rdiff | 12 +++--- .../suite/innodb/r/check_ibd_filesize.result | 4 +- .../suite/innodb/t/temporary_table.test | 1 + .../mariabackup/log_page_corruption.result | 24 ++++++------ storage/innobase/fsp/fsp0fsp.cc | 38 +++++++++++++------ 8 files changed, 66 insertions(+), 49 deletions(-) diff --git a/mysql-test/suite/innodb/r/check_ibd_filesize,32k.rdiff b/mysql-test/suite/innodb/r/check_ibd_filesize,32k.rdiff index 6cf0fdf4159..44446602b9f 100644 --- a/mysql-test/suite/innodb/r/check_ibd_filesize,32k.rdiff +++ b/mysql-test/suite/innodb/r/check_ibd_filesize,32k.rdiff @@ -1,18 +1,18 @@ ---- check_ibd_filesize.result -+++ check_ibd_filesize.result,32k +--- mysql-test/suite/innodb/r/check_ibd_filesize.result 2022-08-16 17:28:06.462350465 +0530 ++++ mysql-test/suite/innodb/r/check_ibd_filesize.reject 2022-08-16 17:29:25.129637040 +0530 @@ -3,18 +3,12 @@ # SPACE IN 5.7 THAN IN 5.6 # CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 196608 +-# bytes: 65536 ++# bytes: 131072 INSERT INTO t1 SELECT * FROM seq_1_to_25000; -# bytes: 9437184 +# bytes: 786432 DROP TABLE t1; CREATE TABLE t1 (a INT PRIMARY KEY, b BLOB) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 196608 +-# bytes: 65536 ++# bytes: 131072 INSERT INTO t1 SELECT seq,REPEAT('a',30000) FROM seq_1_to_20; -# bytes: 4194304 -DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/check_ibd_filesize,4k.rdiff b/mysql-test/suite/innodb/r/check_ibd_filesize,4k.rdiff index 52cd6832755..ef55ad971fe 100644 --- a/mysql-test/suite/innodb/r/check_ibd_filesize,4k.rdiff +++ b/mysql-test/suite/innodb/r/check_ibd_filesize,4k.rdiff @@ -1,17 +1,17 @@ ---- check_ibd_filesize.result -+++ check_ibd_filesize.result,4k +--- mysql-test/suite/innodb/r/check_ibd_filesize.result 2022-08-16 17:28:06.462350465 +0530 ++++ mysql-test/suite/innodb/r/check_ibd_filesize.reject 2022-08-16 17:31:39.288769153 +0530 @@ -3,18 +3,18 @@ # SPACE IN 5.7 THAN IN 5.6 # CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 24576 +-# bytes: 65536 ++# bytes: 16384 INSERT INTO t1 SELECT * FROM seq_1_to_25000; # bytes: 9437184 DROP TABLE t1; CREATE TABLE t1 (a INT PRIMARY KEY, b BLOB) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 24576 +-# bytes: 65536 ++# bytes: 16384 INSERT INTO t1 SELECT seq,REPEAT('a',30000) FROM seq_1_to_20; # bytes: 4194304 DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/check_ibd_filesize,64k.rdiff b/mysql-test/suite/innodb/r/check_ibd_filesize,64k.rdiff index 23d9fbe608f..bcdcea31160 100644 --- a/mysql-test/suite/innodb/r/check_ibd_filesize,64k.rdiff +++ b/mysql-test/suite/innodb/r/check_ibd_filesize,64k.rdiff @@ -1,18 +1,18 @@ ---- check_ibd_filesize.result -+++ check_ibd_filesize.result,64k +--- mysql-test/suite/innodb/r/check_ibd_filesize.result 2022-08-16 17:28:06.462350465 +0530 ++++ mysql-test/suite/innodb/r/check_ibd_filesize.reject 2022-08-16 17:30:28.957174270 +0530 @@ -3,18 +3,12 @@ # SPACE IN 5.7 THAN IN 5.6 # CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 393216 +-# bytes: 65536 ++# bytes: 262144 INSERT INTO t1 SELECT * FROM seq_1_to_25000; -# bytes: 9437184 +# bytes: 983040 DROP TABLE t1; CREATE TABLE t1 (a INT PRIMARY KEY, b BLOB) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 393216 +-# bytes: 65536 ++# bytes: 262144 INSERT INTO t1 SELECT seq,REPEAT('a',30000) FROM seq_1_to_20; -# bytes: 4194304 -DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/check_ibd_filesize,8k.rdiff b/mysql-test/suite/innodb/r/check_ibd_filesize,8k.rdiff index 17936a3def9..7b699ef4cea 100644 --- a/mysql-test/suite/innodb/r/check_ibd_filesize,8k.rdiff +++ b/mysql-test/suite/innodb/r/check_ibd_filesize,8k.rdiff @@ -1,17 +1,17 @@ ---- check_ibd_filesize.result -+++ check_ibd_filesize.result,8k +--- mysql-test/suite/innodb/r/check_ibd_filesize.result 2022-08-16 17:28:06.462350465 +0530 ++++ mysql-test/suite/innodb/r/check_ibd_filesize.reject 2022-08-16 17:31:03.516962339 +0530 @@ -3,18 +3,18 @@ # SPACE IN 5.7 THAN IN 5.6 # CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 49152 +-# bytes: 65536 ++# bytes: 32768 INSERT INTO t1 SELECT * FROM seq_1_to_25000; # bytes: 9437184 DROP TABLE t1; CREATE TABLE t1 (a INT PRIMARY KEY, b BLOB) ENGINE=InnoDB; --# bytes: 98304 -+# bytes: 49152 +-# bytes: 65536 ++# bytes: 32768 INSERT INTO t1 SELECT seq,REPEAT('a',30000) FROM seq_1_to_20; # bytes: 4194304 DROP TABLE t1; diff --git a/mysql-test/suite/innodb/r/check_ibd_filesize.result b/mysql-test/suite/innodb/r/check_ibd_filesize.result index a6f5fbd9387..0d224d6ac5f 100644 --- a/mysql-test/suite/innodb/r/check_ibd_filesize.result +++ b/mysql-test/suite/innodb/r/check_ibd_filesize.result @@ -3,12 +3,12 @@ # SPACE IN 5.7 THAN IN 5.6 # CREATE TABLE t1 (a INT PRIMARY KEY) ENGINE=InnoDB; -# bytes: 98304 +# bytes: 65536 INSERT INTO t1 SELECT * FROM seq_1_to_25000; # bytes: 9437184 DROP TABLE t1; CREATE TABLE t1 (a INT PRIMARY KEY, b BLOB) ENGINE=InnoDB; -# bytes: 98304 +# bytes: 65536 INSERT INTO t1 SELECT seq,REPEAT('a',30000) FROM seq_1_to_20; # bytes: 4194304 DROP TABLE t1; diff --git a/mysql-test/suite/innodb/t/temporary_table.test b/mysql-test/suite/innodb/t/temporary_table.test index 5f64eb7d22b..cc290b03c34 100644 --- a/mysql-test/suite/innodb/t/temporary_table.test +++ b/mysql-test/suite/innodb/t/temporary_table.test @@ -5,6 +5,7 @@ # --source include/have_innodb.inc +--source include/innodb_page_size.inc # Embedded server does not restart of server --source include/not_embedded.inc --source include/no_valgrind_without_big.inc diff --git a/mysql-test/suite/mariabackup/log_page_corruption.result b/mysql-test/suite/mariabackup/log_page_corruption.result index 01ad4422c6a..b1dc6c8fd5f 100644 --- a/mysql-test/suite/mariabackup/log_page_corruption.result +++ b/mysql-test/suite/mariabackup/log_page_corruption.result @@ -30,13 +30,13 @@ FOUND 1 /Database page corruption detected.*/ in backup.log FOUND 1 /completed OK!/ in backup.log --- "innodb_corrupted_pages" file content: --- test/t1_corrupted -6 8 9 +4 6 7 test/t2_corrupted -7 8 10 +5 6 8 test/t4_corrupted_new 1 test/t5_corrupted_to_rename_renamed -6 +4 test/t7_corrupted_to_alter 3 ------ @@ -46,19 +46,19 @@ INSERT INTO t3_inc VALUES (3), (4), (5), (6), (7), (8), (9); # Backup must not fail, but "innodb_corrupted_pages" file must be created due to --log-innodb-page-corruption option --- "innodb_corrupted_pages" file content: --- test/t1_corrupted -6 8 9 +4 6 7 test/t1_inc_corrupted -6 8 9 +4 6 7 test/t2_corrupted -7 8 10 +5 6 8 test/t2_inc_corrupted -7 8 10 +5 6 8 test/t4_inc_corrupted_new 1 test/t5_corrupted_to_rename_renamed -6 +4 test/t5_inc_corrupted_to_rename_renamed -6 +4 test/t7_inc_corrupted_to_alter 3 ------ @@ -82,15 +82,15 @@ DROP TABLE t7_inc_corrupted_to_alter; # Full backup with --log-innodb-page-corruption --- "innodb_corrupted_pages" file content: --- test/t3 -6 8 +4 6 ------ # Extend some tablespace and corrupt extended pages for incremental backup # Incremental backup --log-innodb-page-corruption --- "innodb_corrupted_pages" file content: --- test/t3 -6 8 +4 6 test/t3_inc -6 8 +4 6 ------ # Full backup prepare # "innodb_corrupted_pages" file must not exist after successful prepare diff --git a/storage/innobase/fsp/fsp0fsp.cc b/storage/innobase/fsp/fsp0fsp.cc index ab143a16417..ba50483cee4 100644 --- a/storage/innobase/fsp/fsp0fsp.cc +++ b/storage/innobase/fsp/fsp0fsp.cc @@ -1886,6 +1886,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, fseg_header_t* header = 0; /* remove warning */ ulint n_reserved; ulint i; + bool reserved_extent = false; DBUG_ENTER("fseg_create"); @@ -1909,17 +1910,34 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, fil_block_check_type(*block, type, mtr); } - if (!has_done_reservation - && !fsp_reserve_free_extents(&n_reserved, space, 2, - FSP_NORMAL, mtr)) { - DBUG_RETURN(NULL); - } - space_header = fsp_get_space_header(space, page_size, mtr); +inode_alloc: inode = fsp_alloc_seg_inode(space, space_header, mtr); if (inode == NULL) { +reserve_extent: + if (!has_done_reservation && !reserved_extent) { + + if (!fsp_reserve_free_extents( + &n_reserved, space, 2, + FSP_NORMAL, mtr)) { + DBUG_RETURN(NULL); + } + + /* Extents reserved successfully. So + try allocating the page or inode */ + reserved_extent = true; + if (inode) { + goto page_alloc; + } + + goto inode_alloc; + } + + if (inode) { + fsp_free_seg_inode(space, page_size, inode, mtr); + } goto funct_exit; } @@ -1944,6 +1962,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, } if (!block) { +page_alloc: block = fseg_alloc_free_page_low(space, page_size, inode, 0, FSP_UP, mtr, mtr @@ -1957,10 +1976,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, ut_ad(!has_done_reservation || block != NULL); if (block == NULL) { - - fsp_free_seg_inode(space, page_size, inode, mtr); - - goto funct_exit; + goto reserve_extent; } ut_ad(rw_lock_get_x_lock_count(&block->lock) == 1); @@ -1980,7 +1996,7 @@ fseg_create(fil_space_t *space, ulint byte_offset, mtr_t *mtr, mlog_write_ulint(header + FSEG_HDR_SPACE, space->id, MLOG_4BYTES, mtr); funct_exit: - if (!has_done_reservation) { + if (!has_done_reservation && reserved_extent) { space->release_free_extents(n_reserved); } From ec37906646edf4d40ab920c9f2ae144c686221cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 18 Aug 2022 16:02:13 +0300 Subject: [PATCH 16/31] MDEV-29321 Percona XtraDB 5.7 can't be upgrade to MariaDB 10.6 or above In MySQL 5.7, rollback segments 1 to 32 are used for temporary tables, which is an unnecessary file format change from MySQL 5.6. This format change was avoided in MariaDB Server by commit 124bae082bf17e9af1fc77f78bbebd019635be5c (MDEV-12289). An upgrade from MySQL 5.7 would crash due to dereferencing a null pointer, which is a regression due to commit 0b47c126e31cddda1e94588799599e138400bcf8 (MDEV-13542). trx_rseg_t::get(): Return nullptr if no tablespace exists. This is where the upgrade would crash. trx_rseg_mem_restore(): Return DB_TABLESPACE_NOT_FOUND if the undo tablespace does not exist. This is likely dead code. --- storage/innobase/trx/trx0rseg.cc | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/innobase/trx/trx0rseg.cc b/storage/innobase/trx/trx0rseg.cc index 34ec21d6013..760c4e707ce 100644 --- a/storage/innobase/trx/trx0rseg.cc +++ b/storage/innobase/trx/trx0rseg.cc @@ -290,6 +290,11 @@ bool trx_rseg_read_wsrep_checkpoint(XID& xid) buf_block_t *trx_rseg_t::get(mtr_t *mtr, dberr_t *err) const { + if (!space) + { + if (err) *err= DB_TABLESPACE_NOT_FOUND; + return nullptr; + } return buf_page_get_gen(page_id(), 0, RW_X_LATCH, nullptr, BUF_GET, mtr, err); } @@ -435,6 +440,8 @@ static dberr_t trx_undo_lists_init(trx_rseg_t *rseg, trx_id_t &max_trx_id, static dberr_t trx_rseg_mem_restore(trx_rseg_t *rseg, trx_id_t &max_trx_id, mtr_t *mtr) { + if (!rseg->space) + return DB_TABLESPACE_NOT_FOUND; dberr_t err; const buf_block_t *rseg_hdr= buf_page_get_gen(rseg->page_id(), 0, RW_S_LATCH, nullptr, BUF_GET, mtr, From a1055ab35d29437b717e83b1a388eaa02901c42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 19 Aug 2022 09:18:24 +0300 Subject: [PATCH 17/31] MDEV-29043 mariabackup --compress hangs Even though commit b817afaa1c148437e1016d1981f138d0c46ccbc8 passed the test mariabackup.compress_qpress, that test turned out to be too small to reveal one more problem that had previously been prevented by the existence of ctrl_mutex. I did not realize that there can be multiple concurrent callers to compress_write(). One of them is the log copying thread; further callers are data file copying threads (default: --parallel=1). By default, there is only one compression worker thread (--compress-threads=1). compress_write(): Fix a race condition between threads that would use the same worker thread object. Make thd->data_avail contain the thread identifier of the submitter, and add thd->avail_cond to notify other compress_write() threads that are waiting for a slot. --- extra/mariabackup/ds_compress.cc | 51 +++++++++++++++++++++++++++----- 1 file changed, 43 insertions(+), 8 deletions(-) diff --git a/extra/mariabackup/ds_compress.cc b/extra/mariabackup/ds_compress.cc index dc1140edfee..a89e676190d 100644 --- a/extra/mariabackup/ds_compress.cc +++ b/extra/mariabackup/ds_compress.cc @@ -34,9 +34,10 @@ typedef struct { pthread_t id; uint num; pthread_mutex_t data_mutex; + pthread_cond_t avail_cond; pthread_cond_t data_cond; pthread_cond_t done_cond; - my_bool data_avail; + pthread_t data_avail; my_bool cancelled; const char *from; size_t from_len; @@ -197,9 +198,13 @@ compress_write(ds_file_t *file, const uchar *buf, size_t len) threads = comp_ctxt->threads; nthreads = comp_ctxt->nthreads; + const pthread_t self = pthread_self(); + ptr = (const char *) buf; while (len > 0) { - uint max_thread; + bool wait = nthreads == 1; +retry: + bool submitted = false; /* Send data to worker threads for compression */ for (i = 0; i < nthreads; i++) { @@ -208,16 +213,33 @@ compress_write(ds_file_t *file, const uchar *buf, size_t len) thd = threads + i; pthread_mutex_lock(&thd->data_mutex); + if (thd->data_avail == pthread_t(~0UL)) { + } else if (!wait) { +skip: + pthread_mutex_unlock(&thd->data_mutex); + continue; + } else { + for (;;) { + pthread_cond_wait(&thd->avail_cond, + &thd->data_mutex); + if (thd->data_avail + == pthread_t(~0UL)) { + break; + } + goto skip; + } + } chunk_len = (len > COMPRESS_CHUNK_SIZE) ? COMPRESS_CHUNK_SIZE : len; thd->from = ptr; thd->from_len = chunk_len; - thd->data_avail = TRUE; + thd->data_avail = self; pthread_cond_signal(&thd->data_cond); pthread_mutex_unlock(&thd->data_mutex); + submitted = true; len -= chunk_len; if (len == 0) { break; @@ -225,13 +247,20 @@ compress_write(ds_file_t *file, const uchar *buf, size_t len) ptr += chunk_len; } - max_thread = (i < nthreads) ? i : nthreads - 1; + if (!submitted) { + wait = true; + goto retry; + } - /* Reap and stream the compressed data */ - for (i = 0; i <= max_thread; i++) { + for (i = 0; i < nthreads; i++) { thd = threads + i; pthread_mutex_lock(&thd->data_mutex); + if (thd->data_avail != self) { + pthread_mutex_unlock(&thd->data_mutex); + continue; + } + while (!thd->to_len) { pthread_cond_wait(&thd->done_cond, &thd->data_mutex); @@ -249,6 +278,8 @@ compress_write(ds_file_t *file, const uchar *buf, size_t len) } thd->to_len = 0; + thd->data_avail = pthread_t(~0UL); + pthread_cond_signal(&thd->avail_cond); pthread_mutex_unlock(&thd->data_mutex); if (fail) { @@ -336,6 +367,7 @@ destroy_worker_thread(comp_thread_ctxt_t *thd) pthread_join(thd->id, NULL); + pthread_cond_destroy(&thd->avail_cond); pthread_cond_destroy(&thd->data_cond); pthread_cond_destroy(&thd->done_cond); pthread_mutex_destroy(&thd->data_mutex); @@ -363,11 +395,14 @@ create_worker_threads(uint n) /* Initialize and data mutex and condition var */ if (pthread_mutex_init(&thd->data_mutex, NULL) || + pthread_cond_init(&thd->avail_cond, NULL) || pthread_cond_init(&thd->data_cond, NULL) || pthread_cond_init(&thd->done_cond, NULL)) { goto err; } + thd->data_avail = pthread_t(~0UL); + if (pthread_create(&thd->id, NULL, compress_worker_thread_func, thd)) { msg("compress: pthread_create() failed: " @@ -409,13 +444,13 @@ compress_worker_thread_func(void *arg) pthread_mutex_lock(&thd->data_mutex); while (1) { - while (!thd->data_avail && !thd->cancelled) { + while (!thd->cancelled + && (thd->to_len || thd->data_avail == pthread_t(~0UL))) { pthread_cond_wait(&thd->data_cond, &thd->data_mutex); } if (thd->cancelled) break; - thd->data_avail = FALSE; thd->to_len = qlz_compress(thd->from, thd->to, thd->from_len, &thd->state); From c2df3d30c065d962d4d40f94aca42b527f647bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 18 Aug 2022 17:12:00 +0300 Subject: [PATCH 18/31] MDEV-21452 fixup: Avoid an unnecessary mutex operation --- extra/mariabackup/xtrabackup.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/mariabackup/xtrabackup.cc b/extra/mariabackup/xtrabackup.cc index 1dd52e6bce0..62a619587e3 100644 --- a/extra/mariabackup/xtrabackup.cc +++ b/extra/mariabackup/xtrabackup.cc @@ -2477,11 +2477,11 @@ xb_write_delta_metadata(const char *filename, const xb_delta_info_t *info) /* ================= backup ================= */ void xtrabackup_io_throttling() { - if (!xtrabackup_backup) + if (!xtrabackup_backup || !xtrabackup_throttle) return; mysql_mutex_lock(&log_sys.mutex); - if (xtrabackup_throttle && (io_ticket--) < 0) + if (io_ticket-- < 0) mysql_cond_wait(&wait_throttle, &log_sys.mutex); mysql_mutex_unlock(&log_sys.mutex); } From 75c416d3627650a5b43c70a8150292990206e3e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Fri, 19 Aug 2022 09:26:13 +0300 Subject: [PATCH 19/31] MDEV-24626 fixup: mariabackup.compress_qpress --- mysql-test/suite/mariabackup/compress_qpress.test | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/mysql-test/suite/mariabackup/compress_qpress.test b/mysql-test/suite/mariabackup/compress_qpress.test index f86efe44e5d..c7762f8e55e 100644 --- a/mysql-test/suite/mariabackup/compress_qpress.test +++ b/mysql-test/suite/mariabackup/compress_qpress.test @@ -12,6 +12,10 @@ INSERT INTO t VALUES(2); echo # xtrabackup prepare; --disable_result_log +# Because MDEV-24626 in 10.6 optimized file creation, we could end up with +# t.new.qp instead of t.ibd.qp unless a log checkpoint happened to be +# triggered between CREATE TABLE and the backup run. +--replace_result t.new t.ibd list_files $targetdir/test *.qp; exec $XTRABACKUP --decompress --remove-original --target-dir=$targetdir; list_files $targetdir/test *.qp; From 7624bf868ed0d08276cb27c5cdbd007faf4de3c4 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Sat, 6 Aug 2022 01:16:21 +0530 Subject: [PATCH 20/31] MDEV-29250 InnoDB: Failing assertion: table->get_ref_count() == 0 Reason: ====== This issue is caused by race condition between fulltext DDL and purge thread. DDL sets the signal to stop the purge thread to process the new undo log records and wait for the ongoing processed FTS table undo log records to finish. But in dict_acquire_mdl_shared(),InnoDB release all innodb table related locks before acquiring the mdl. At the same time, DDL assumes that there are no purge threads working on fts table. There is a possiblity that purge thread can skip processing the valid undo log records if it checks purge_sys.must_wait_FTS() twice in different places. Solution: ========== Add the purge_sys.must_wait_FTS() check in dict_acquire_mdl_shared() to avoid the purge thread processing undo log records. dict_open_table_on_id(): return -1 if the purge thread has to wait dict_acquire_mdl_shared(): Added 1 new parameters to indicate that purge thread invoking the function, return -1 if the purge thread has to wait. --- storage/innobase/dict/dict0dict.cc | 31 +++++++++++++++++++++------- storage/innobase/include/dict0dict.h | 2 +- storage/innobase/row/row0purge.cc | 3 ++- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/storage/innobase/dict/dict0dict.cc b/storage/innobase/dict/dict0dict.cc index 52a7d5a4b36..ffb76de4f7b 100644 --- a/storage/innobase/dict/dict0dict.cc +++ b/storage/innobase/dict/dict0dict.cc @@ -662,7 +662,7 @@ dict_table_t::parse_name<>(char(&)[NAME_LEN + 1], char(&)[NAME_LEN + 1], @param[in] table_op operation to perform when opening @return table object after locking MDL shared @retval nullptr if the table is not readable, or if trylock && MDL blocked */ -template +template dict_table_t* dict_acquire_mdl_shared(dict_table_t *table, THD *thd, @@ -674,9 +674,11 @@ dict_acquire_mdl_shared(dict_table_t *table, MDL_context *mdl_context= static_cast(thd_mdl_context(thd)); size_t db_len; + dict_table_t *not_found= nullptr; if (trylock) { + static_assert(!trylock || !purge_thd, "usage"); dict_sys.freeze(SRW_LOCK_CALL); db_len= dict_get_db_name_len(table->name.m_name); dict_sys.unfreeze(); @@ -748,7 +750,13 @@ retry: } } +retry_table_open: dict_sys.freeze(SRW_LOCK_CALL); + if (purge_thd && purge_sys.must_wait_FTS()) + { + not_found= reinterpret_cast(-1); + goto return_without_mdl; + } table= dict_sys.find_table(table_id); if (table) table->acquire(); @@ -756,6 +764,11 @@ retry: { dict_sys.unfreeze(); dict_sys.lock(SRW_LOCK_CALL); + if (purge_thd && purge_sys.must_wait_FTS()) + { + dict_sys.unlock(); + goto retry_table_open; + } table= dict_load_table_on_id(table_id, table_op == DICT_TABLE_OP_LOAD_TABLESPACE ? DICT_ERR_IGNORE_RECOVER_LOCK @@ -777,7 +790,7 @@ return_without_mdl: mdl_context->release_lock(*mdl); *mdl= nullptr; } - return nullptr; + return not_found; } size_t db1_len, tbl1_len; @@ -814,9 +827,9 @@ return_without_mdl: goto retry; } -template dict_table_t* dict_acquire_mdl_shared +template dict_table_t* dict_acquire_mdl_shared (dict_table_t*,THD*,MDL_ticket**,dict_table_op_t); -template dict_table_t* dict_acquire_mdl_shared +template dict_table_t* dict_acquire_mdl_shared (dict_table_t*,THD*,MDL_ticket**,dict_table_op_t); /** Look up a table by numeric identifier. @@ -842,13 +855,14 @@ dict_table_open_on_id(table_id_t table_id, bool dict_locked, { if (purge_thd && purge_sys.must_wait_FTS()) { - table= nullptr; + table= reinterpret_cast(-1); goto func_exit; } table->acquire(); if (thd && !dict_locked) - table= dict_acquire_mdl_shared(table, thd, mdl, table_op); + table= dict_acquire_mdl_shared( + table, thd, mdl, table_op); } else if (table_op != DICT_TABLE_OP_OPEN_ONLY_IF_CACHED) { @@ -866,7 +880,7 @@ dict_table_open_on_id(table_id_t table_id, bool dict_locked, if (purge_thd && purge_sys.must_wait_FTS()) { dict_sys.unlock(); - return nullptr; + return reinterpret_cast(-1); } table->acquire(); } @@ -876,7 +890,8 @@ dict_table_open_on_id(table_id_t table_id, bool dict_locked, if (table && thd) { dict_sys.freeze(SRW_LOCK_CALL); - table= dict_acquire_mdl_shared(table, thd, mdl, table_op); + table= dict_acquire_mdl_shared( + table, thd, mdl, table_op); dict_sys.unfreeze(); } return table; diff --git a/storage/innobase/include/dict0dict.h b/storage/innobase/include/dict0dict.h index 29673f5bc95..cfaf4fab83e 100644 --- a/storage/innobase/include/dict0dict.h +++ b/storage/innobase/include/dict0dict.h @@ -132,7 +132,7 @@ enum dict_table_op_t { @param[in] table_op operation to perform when opening @return table object after locking MDL shared @retval NULL if the table is not readable, or if trylock && MDL blocked */ -template +template dict_table_t* dict_acquire_mdl_shared(dict_table_t *table, THD *thd, diff --git a/storage/innobase/row/row0purge.cc b/storage/innobase/row/row0purge.cc index 47625b91f35..8bbb0a36144 100644 --- a/storage/innobase/row/row0purge.cc +++ b/storage/innobase/row/row0purge.cc @@ -941,7 +941,8 @@ try_again: table_id, false, DICT_TABLE_OP_NORMAL, node->purge_thd, &node->mdl_ticket); - if (!node->table && purge_sys.must_wait_FTS()) { + if (node->table == reinterpret_cast(-1)) { + /* purge stop signal */ goto try_again; } From f02ca429f70c16be2b2e3d5671d9990cd3d474b6 Mon Sep 17 00:00:00 2001 From: Brad Smith Date: Sun, 21 Aug 2022 00:34:41 -0400 Subject: [PATCH 21/31] Revert aligned_alloc() addition from MDEV-28836 As pointed out with MDEV-29308 there are issues with the code as is. MariaDB is built as C++11 / C99. aligned_alloc() is not guarenteed to be exposed when building with any mode other than C++17 / C11. The other *BSD's have their stdlib.h header to expose the function with C+11 anyway, but the issue exists in the C99 code too, the build just does not use -Werror. Linux globally defines _GNU_SOURCE hiding the issue as well. --- cmake/os/WindowsCache.cmake | 1 - config.h.cmake | 1 - configure.cmake | 8 -------- include/aligned.h | 5 +---- storage/innobase/buf/buf0flu.cc | 3 --- 5 files changed, 1 insertion(+), 17 deletions(-) diff --git a/cmake/os/WindowsCache.cmake b/cmake/os/WindowsCache.cmake index 923ec371609..a5f808c8d2c 100644 --- a/cmake/os/WindowsCache.cmake +++ b/cmake/os/WindowsCache.cmake @@ -23,7 +23,6 @@ IF(MSVC) SET(BFD_H_EXISTS 0 CACHE INTERNAL "") SET(HAVE_ACCESS 1 CACHE INTERNAL "") SET(HAVE_ALARM CACHE INTERNAL "") -SET(HAVE_ALIGNED_ALLOC CACHE INTERNAL "") SET(HAVE_ALLOCA_H CACHE INTERNAL "") SET(HAVE_ARPA_INET_H CACHE INTERNAL "") SET(HAVE_BACKTRACE CACHE INTERNAL "") diff --git a/config.h.cmake b/config.h.cmake index 4ff2f2fbf12..568e0868d12 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -19,7 +19,6 @@ /* Headers we may want to use. */ #cmakedefine STDC_HEADERS 1 #cmakedefine _GNU_SOURCE 1 -#cmakedefine HAVE_ALIGNED_ALLOC 1 #cmakedefine HAVE_ALLOCA_H 1 #cmakedefine HAVE_ARPA_INET_H 1 #cmakedefine HAVE_ASM_TERMBITS_H 1 diff --git a/configure.cmake b/configure.cmake index a3f607e6bcf..5b49a065b5a 100644 --- a/configure.cmake +++ b/configure.cmake @@ -326,14 +326,6 @@ ENDIF() CHECK_FUNCTION_EXISTS (accept4 HAVE_ACCEPT4) CHECK_FUNCTION_EXISTS (access HAVE_ACCESS) CHECK_FUNCTION_EXISTS (alarm HAVE_ALARM) -IF (CMAKE_SYSTEM_NAME MATCHES "Linux" AND NOT WITH_ASAN) - # When an old custom memory allocator library is used, aligned_alloc() - # could invoke the built-in allocator in libc, not matching - # the overriden free() in the custom memory allocator. - SET(HAVE_ALIGNED_ALLOC 0) -ELSE() - CHECK_FUNCTION_EXISTS (aligned_alloc HAVE_ALIGNED_ALLOC) -ENDIF() SET(HAVE_ALLOCA 1) CHECK_FUNCTION_EXISTS (backtrace HAVE_BACKTRACE) CHECK_FUNCTION_EXISTS (backtrace_symbols HAVE_BACKTRACE_SYMBOLS) diff --git a/include/aligned.h b/include/aligned.h index 0ae1f0d0848..81bd5d3f6f7 100644 --- a/include/aligned.h +++ b/include/aligned.h @@ -14,8 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA */ -#ifdef HAVE_ALIGNED_ALLOC -#elif defined __linux__ +#if defined __linux__ # include #endif @@ -23,8 +22,6 @@ inline void *aligned_malloc(size_t size, size_t alignment) { #ifdef _WIN32 return _aligned_malloc(size, alignment); -#elif defined HAVE_ALIGNED_ALLOC - return aligned_alloc(alignment, size); #elif defined __linux__ return memalign(alignment, size); #else diff --git a/storage/innobase/buf/buf0flu.cc b/storage/innobase/buf/buf0flu.cc index a2ec96183b6..1e6c441bb76 100644 --- a/storage/innobase/buf/buf0flu.cc +++ b/storage/innobase/buf/buf0flu.cc @@ -579,9 +579,6 @@ static void buf_tmp_reserve_compression_buf(buf_tmp_buffer_t* slot) size= size + LZO1X_1_15_MEM_COMPRESS; #elif defined HAVE_SNAPPY size= snappy_max_compressed_length(size); -#endif -#if defined HAVE_ALIGNED_ALLOC && (defined HAVE_LZO || defined HAVE_SNAPPY) - size= MY_ALIGN(size, srv_page_size); #endif slot->comp_buf= static_cast(aligned_malloc(size, srv_page_size)); } From fd0cd4801a00c61adb5ab6cbefcf360467a02f20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Aug 2022 12:32:47 +0300 Subject: [PATCH 22/31] MDEV-13013 fixup: Adjust a test --- .../innodb_zip/r/wl6347_comp_indx_stat.result | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result b/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result index bc766135e56..c48bc32d596 100644 --- a/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result +++ b/mysql-test/suite/innodb_zip/r/wl6347_comp_indx_stat.result @@ -1022,7 +1022,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 122880 +The size of the tab5.ibd file: 106496 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -1329,7 +1329,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 245760 +The size of the tab5.ibd file: 212992 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -2633,7 +2633,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 122880 +The size of the tab5.ibd file: 106496 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -2942,7 +2942,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 245760 +The size of the tab5.ibd file: 212992 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -4147,7 +4147,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 122880 +The size of the tab5.ibd file: 106496 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -4435,7 +4435,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 245760 +The size of the tab5.ibd file: 212992 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -5692,7 +5692,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 122880 +The size of the tab5.ibd file: 106496 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -5999,7 +5999,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 245760 +The size of the tab5.ibd file: 212992 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -7282,7 +7282,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 122880 +The size of the tab5.ibd file: 106496 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data @@ -7589,7 +7589,7 @@ AND compress_ops BETWEEN @inl_val AND 1000 AND table_name='tab5' AND database_name='test' AND index_name like 'idx%' ; compress_stat 1 -The size of the tab5.ibd file: 245760 +The size of the tab5.ibd file: 212992 # for determintic resons simple data should be inserted. # insert some 100 records # Load the data From 6005f3c548d312f48b575961889194a0a4767ae1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Mon, 22 Aug 2022 12:33:46 +0300 Subject: [PATCH 23/31] MDEV-25257 follow-up: Fix a test Ever since commit 09177eadc39ae1e777ad473970456cb9dd9c3993 the test innodb.row_format_redundant cannot work when the data file was created with innodb_checksum_algorithm=full_crc32. Backport of 10.5 commit a9d0bb12e68a11c119a3534f354c7a7ef13dd5b5 --- mysql-test/suite/innodb/t/row_format_redundant.opt | 1 + 1 file changed, 1 insertion(+) create mode 100644 mysql-test/suite/innodb/t/row_format_redundant.opt diff --git a/mysql-test/suite/innodb/t/row_format_redundant.opt b/mysql-test/suite/innodb/t/row_format_redundant.opt new file mode 100644 index 00000000000..c44c611ed60 --- /dev/null +++ b/mysql-test/suite/innodb/t/row_format_redundant.opt @@ -0,0 +1 @@ +--innodb-checksum-algorithm=crc32 From 316847eab72022cd11351ea1bfa91234bee75839 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Mon, 22 Aug 2022 11:50:15 +0400 Subject: [PATCH 24/31] MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result TIMESTAMP columns were compared as strings in ALL/ANY comparison, which did not work well near DST time change. Changing ALL/ANY comparison to use "Native" representation to compare TIMESTAMP columns, like simple comparison does. --- mysql-test/main/timezone2.result | 22 +++++++++ mysql-test/main/timezone2.test | 15 ++++++ sql/sql_class.cc | 80 ++++++++++++++++++++++---------- sql/sql_class.h | 2 + 4 files changed, 95 insertions(+), 24 deletions(-) diff --git a/mysql-test/main/timezone2.result b/mysql-test/main/timezone2.result index f943e285951..806255f26f5 100644 --- a/mysql-test/main/timezone2.result +++ b/mysql-test/main/timezone2.result @@ -654,3 +654,25 @@ SET time_zone=DEFAULT; # # End of 10.4 tests # +# +# MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result +# +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP NULL); +SET timestamp=1288477526; +/* this is summer time, earlier */ +INSERT INTO t1 VALUES (NOW()); +SET timestamp=1288477526+3599; +/* this is winter time, later */ +INSERT INTO t1 VALUES (NOW()); +SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a; +a UNIX_TIMESTAMP(a) +2010-10-31 02:25:26 1288477526 +2010-10-31 02:25:25 1288481125 +SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a <= ALL (SELECT * FROM t1); +a UNIX_TIMESTAMP(a) +2010-10-31 02:25:26 1288477526 +SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1); +a UNIX_TIMESTAMP(a) +2010-10-31 02:25:25 1288481125 +DROP TABLE t1; diff --git a/mysql-test/main/timezone2.test b/mysql-test/main/timezone2.test index 6a8c9f258e4..1feb8916871 100644 --- a/mysql-test/main/timezone2.test +++ b/mysql-test/main/timezone2.test @@ -598,3 +598,18 @@ SET time_zone=DEFAULT; --echo # --echo # End of 10.4 tests --echo # + +--echo # +--echo # MDEV-27101 Subquery using the ALL keyword on TIMESTAMP columns produces a wrong result +--echo # + +SET time_zone='Europe/Moscow'; +CREATE TABLE t1 (a TIMESTAMP NULL); +SET timestamp=1288477526; /* this is summer time, earlier */ +INSERT INTO t1 VALUES (NOW()); +SET timestamp=1288477526+3599; /* this is winter time, later */ +INSERT INTO t1 VALUES (NOW()); +SELECT a, UNIX_TIMESTAMP(a) FROM t1 ORDER BY a; +SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a <= ALL (SELECT * FROM t1); +SELECT a, UNIX_TIMESTAMP(a) FROM t1 WHERE a >= ALL (SELECT * FROM t1); +DROP TABLE t1; diff --git a/sql/sql_class.cc b/sql/sql_class.cc index bc4c54be605..b431fc75981 100644 --- a/sql/sql_class.cc +++ b/sql/sql_class.cc @@ -3698,6 +3698,41 @@ void select_max_min_finder_subselect::cleanup() } +void select_max_min_finder_subselect::set_op(const Type_handler *th) +{ + if (th->is_val_native_ready()) + { + op= &select_max_min_finder_subselect::cmp_native; + return; + } + + switch (th->cmp_type()) { + case REAL_RESULT: + op= &select_max_min_finder_subselect::cmp_real; + break; + case INT_RESULT: + op= &select_max_min_finder_subselect::cmp_int; + break; + case STRING_RESULT: + op= &select_max_min_finder_subselect::cmp_str; + break; + case DECIMAL_RESULT: + op= &select_max_min_finder_subselect::cmp_decimal; + break; + case TIME_RESULT: + if (th->field_type() == MYSQL_TYPE_TIME) + op= &select_max_min_finder_subselect::cmp_time; + else + op= &select_max_min_finder_subselect::cmp_str; + break; + case ROW_RESULT: + // This case should never be chosen + DBUG_ASSERT(0); + op= 0; + } +} + + int select_max_min_finder_subselect::send_data(List &items) { DBUG_ENTER("select_max_min_finder_subselect::send_data"); @@ -3716,30 +3751,7 @@ int select_max_min_finder_subselect::send_data(List &items) if (!cache) { cache= val_item->get_cache(thd); - switch (val_item->cmp_type()) { - case REAL_RESULT: - op= &select_max_min_finder_subselect::cmp_real; - break; - case INT_RESULT: - op= &select_max_min_finder_subselect::cmp_int; - break; - case STRING_RESULT: - op= &select_max_min_finder_subselect::cmp_str; - break; - case DECIMAL_RESULT: - op= &select_max_min_finder_subselect::cmp_decimal; - break; - case TIME_RESULT: - if (val_item->field_type() == MYSQL_TYPE_TIME) - op= &select_max_min_finder_subselect::cmp_time; - else - op= &select_max_min_finder_subselect::cmp_str; - break; - case ROW_RESULT: - // This case should never be choosen - DBUG_ASSERT(0); - op= 0; - } + set_op(val_item->type_handler()); } cache->store(val_item); it->store(0, cache); @@ -3833,6 +3845,26 @@ bool select_max_min_finder_subselect::cmp_str() return (sortcmp(val1, val2, cache->collation.collation) < 0); } + +bool select_max_min_finder_subselect::cmp_native() +{ + NativeBuffer cvalue, mvalue; + Item *maxmin= ((Item_singlerow_subselect *)item)->element_index(0); + bool cvalue_is_null= cache->val_native(thd, &cvalue); + bool mvalue_is_null= maxmin->val_native(thd, &mvalue); + + /* Ignore NULLs for ANY and keep them for ALL subqueries */ + if (cvalue_is_null) + return (is_all && !mvalue_is_null) || (!is_all && mvalue_is_null); + if (mvalue_is_null) + return !is_all; + + const Type_handler *th= cache->type_handler(); + return fmax ? th->cmp_native(cvalue, mvalue) > 0 : + th->cmp_native(cvalue, mvalue) < 0; +} + + int select_exists_subselect::send_data(List &items) { DBUG_ENTER("select_exists_subselect::send_data"); diff --git a/sql/sql_class.h b/sql/sql_class.h index 9aa243309a5..28c3fed6570 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -6068,6 +6068,7 @@ class select_max_min_finder_subselect :public select_subselect bool (select_max_min_finder_subselect::*op)(); bool fmax; bool is_all; + void set_op(const Type_handler *ha); public: select_max_min_finder_subselect(THD *thd_arg, Item_subselect *item_arg, bool mx, bool all): @@ -6080,6 +6081,7 @@ public: bool cmp_decimal(); bool cmp_str(); bool cmp_time(); + bool cmp_native(); }; /* EXISTS subselect interface class */ From c7f8cfc9e733517cff4aaa6f6eaca625a3afc098 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 16 Aug 2022 15:31:49 +0530 Subject: [PATCH 25/31] MDEV-27700 ASAN: Heap_use_after_free in btr_search_drop_page_hash_index() Reason: ======= Race condition between btr_search_drop_hash_index() and btr_search_lazy_free(). One thread does resizing of buffer pool and clears the ahi on all pages in the buffer pool, frees the index and table while removing the last reference. At the same time, other thread access index->heap in btr_search_drop_hash_index(). Solution: ========= Acquire the respective ahi latch before checking index->freed() btr_search_drop_page_hash_index(): Added new parameter to indicate that drop ahi entries only if the index is marked as freed btr_search_check_marked_free_index(): Acquire all ahi latches and return true if the index was freed --- storage/innobase/btr/btr0btr.cc | 5 ++-- storage/innobase/btr/btr0sea.cc | 36 ++++++++++++++++++++++++---- storage/innobase/buf/buf0buf.cc | 13 ++++------ storage/innobase/include/btr0sea.h | 18 +++++++++++--- storage/innobase/include/btr0sea.inl | 13 ++++++---- 5 files changed, 63 insertions(+), 22 deletions(-) diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index b2c3b55cbaa..2ef5495a510 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -721,8 +721,9 @@ void btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, bool blob) { ut_ad(mtr_memo_contains(mtr, block, MTR_MEMO_PAGE_X_FIX)); -#ifdef BTR_CUR_HASH_ADAPT - if (block->index && !block->index->freed()) { +#if defined BTR_CUR_HASH_ADAPT && defined UNIV_DEBUG + if (block->index + && !btr_search_check_marked_free_index(block)) { ut_ad(!blob); ut_ad(page_is_leaf(block->frame)); } diff --git a/storage/innobase/btr/btr0sea.cc b/storage/innobase/btr/btr0sea.cc index 77c8845729d..8cc30de0dd8 100644 --- a/storage/innobase/btr/btr0sea.cc +++ b/storage/innobase/btr/btr0sea.cc @@ -1102,8 +1102,11 @@ fail_and_release_page: index page for which we know that block->buf_fix_count == 0 or it is an index page which has already been removed from the buf_pool->page_hash - i.e.: it is in state BUF_BLOCK_REMOVE_HASH */ -void btr_search_drop_page_hash_index(buf_block_t* block) + i.e.: it is in state BUF_BLOCK_REMOVE_HASH +@param[in] garbage_collect drop ahi only if the index is marked + as freed */ +void btr_search_drop_page_hash_index(buf_block_t* block, + bool garbage_collect) { ulint n_fields; ulint n_bytes; @@ -1149,13 +1152,21 @@ retry: % btr_ahi_parts; latch = btr_search_latches[ahi_slot]; + rw_lock_s_lock(latch); + dict_index_t* index = block->index; bool is_freed = index && index->freed(); if (is_freed) { + rw_lock_s_unlock(latch); rw_lock_x_lock(latch); - } else { - rw_lock_s_lock(latch); + if (index != block->index) { + rw_lock_x_unlock(latch); + goto retry; + } + } else if (garbage_collect) { + rw_lock_s_unlock(latch); + return; } assert_block_ahi_valid(block); @@ -2220,5 +2231,22 @@ btr_search_validate() return(true); } +#ifdef UNIV_DEBUG +bool btr_search_check_marked_free_index(const buf_block_t *block) +{ + const index_id_t index_id= btr_page_get_index_id(block->frame); + + rw_lock_t *ahi_latch= btr_get_search_latch( + index_id, block->page.id.space()); + + rw_lock_s_lock(ahi_latch); + + bool is_freed= block->index && block->index->freed(); + + rw_lock_s_unlock(ahi_latch); + + return is_freed; +} +#endif /* UNIV_DEBUG */ #endif /* defined UNIV_AHI_DEBUG || defined UNIV_DEBUG */ #endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/buf/buf0buf.cc b/storage/innobase/buf/buf0buf.cc index e4332fc3f32..8658b3a4a89 100644 --- a/storage/innobase/buf/buf0buf.cc +++ b/storage/innobase/buf/buf0buf.cc @@ -3935,18 +3935,14 @@ static void buf_defer_drop_ahi(buf_block_t *block, mtr_memo_type_t fix_type) /* Temporarily release our S-latch. */ rw_lock_s_unlock(&block->lock); rw_lock_x_lock(&block->lock); - if (dict_index_t *index= block->index) - if (index->freed()) - btr_search_drop_page_hash_index(block); + btr_search_drop_page_hash_index(block, true); rw_lock_x_unlock(&block->lock); rw_lock_s_lock(&block->lock); break; case MTR_MEMO_PAGE_SX_FIX: rw_lock_sx_unlock(&block->lock); rw_lock_x_lock(&block->lock); - if (dict_index_t *index= block->index) - if (index->freed()) - btr_search_drop_page_hash_index(block); + btr_search_drop_page_hash_index(block, true); rw_lock_x_unlock(&block->lock); rw_lock_sx_lock(&block->lock); break; @@ -3993,8 +3989,7 @@ static buf_block_t* buf_page_mtr_lock(buf_block_t *block, #ifdef BTR_CUR_HASH_ADAPT { - dict_index_t *index= block->index; - if (index && index->freed()) + if (block->index) buf_defer_drop_ahi(block, fix_type); } #endif /* BTR_CUR_HASH_ADAPT */ @@ -4916,7 +4911,7 @@ buf_page_get_known_nowait( # ifdef BTR_CUR_HASH_ADAPT ut_ad(!block->page.file_page_was_freed - || (block->index && block->index->freed())); + || btr_search_check_marked_free_index(block)); # else /* BTR_CUR_HASH_ADAPT */ ut_ad(!block->page.file_page_was_freed); # endif /* BTR_CUR_HASH_ADAPT */ diff --git a/storage/innobase/include/btr0sea.h b/storage/innobase/include/btr0sea.h index 8ed0a13f0b5..8277be8ac14 100644 --- a/storage/innobase/include/btr0sea.h +++ b/storage/innobase/include/btr0sea.h @@ -99,8 +99,11 @@ btr_search_move_or_delete_hash_entries( index page for which we know that block->buf_fix_count == 0 or it is an index page which has already been removed from the buf_pool->page_hash - i.e.: it is in state BUF_BLOCK_REMOVE_HASH */ -void btr_search_drop_page_hash_index(buf_block_t* block); + i.e.: it is in state BUF_BLOCK_REMOVE_HASH +@param[in] garbage_collect drop ahi only if the index is marked + as freed */ +void btr_search_drop_page_hash_index(buf_block_t* block, + bool garbage_collect= false); /** Drop possible adaptive hash index entries when a page is evicted from the buffer pool or freed in a file, or the index is being dropped. @@ -173,16 +176,25 @@ A table is selected from an array of tables using pair of index-id, space-id. @param[in] index index handler @return hash table */ static inline hash_table_t* btr_get_search_table(const dict_index_t* index); + +#ifdef UNIV_DEBUG +/** @return if the index is marked as freed */ +bool btr_search_check_marked_free_index(const buf_block_t *block); +#endif /* UNIV_DEBUG */ + #else /* BTR_CUR_HASH_ADAPT */ # define btr_search_sys_create(size) # define btr_search_sys_free() -# define btr_search_drop_page_hash_index(block) +# define btr_search_drop_page_hash_index(block, garbage_collect) # define btr_search_s_lock_all(index) # define btr_search_s_unlock_all(index) # define btr_search_info_update(index, cursor) # define btr_search_move_or_delete_hash_entries(new_block, block) # define btr_search_update_hash_on_insert(cursor, ahi_latch) # define btr_search_update_hash_on_delete(cursor) +#ifdef UNIV_DEBUG +# define btr_search_check_marked_free_index(block) +#endif /* UNIV_DEBUG */ #endif /* BTR_CUR_HASH_ADAPT */ #ifdef BTR_CUR_ADAPT diff --git a/storage/innobase/include/btr0sea.inl b/storage/innobase/include/btr0sea.inl index 9db0084ce59..b07f3882fd2 100644 --- a/storage/innobase/include/btr0sea.inl +++ b/storage/innobase/include/btr0sea.inl @@ -158,6 +158,14 @@ static inline bool btr_search_own_any() } #endif /* UNIV_DEBUG */ +static inline rw_lock_t* btr_get_search_latch( + index_id_t index_id, ulint space_id) +{ + ulint ifold = ut_fold_ulint_pair(ulint(index_id), space_id); + + return(btr_search_latches[ifold % btr_ahi_parts]); +} + /** Get the adaptive hash search index latch for a b-tree. @param[in] index b-tree index @return latch */ @@ -167,10 +175,7 @@ static inline rw_lock_t* btr_get_search_latch(const dict_index_t* index) ut_ad(!index->table->space || index->table->space->id == index->table->space_id); - ulint ifold = ut_fold_ulint_pair(ulint(index->id), - index->table->space_id); - - return(btr_search_latches[ifold % btr_ahi_parts]); + return btr_get_search_latch(index->id, index->table->space_id); } /** Get the hash-table based on index attributes. From 55c648a73880013351dfe07edcd2af4a487ad873 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 23 Aug 2022 08:58:23 +0400 Subject: [PATCH 26/31] MDEV-27099 Subquery using the ALL keyword on INET6 columns produces a wrong result This problem was earlier fixed by MDEV-27101. Adding INET6 tests only. --- .../mysql-test/type_inet/type_inet6.result | 16 ++++++++++++++++ .../mysql-test/type_inet/type_inet6.test | 11 +++++++++++ 2 files changed, 27 insertions(+) diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.result b/plugin/type_inet/mysql-test/type_inet/type_inet6.result index 2cc6942aa52..de2f504445d 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.result @@ -2213,3 +2213,19 @@ SELECT * FROM companies; id name DROP TABLE divisions; DROP TABLE companies; +# +# MDEV-27099 Subquery using the ALL keyword on INET6 columns produces a wrong result +# +CREATE TABLE t1 (d INET6); +INSERT INTO t1 VALUES ('1::0'), ('12::0'); +SELECT * FROM t1 ORDER BY d; +d +1:: +12:: +SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1); +d +1:: +SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1); +d +12:: +DROP TABLE t1; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.test b/plugin/type_inet/mysql-test/type_inet/type_inet6.test index ef8399d981f..becc063ddc9 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.test @@ -1630,3 +1630,14 @@ DELETE FROM companies WHERE id IN (SELECT company_id FROM divisions); SELECT * FROM companies; DROP TABLE divisions; DROP TABLE companies; + +--echo # +--echo # MDEV-27099 Subquery using the ALL keyword on INET6 columns produces a wrong result +--echo # + +CREATE TABLE t1 (d INET6); +INSERT INTO t1 VALUES ('1::0'), ('12::0'); +SELECT * FROM t1 ORDER BY d; +SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1); +SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1); +DROP TABLE t1; From 01f9c81237b1d096e3011a4af8d87d2ba81558a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 23 Aug 2022 08:47:49 +0300 Subject: [PATCH 27/31] MDEV-29336: Potential deadlock in btr_page_alloc_low() with the AHI The index root page contains the fields BTR_SEG_TOP and BTR_SEG_LEAF which keep track of allocated pages in the index tree. These fields are normally protected by an Update latch, so that concurrent read access to other parts of the page will be possible. When the index root page is already exclusively latched in the mini-transaction, we must not try to acquire a lower-grade Update latch. In fact, when the root page is already X or U latched in the mini-transaction, there is no point to acquire another latch. Moreover, after a U latch was acquired on top of an X-latch, mtr_t::defer_drop_ahi() would trigger an assertion failure or lock corruption in block->page.lock.u_x_upgrade() because X locks already exist on the block. This problem may have been introduced in commit 03ca6495df31313c96e38834b9a235245e2ae2a8 (MDEV-24142). btr_page_alloc_low(), btr_page_free(): Initially buffer-fix the root page. If it is already U or X latched, release the buffer-fix. Else, upgrade the buffer-fix to a U latch. mtr_t::u_lock_register(): Upgrade a buffer-fix to U latch. mtr_t::have_u_or_x_latch(): Check if U or X latches are already registered in the mini-transaction. --- storage/innobase/btr/btr0btr.cc | 48 ++++++++++++++++++++++++++---- storage/innobase/include/mtr0mtr.h | 13 ++++++++ storage/innobase/mtr/mtr0mtr.cc | 23 ++++++++++++++ 3 files changed, 79 insertions(+), 5 deletions(-) diff --git a/storage/innobase/btr/btr0btr.cc b/storage/innobase/btr/btr0btr.cc index 3b994c5fa98..9ce96f66e64 100644 --- a/storage/innobase/btr/btr0btr.cc +++ b/storage/innobase/btr/btr0btr.cc @@ -513,9 +513,28 @@ btr_page_alloc_low( page should be initialized. */ dberr_t* err) /*!< out: error code */ { - buf_block_t *root= btr_root_block_get(index, RW_SX_LATCH, mtr, err); + const auto savepoint= mtr->get_savepoint(); + buf_block_t *root= btr_root_block_get(index, RW_NO_LATCH, mtr, err); if (UNIV_UNLIKELY(!root)) return root; + + if (mtr->have_u_or_x_latch(*root)) + { +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!root->index || !root->index->freed()); +#endif + mtr->release_block_at_savepoint(savepoint, root); + } + else + { + mtr->u_lock_register(savepoint); + root->page.lock.u_lock(); +#ifdef BTR_CUR_HASH_ADAPT + if (root->index) + mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX); +#endif + } + fseg_header_t *seg_header= root->page.frame + (level ? PAGE_HEADER + PAGE_BTR_SEG_TOP : PAGE_HEADER + PAGE_BTR_SEG_LEAF); return fseg_alloc_free_page_general(seg_header, hint_page_no, file_direction, @@ -610,11 +629,30 @@ dberr_t btr_page_free(dict_index_t* index, buf_block_t* block, mtr_t* mtr, fil_space_t *space= index->table->space; dberr_t err; - if (page_t* root = btr_root_get(index, mtr, &err)) + + const auto savepoint= mtr->get_savepoint(); + if (buf_block_t *root= btr_root_block_get(index, RW_NO_LATCH, mtr, &err)) { - err= fseg_free_page(&root[blob || page_is_leaf(block->page.frame) - ? PAGE_HEADER + PAGE_BTR_SEG_LEAF - : PAGE_HEADER + PAGE_BTR_SEG_TOP], + if (mtr->have_u_or_x_latch(*root)) + { +#ifdef BTR_CUR_HASH_ADAPT + ut_ad(!root->index || !root->index->freed()); +#endif + mtr->release_block_at_savepoint(savepoint, root); + } + else + { + mtr->u_lock_register(savepoint); + root->page.lock.u_lock(); +#ifdef BTR_CUR_HASH_ADAPT + if (root->index) + mtr_t::defer_drop_ahi(root, MTR_MEMO_PAGE_SX_FIX); +#endif + } + err= fseg_free_page(&root->page.frame[blob || + page_is_leaf(block->page.frame) + ? PAGE_HEADER + PAGE_BTR_SEG_LEAF + : PAGE_HEADER + PAGE_BTR_SEG_TOP], space, page, mtr, space_latched); } if (err == DB_SUCCESS) diff --git a/storage/innobase/include/mtr0mtr.h b/storage/innobase/include/mtr0mtr.h index 8a3fe59ac59..edf583aa466 100644 --- a/storage/innobase/include/mtr0mtr.h +++ b/storage/innobase/include/mtr0mtr.h @@ -177,6 +177,10 @@ struct mtr_t { @param block buffer pool block to search for */ bool have_x_latch(const buf_block_t &block) const; + /** Check if we are holding a block latch in S or U mode + @param block buffer pool block to search for */ + bool have_u_or_x_latch(const buf_block_t &block) const; + /** Copy the tablespaces associated with the mini-transaction (needed for generating FILE_MODIFY records) @param[in] mtr mini-transaction that may modify @@ -336,6 +340,15 @@ public: @param rw_latch RW_S_LATCH, RW_SX_LATCH, RW_X_LATCH, RW_NO_LATCH */ void page_lock(buf_block_t *block, ulint rw_latch); + /** Register a page latch on a buffer-fixed block was buffer-fixed. + @param latch latch type */ + void u_lock_register(ulint savepoint) + { + mtr_memo_slot_t *slot= m_memo.at(savepoint); + ut_ad(slot->type == MTR_MEMO_BUF_FIX); + slot->type= MTR_MEMO_PAGE_SX_FIX; + } + /** Upgrade U locks on a block to X */ void page_lock_upgrade(const buf_block_t &block); /** Upgrade X lock to X */ diff --git a/storage/innobase/mtr/mtr0mtr.cc b/storage/innobase/mtr/mtr0mtr.cc index 594982f07c2..45c02f48ec8 100644 --- a/storage/innobase/mtr/mtr0mtr.cc +++ b/storage/innobase/mtr/mtr0mtr.cc @@ -1220,6 +1220,21 @@ struct FindBlockX } }; +/** Find out whether a block was not X or U latched by the mini-transaction */ +struct FindBlockUX +{ + const buf_block_t █ + + FindBlockUX(const buf_block_t &block): block(block) {} + + /** @return whether the block was not found x-latched */ + bool operator()(const mtr_memo_slot_t *slot) const + { + return slot->object != &block || + !(slot->type & (MTR_MEMO_PAGE_X_FIX | MTR_MEMO_PAGE_SX_FIX)); + } +}; + #ifdef UNIV_DEBUG /** Assert that the block is not present in the mini-transaction */ struct FindNoBlock @@ -1250,6 +1265,14 @@ bool mtr_t::have_x_latch(const buf_block_t &block) const return true; } +bool mtr_t::have_u_or_x_latch(const buf_block_t &block) const +{ + if (m_memo.for_each_block(CIterate(FindBlockUX(block)))) + return false; + ut_ad(block.page.lock.have_u_or_x()); + return true; +} + /** Check if we are holding exclusive tablespace latch @param space tablespace to search for @param shared whether to look for shared latch, instead of exclusive From 4feb9df105158eab216395db36ad5387d09d5cea Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Thu, 18 Aug 2022 16:21:31 +0530 Subject: [PATCH 28/31] MDEV-29282 atomic.rename_trigger fails occasionally This reverts part of commit 212994f704496d01881f377e34e04bd007e5e298 (MDEV-28974) and implements a better fix that works in that special case while avoiding other failures. fil_name_process(): Do not rename the tablespace in deferred_spaces; it already contains the latest name for the space id. deferred_spaces::create(): In mariadb-backup --prepare, replace absolute data directory file path with short name relative to the backup directory and store it as filename while deferring tablespace file creation. --- mysql-test/suite/atomic/disabled.def | 13 ------------- storage/innobase/log/log0recv.cc | 27 +++++++++++++++++++++++---- 2 files changed, 23 insertions(+), 17 deletions(-) delete mode 100644 mysql-test/suite/atomic/disabled.def diff --git a/mysql-test/suite/atomic/disabled.def b/mysql-test/suite/atomic/disabled.def deleted file mode 100644 index 6d8defe344a..00000000000 --- a/mysql-test/suite/atomic/disabled.def +++ /dev/null @@ -1,13 +0,0 @@ -############################################################################## -# -# List the test cases that are to be disabled temporarily. -# -# Separate the test case name and the comment with ':'. -# -# : BUG# -# -# Do not use any TAB characters for whitespace. -# -############################################################################## - -rename_trigger : MDEV-29282 atomic.rename_trigger fails occasionly diff --git a/storage/innobase/log/log0recv.cc b/storage/innobase/log/log0recv.cc index 9b6f778b859..fc2d50da62c 100644 --- a/storage/innobase/log/log0recv.cc +++ b/storage/innobase/log/log0recv.cc @@ -829,7 +829,29 @@ processed: fil_space_t *space= fil_space_t::create(it->first, flags, FIL_TYPE_TABLESPACE, crypt_data); ut_ad(space); - space->add(name.c_str(), OS_FILE_CLOSED, size, false, false); + const char *filename= name.c_str(); + if (srv_operation == SRV_OPERATION_RESTORE) + { + const char* tbl_name = strrchr(filename, '/'); +#ifdef _WIN32 + if (const char *last = strrchr(filename, '\\')) + { + if (last > tbl_name) + tbl_name = last; + } +#endif + if (tbl_name) + { + while (--tbl_name > filename && +#ifdef _WIN32 + *tbl_name != '\\' && +#endif + *tbl_name != '/'); + if (tbl_name > filename) + filename= tbl_name + 1; + } + } + space->add(filename, OS_FILE_CLOSED, size, false, false); space->recv_size= it->second.size; space->size_in_header= size; return space; @@ -1227,9 +1249,6 @@ static void fil_name_process(const char *name, ulint len, uint32_t space_id, d->deleted = true; goto got_deleted; } - if (ftype == FILE_RENAME) { - d->file_name= fname.name; - } goto reload; } From 0e8544cd73c190dc4da0a3e83eefffe70bb3f755 Mon Sep 17 00:00:00 2001 From: Alexander Barkov Date: Tue, 23 Aug 2022 13:17:35 +0400 Subject: [PATCH 29/31] MDEV-29355 Backport templatized INET6 implementation from 10.7 to 10.6 --- plugin/type_inet/item_inetfunc.cc | 27 +- .../mysql-test/type_inet/type_inet6.result | 28 + .../mysql-test/type_inet/type_inet6.test | 15 + plugin/type_inet/plugin.cc | 8 +- plugin/type_inet/sql_type_inet.cc | 1205 +---------- plugin/type_inet/sql_type_inet.h | 912 +------- sql/sql_string.h | 5 + sql/sql_type_fixedbin.h | 1913 +++++++++++++++++ sql/sql_type_fixedbin_storage.h | 173 ++ 9 files changed, 2174 insertions(+), 2112 deletions(-) create mode 100644 sql/sql_type_fixedbin.h create mode 100644 sql/sql_type_fixedbin_storage.h diff --git a/plugin/type_inet/item_inetfunc.cc b/plugin/type_inet/item_inetfunc.cc index dcccf762295..b23ae04a861 100644 --- a/plugin/type_inet/item_inetfunc.cc +++ b/plugin/type_inet/item_inetfunc.cc @@ -158,7 +158,7 @@ String *Item_func_inet6_aton::val_str(String *buffer) return buffer; } - Inet6_null ipv6(*tmp.string()); + Inet6Bundle::Fbt_null ipv6(*tmp.string()); if (!ipv6.is_null()) { ipv6.to_binary(buffer); @@ -197,7 +197,7 @@ String *Item_func_inet6_ntoa::val_str_ascii(String *buffer) return buffer; } - Inet6_null ipv6(static_cast(*tmp.string())); + Inet6Bundle::Fbt_null ipv6(static_cast(*tmp.string())); if (!ipv6.is_null()) { ipv6.to_string(buffer); @@ -221,6 +221,22 @@ longlong Item_func_is_ipv4::val_int() return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null(); } +class IP6 : public Inet6Bundle::Fbt_null +{ +public: + IP6(Item* arg) : Inet6Bundle::Fbt_null(arg) {} + bool is_v4compat() const + { + static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size"); + return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer); + } + bool is_v4mapped() const + { + static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size"); + return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer); + } +}; + /** Checks if the passed string represents an IPv6-address. @@ -230,17 +246,16 @@ longlong Item_func_is_ipv6::val_int() { DBUG_ASSERT(fixed()); String_ptr_and_buffer tmp(args[0]); - return !tmp.is_null() && !Inet6_null(*tmp.string()).is_null(); + return !tmp.is_null() && !Inet6Bundle::Fbt_null(*tmp.string()).is_null(); } - /** Checks if the passed IPv6-address is an IPv4-compat IPv6-address. */ longlong Item_func_is_ipv4_compat::val_int() { - Inet6_null ip6(args[0]); + IP6 ip6(args[0]); return !ip6.is_null() && ip6.is_v4compat(); } @@ -251,6 +266,6 @@ longlong Item_func_is_ipv4_compat::val_int() longlong Item_func_is_ipv4_mapped::val_int() { - Inet6_null ip6(args[0]); + IP6 ip6(args[0]); return !ip6.is_null() && ip6.is_v4mapped(); } diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.result b/plugin/type_inet/mysql-test/type_inet/type_inet6.result index c8192f7bde3..ecd1e2f797c 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.result +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.result @@ -2229,3 +2229,31 @@ SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1); d 12:: DROP TABLE t1; +# +# MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle::Fbt FixedBinTypeBundle::Field_fbt::to_fbt() +# +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10'); +INSERT INTO t1(id) VALUES (1), (2), (3), (4); +INSERT INTO t1 VALUES (5,'::5'), (6,'::6'); +SELECT * FROM t1 ORDER BY a; +id a +5 ::5 +6 ::6 +1 ::10 +2 ::10 +3 ::10 +4 ::10 +CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a; +CREATE TABLE t2 SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; +a m +::5 5 +::6 6 +::10 1 +SELECT * FROM t2 ORDER BY a; +a m +::5 5 +::6 6 +::10 1 +DROP VIEW v1; +DROP TABLE t1, t2; diff --git a/plugin/type_inet/mysql-test/type_inet/type_inet6.test b/plugin/type_inet/mysql-test/type_inet/type_inet6.test index becc063ddc9..2cdbc0eb2b9 100644 --- a/plugin/type_inet/mysql-test/type_inet/type_inet6.test +++ b/plugin/type_inet/mysql-test/type_inet/type_inet6.test @@ -1641,3 +1641,18 @@ SELECT * FROM t1 ORDER BY d; SELECT * FROM t1 WHERE d <= ALL (SELECT * FROM t1); SELECT * FROM t1 WHERE d >= ALL (SELECT * FROM t1); DROP TABLE t1; + +--echo # +--echo # MDEV-27015 Assertion `!is_null()' failed in FixedBinTypeBundle::Fbt FixedBinTypeBundle::Field_fbt::to_fbt() +--echo # + +CREATE TABLE t1 (id int NOT NULL PRIMARY KEY, a INET6(6) DEFAULT '::10'); +INSERT INTO t1(id) VALUES (1), (2), (3), (4); +INSERT INTO t1 VALUES (5,'::5'), (6,'::6'); +SELECT * FROM t1 ORDER BY a; +CREATE VIEW v1(a, m) AS SELECT a, MIN(id) FROM t1 GROUP BY a; +CREATE TABLE t2 SELECT * FROM v1; +SELECT * FROM v1 ORDER BY a; +SELECT * FROM t2 ORDER BY a; +DROP VIEW v1; +DROP TABLE t1, t2; diff --git a/plugin/type_inet/plugin.cc b/plugin/type_inet/plugin.cc index 77804c82af6..0b57e2bec1f 100644 --- a/plugin/type_inet/plugin.cc +++ b/plugin/type_inet/plugin.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2019 MariaDB Corporation +/* Copyright (c) 2019,2021 MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,14 +21,10 @@ #include #include - -Type_handler_inet6 type_handler_inet6; - - static struct st_mariadb_data_type plugin_descriptor_type_inet6= { MariaDB_DATA_TYPE_INTERFACE_VERSION, - &type_handler_inet6 + Inet6Bundle::type_handler_fbt() }; diff --git a/plugin/type_inet/sql_type_inet.cc b/plugin/type_inet/sql_type_inet.cc index 488a569ef7d..b8d2ef706a9 100644 --- a/plugin/type_inet/sql_type_inet.cc +++ b/plugin/type_inet/sql_type_inet.cc @@ -1,6 +1,6 @@ /* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. Copyright (c) 2014 MariaDB Foundation - Copyright (c) 2019 MariaDB Corporation + Copyright (c) 2019,2022 MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -168,7 +168,7 @@ bool Inet4::ascii_to_ipv4(const char *str, size_t str_length) IPv4-part differently on different platforms. */ -bool Inet6::ascii_to_ipv6(const char *str, size_t str_length) +bool Inet6::ascii_to_fbt(const char *str, size_t str_length) { if (str_length < 2) { @@ -507,1203 +507,8 @@ size_t Inet6::to_string(char *dst, size_t dstsize) const return (size_t) (p - dst); } - -bool Inet6::fix_fields_maybe_null_on_conversion_to_inet6(Item *item) +const Name &Inet6::default_value() { - if (item->maybe_null()) - return true; - if (item->type_handler() == &type_handler_inet6) - return false; - if (!item->const_item() || item->is_expensive()) - return true; - return Inet6_null(item, false).is_null(); -} - - -bool Inet6::make_from_item(Item *item, bool warn) -{ - if (item->type_handler() == &type_handler_inet6) - { - Native tmp(m_buffer, sizeof(m_buffer)); - bool rc= item->val_native(current_thd, &tmp); - if (rc) - return true; - DBUG_ASSERT(tmp.length() == sizeof(m_buffer)); - if (tmp.ptr() != m_buffer) - memcpy(m_buffer, tmp.ptr(), sizeof(m_buffer)); - return false; - } - StringBufferInet6 tmp; - String *str= item->val_str(&tmp); - return str ? make_from_character_or_binary_string(str, warn) : true; -} - - -bool Inet6::make_from_character_or_binary_string(const String *str, bool warn) -{ - static Name name= type_handler_inet6.name(); - if (str->charset() != &my_charset_bin) - { - bool rc= character_string_to_ipv6(str->ptr(), str->length(), - str->charset()); - if (rc && warn) - current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - name.ptr(), - ErrConvString(str).ptr()); - return rc; - } - if (str->length() != sizeof(m_buffer)) - { - if (warn) - current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - name.ptr(), - ErrConvString(str).ptr()); - return true; - } - DBUG_ASSERT(str->ptr() != m_buffer); - memcpy(m_buffer, str->ptr(), sizeof(m_buffer)); - return false; -}; - - -/********************************************************************/ - - -class cmp_item_inet6: public cmp_item_scalar -{ - Inet6 m_native; -public: - cmp_item_inet6() - :cmp_item_scalar(), - m_native(Inet6_zero()) - { } - void store_value(Item *item) override - { - m_native= Inet6(item, &m_null_value); - } - int cmp_not_null(const Value *val) override - { - DBUG_ASSERT(!val->is_null()); - DBUG_ASSERT(val->is_string()); - Inet6_null tmp(val->m_string); - DBUG_ASSERT(!tmp.is_null()); - return m_native.cmp(tmp); - } - int cmp(Item *arg) override - { - Inet6_null tmp(arg); - return m_null_value || tmp.is_null() ? UNKNOWN : m_native.cmp(tmp) != 0; - } - int compare(cmp_item *ci) override - { - cmp_item_inet6 *tmp= static_cast(ci); - DBUG_ASSERT(!m_null_value); - DBUG_ASSERT(!tmp->m_null_value); - return m_native.cmp(tmp->m_native); - } - cmp_item *make_same(THD *thd) override - { - return new (thd->mem_root) cmp_item_inet6(); - } -}; - - -class Field_inet6: public Field -{ - static void set_min_value(char *ptr) - { - memset(ptr, 0, Inet6::binary_length()); - } - static void set_max_value(char *ptr) - { - memset(ptr, 0xFF, Inet6::binary_length()); - } - void store_warning(const ErrConv &str, - Sql_condition::enum_warning_level level) - { - static const Name type_name= type_handler_inet6.name(); - if (get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) - return; - const TABLE_SHARE *s= table->s; - get_thd()->push_warning_truncated_value_for_field(level, type_name.ptr(), - str.ptr(), - s ? s->db.str : nullptr, - s ? s->table_name.str - : nullptr, - field_name.str); - } - int set_null_with_warn(const ErrConv &str) - { - store_warning(str, Sql_condition::WARN_LEVEL_WARN); - set_null(); - return 1; - } - int set_min_value_with_warn(const ErrConv &str) - { - store_warning(str, Sql_condition::WARN_LEVEL_WARN); - set_min_value((char*) ptr); - return 1; - } - int set_max_value_with_warn(const ErrConv &str) - { - store_warning(str, Sql_condition::WARN_LEVEL_WARN); - set_max_value((char*) ptr); - return 1; - } - int store_inet6_null_with_warn(const Inet6_null &inet6, - const ErrConvString &err) - { - DBUG_ASSERT(marked_for_write_or_computed()); - if (inet6.is_null()) - return maybe_null() ? set_null_with_warn(err) : - set_min_value_with_warn(err); - inet6.to_binary((char *) ptr, Inet6::binary_length()); - return 0; - } - -public: - Field_inet6(const LEX_CSTRING *field_name_arg, const Record_addr &rec) - :Field(rec.ptr(), Inet6::max_char_length(), - rec.null_ptr(), rec.null_bit(), Field::NONE, field_name_arg) - { - flags|= BINARY_FLAG | UNSIGNED_FLAG; - } - const Type_handler *type_handler() const override - { - return &type_handler_inet6; - } - uint32 max_display_length() const override { return field_length; } - bool str_needs_quotes() const override { return true; } - const DTCollation &dtcollation() const override - { - static DTCollation_numeric c; - return c; - } - CHARSET_INFO *charset(void) const override { return &my_charset_numeric; } - const CHARSET_INFO *sort_charset(void) const override { return &my_charset_bin; } - /** - This makes client-server protocol convert the value according - to @@character_set_client. - */ - bool binary() const override { return false; } - enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; } - - bool is_equal(const Column_definition &new_field) const override - { - return new_field.type_handler() == type_handler(); - } - bool eq_def(const Field *field) const override - { - return Field::eq_def(field); - } - double pos_in_interval(Field *min, Field *max) override - { - return pos_in_interval_val_str(min, max, 0); - } - int cmp(const uchar *a, const uchar *b) const override - { return memcmp(a, b, pack_length()); } - - void sort_string(uchar *to, uint length) override - { - DBUG_ASSERT(length == pack_length()); - memcpy(to, ptr, length); - } - uint32 pack_length() const override - { - return Inet6::binary_length(); - } - uint pack_length_from_metadata(uint field_metadata) const override - { - return Inet6::binary_length(); - } - - void sql_type(String &str) const override - { - static Name name= type_handler_inet6.name(); - str.set_ascii(name.ptr(), name.length()); - } - - void make_send_field(Send_field *to) override - { - Field::make_send_field(to); - to->set_data_type_name(type_handler_inet6.name().lex_cstring()); - } - - bool validate_value_in_record(THD *thd, const uchar *record) const override - { - return false; - } - - String *val_str(String *val_buffer, - String *val_ptr __attribute__((unused))) override - { - DBUG_ASSERT(marked_for_read()); - Inet6_null tmp((const char *) ptr, pack_length()); - return tmp.to_string(val_buffer) ? NULL : val_buffer; - } - - my_decimal *val_decimal(my_decimal *to) override - { - DBUG_ASSERT(marked_for_read()); - my_decimal_set_zero(to); - return to; - } - - longlong val_int() override - { - DBUG_ASSERT(marked_for_read()); - return 0; - } - - double val_real() override - { - DBUG_ASSERT(marked_for_read()); - return 0; - } - - bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override - { - DBUG_ASSERT(marked_for_read()); - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - return false; - } - - bool val_bool(void) override - { - DBUG_ASSERT(marked_for_read()); - return !Inet6::only_zero_bytes((const char *) ptr, Inet6::binary_length()); - } - - int store_native(const Native &value) override - { - DBUG_ASSERT(marked_for_write_or_computed()); - DBUG_ASSERT(value.length() == Inet6::binary_length()); - memcpy(ptr, value.ptr(), value.length()); - return 0; - } - - int store(const char *str, size_t length, CHARSET_INFO *cs) override - { - return cs == &my_charset_bin ? store_binary(str, length) : - store_text(str, length, cs); - } - - int store_text(const char *str, size_t length, CHARSET_INFO *cs) override - { - return store_inet6_null_with_warn(Inet6_null(str, length, cs), - ErrConvString(str, length, cs)); - } - - int store_binary(const char *str, size_t length) override - { - return store_inet6_null_with_warn(Inet6_null(str, length), - ErrConvString(str, length, - &my_charset_bin)); - } - - int store_hex_hybrid(const char *str, size_t length) override - { - return Field_inet6::store_binary(str, length); - } - - int store_decimal(const my_decimal *num) override - { - DBUG_ASSERT(marked_for_write_or_computed()); - return set_min_value_with_warn(ErrConvDecimal(num)); - } - - int store(longlong nr, bool unsigned_flag) override - { - DBUG_ASSERT(marked_for_write_or_computed()); - return set_min_value_with_warn( - ErrConvInteger(Longlong_hybrid(nr, unsigned_flag))); - } - - int store(double nr) override - { - DBUG_ASSERT(marked_for_write_or_computed()); - return set_min_value_with_warn(ErrConvDouble(nr)); - } - - int store_time_dec(const MYSQL_TIME *ltime, uint dec) override - { - DBUG_ASSERT(marked_for_write_or_computed()); - return set_min_value_with_warn(ErrConvTime(ltime)); - } - - /*** Field conversion routines ***/ - int store_field(Field *from) override - { - // INSERT INTO t1 (inet6_field) SELECT different_field_type FROM t2; - return from->save_in_field(this); - } - int save_in_field(Field *to) override - { - // INSERT INTO t2 (different_field_type) SELECT inet6_field FROM t1; - if (to->charset() == &my_charset_bin && - dynamic_cast - (to->type_handler())) - { - NativeBufferInet6 res; - val_native(&res); - return to->store(res.ptr(), res.length(), &my_charset_bin); - } - return save_in_field_str(to); - } - Copy_func *get_copy_func(const Field *from) const override - { - // ALTER to INET6 from another field - return do_field_string; - } - - Copy_func *get_copy_func_to(const Field *to) const override - { - if (type_handler() == to->type_handler()) - { - // ALTER from INET6 to INET6 - DBUG_ASSERT(pack_length() == to->pack_length()); - DBUG_ASSERT(charset() == to->charset()); - DBUG_ASSERT(sort_charset() == to->sort_charset()); - return Field::do_field_eq; - } - // ALTER from INET6 to another data type - if (to->charset() == &my_charset_bin && - dynamic_cast - (to->type_handler())) - { - /* - ALTER from INET6 to a binary string type, e.g.: - BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB - */ - return do_field_inet6_native_to_binary; - } - return do_field_string; - } - - static void do_field_inet6_native_to_binary(Copy_field *copy) - { - NativeBufferInet6 res; - copy->from_field->val_native(&res); - copy->to_field->store(res.ptr(), res.length(), &my_charset_bin); - } - - bool memcpy_field_possible(const Field *from) const override - { - // INSERT INTO t1 (inet6_field) SELECT field2 FROM t2; - return type_handler() == from->type_handler(); - } - enum_conv_type rpl_conv_type_from(const Conv_source &source, - const Relay_log_info *rli, - const Conv_param ¶m) const override - { - if (type_handler() == source.type_handler() || - (source.type_handler() == &type_handler_string && - source.type_handler()->max_display_length_for_field(source) == - Inet6::binary_length())) - return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); - return CONV_TYPE_IMPOSSIBLE; - } - - /*** Optimizer routines ***/ - bool test_if_equality_guarantees_uniqueness(const Item *const_item) const override - { - /* - This condition: - WHERE inet6_field=const - should return a single distinct value only, - as comparison is done according to INET6. - */ - return true; - } - bool can_be_substituted_to_equal_item(const Context &ctx, - const Item_equal *item_equal) - override - { - switch (ctx.subst_constraint()) { - case ANY_SUBST: - return ctx.compare_type_handler() == item_equal->compare_type_handler(); - case IDENTITY_SUBST: - return true; - } - return false; - } - Item *get_equal_const_item(THD *thd, const Context &ctx, - Item *const_item) override; - bool can_optimize_keypart_ref(const Item_bool_func *cond, - const Item *item) const override - { - /* - Mixing of two different non-traditional types is currently prevented. - This may change in the future. For example, INET4 and INET6 - data types can be made comparable. - But we allow mixing INET6 to a data type directly inherited from - a traditional type, e.g. INET6=VARCHAR/JSON. - */ - DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()-> - is_traditional_scalar_type() || - item->type_handler() == type_handler()); - return true; - } - /** - Test if Field can use range optimizer for a standard comparison operation: - <=, <, =, <=>, >, >= - Note, this method does not cover spatial operations. - */ - bool can_optimize_range(const Item_bool_func *cond, - const Item *item, - bool is_eq_func) const override - { - // See the DBUG_ASSERT comment in can_optimize_keypart_ref() - DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()-> - is_traditional_scalar_type() || - item->type_handler() == type_handler()); - return true; - } - SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, - const Item_bool_func *cond, - scalar_comparison_op op, Item *value) override - { - DBUG_ENTER("Field_inet6::get_mm_leaf"); - if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) - DBUG_RETURN(0); - int err= value->save_in_field_no_warnings(this, 1); - if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) - DBUG_RETURN(&null_element); - if (err > 0) - { - if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) - DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this)); - DBUG_RETURN(NULL); /* Cannot infer anything */ - } - DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); - } - bool can_optimize_hash_join(const Item_bool_func *cond, - const Item *item) const override - { - return can_optimize_keypart_ref(cond, item); - } - bool can_optimize_group_min_max(const Item_bool_func *cond, - const Item *const_item) const override - { - return true; - } - - uint row_pack_length() const override { return pack_length(); } - - Binlog_type_info binlog_type_info() const override - { - DBUG_ASSERT(type() == binlog_type()); - return Binlog_type_info_fixed_string(Field_inet6::binlog_type(), - Inet6::binary_length(), - &my_charset_bin); - } - - uchar *pack(uchar *to, const uchar *from, uint max_length) override - { - DBUG_PRINT("debug", ("Packing field '%s'", field_name.str)); - return StringPack(&my_charset_bin, Inet6::binary_length()). - pack(to, from, max_length); - } - - const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end, - uint param_data) override - { - return StringPack(&my_charset_bin, Inet6::binary_length()). - unpack(to, from, from_end, param_data); - } - - uint max_packed_col_length(uint max_length) override - { - return StringPack::max_packed_col_length(max_length); - } - - uint packed_col_length(const uchar *data_ptr, uint length) override - { - return StringPack::packed_col_length(data_ptr, length); - } - - /**********/ - uint size_of() const override { return sizeof(*this); } -}; - - -class Item_typecast_inet6: public Item_func -{ -public: - Item_typecast_inet6(THD *thd, Item *a) :Item_func(thd, a) {} - - const Type_handler *type_handler() const override - { return &type_handler_inet6; } - - enum Functype functype() const override { return CHAR_TYPECAST_FUNC; } - bool eq(const Item *item, bool binary_cmp) const override - { - if (this == item) - return true; - if (item->type() != FUNC_ITEM || - functype() != ((Item_func*)item)->functype()) - return false; - if (type_handler() != item->type_handler()) - return false; - Item_typecast_inet6 *cast= (Item_typecast_inet6*) item; - return args[0]->eq(cast->args[0], binary_cmp); - } - LEX_CSTRING func_name_cstring() const override - { - static LEX_CSTRING name= {STRING_WITH_LEN("cast_as_inet6") }; - return name; - } - void print(String *str, enum_query_type query_type) override - { - str->append(STRING_WITH_LEN("cast(")); - args[0]->print(str, query_type); - str->append(STRING_WITH_LEN(" as inet6)")); - } - bool fix_length_and_dec() override - { - Type_std_attributes::operator=(Type_std_attributes_inet6()); - if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(args[0])) - set_maybe_null(); - return false; - } - String *val_str(String *to) override - { - Inet6_null tmp(args[0]); - return (null_value= tmp.is_null() || tmp.to_string(to)) ? NULL : to; - } - longlong val_int() override - { - return 0; - } - double val_real() override - { - return 0; - } - my_decimal *val_decimal(my_decimal *to) override - { - my_decimal_set_zero(to); - return to; - } - bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override - { - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - return false; - } - bool val_native(THD *thd, Native *to) override - { - Inet6_null tmp(args[0]); - return null_value= tmp.is_null() || tmp.to_native(to); - } - Item *get_copy(THD *thd) override - { return get_item_copy(thd, this); } -}; - - -class Item_cache_inet6: public Item_cache -{ - NativeBufferInet6 m_value; -public: - Item_cache_inet6(THD *thd) - :Item_cache(thd, &type_handler_inet6) - { } - Item *get_copy(THD *thd) - { return get_item_copy(thd, this); } - bool cache_value() - { - if (!example) - return false; - value_cached= true; - /* - Merge comments: in 10.7 this code migrated to - Item_cache_fbt in to sql/sql_type_fixedbin.h - */ - null_value_inside= null_value= - example->val_native_with_conversion_result(current_thd, - &m_value, - type_handler()); - return true; - } - String* val_str(String *to) - { - if (!has_value()) - return NULL; - Inet6_null tmp(m_value.ptr(), m_value.length()); - return tmp.is_null() || tmp.to_string(to) ? NULL : to; - } - my_decimal *val_decimal(my_decimal *to) - { - if (!has_value()) - return NULL; - my_decimal_set_zero(to); - return to; - } - longlong val_int() - { - if (!has_value()) - return 0; - return 0; - } - double val_real() - { - if (!has_value()) - return 0; - return 0; - } - longlong val_datetime_packed(THD *thd) - { - DBUG_ASSERT(0); - if (!has_value()) - return 0; - return 0; - } - longlong val_time_packed(THD *thd) - { - DBUG_ASSERT(0); - if (!has_value()) - return 0; - return 0; - } - bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) - { - if (!has_value()) - return true; - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - return false; - } - bool val_native(THD *thd, Native *to) - { - if (!has_value()) - return true; - return to->copy(m_value.ptr(), m_value.length()); - } -}; - - -class Item_literal_inet6: public Item_literal -{ - Inet6 m_value; -public: - Item_literal_inet6(THD *thd) - :Item_literal(thd), - m_value(Inet6_zero()) - { } - Item_literal_inet6(THD *thd, const Inet6 &value) - :Item_literal(thd), - m_value(value) - { } - const Type_handler *type_handler() const override - { - return &type_handler_inet6; - } - longlong val_int() override - { - return 0; - } - double val_real() override - { - return 0; - } - String *val_str(String *to) override - { - return m_value.to_string(to) ? NULL : to; - } - my_decimal *val_decimal(my_decimal *to) override - { - my_decimal_set_zero(to); - return to; - } - bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override - { - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - return false; - } - bool val_native(THD *thd, Native *to) override - { - return m_value.to_native(to); - } - void print(String *str, enum_query_type query_type) override - { - StringBufferInet6 tmp; - m_value.to_string(&tmp); - str->append(STRING_WITH_LEN("INET6'")); - str->append(tmp); - str->append('\''); - } - Item *get_copy(THD *thd) override - { return get_item_copy(thd, this); } - - // Non-overriding methods - void set_value(const Inet6 &value) - { - m_value= value; - } -}; - - -class Item_copy_inet6: public Item_copy -{ - NativeBufferInet6 m_value; -public: - Item_copy_inet6(THD *thd, Item *item_arg): Item_copy(thd, item_arg) {} - - bool val_native(THD *thd, Native *to) override - { - if (null_value) - return true; - return to->copy(m_value.ptr(), m_value.length()); - } - String *val_str(String *to) override - { - if (null_value) - return NULL; - Inet6_null tmp(m_value.ptr(), m_value.length()); - return tmp.is_null() || tmp.to_string(to) ? NULL : to; - } - my_decimal *val_decimal(my_decimal *to) override - { - my_decimal_set_zero(to); - return to; - } - double val_real() override - { - return 0; - } - longlong val_int() override - { - return 0; - } - bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override - { - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - return null_value; - } - void copy() override - { - null_value= item->val_native(current_thd, &m_value); - DBUG_ASSERT(null_value == item->null_value); - } - int save_in_field(Field *field, bool no_conversions) override - { - return Item::save_in_field(field, no_conversions); - } - Item *get_copy(THD *thd) override - { return get_item_copy(thd, this); } -}; - - -class in_inet6 :public in_vector -{ - Inet6 m_value; - static int cmp_inet6(void *cmp_arg, Inet6 *a, Inet6 *b) - { - return a->cmp(*b); - } -public: - in_inet6(THD *thd, uint elements) - :in_vector(thd, elements, sizeof(Inet6), (qsort2_cmp) cmp_inet6, 0), - m_value(Inet6_zero()) - { } - const Type_handler *type_handler() const override - { - return &type_handler_inet6; - } - void set(uint pos, Item *item) override - { - Inet6 *buff= &((Inet6 *) base)[pos]; - Inet6_null value(item); - if (value.is_null()) - *buff= Inet6_zero(); - else - *buff= value; - } - uchar *get_value(Item *item) override - { - Inet6_null value(item); - if (value.is_null()) - return 0; - m_value= value; - return (uchar *) &m_value; - } - Item* create_item(THD *thd) override - { - return new (thd->mem_root) Item_literal_inet6(thd); - } - void value_to_item(uint pos, Item *item) override - { - const Inet6 &buff= (((Inet6*) base)[pos]); - static_cast(item)->set_value(buff); - } -}; - - -class Item_char_typecast_func_handler_inet6_to_binary: - public Item_handled_func::Handler_str -{ -public: - const Type_handler *return_type_handler(const Item_handled_func *item) - const override - { - if (item->max_length > MAX_FIELD_VARCHARLENGTH) - return Type_handler::blob_type_handler(item->max_length); - if (item->max_length > 255) - return &type_handler_varchar; - return &type_handler_string; - } - bool fix_length_and_dec(Item_handled_func *xitem) const override - { - return false; - } - String *val_str(Item_handled_func *item, String *to) const override - { - DBUG_ASSERT(dynamic_cast(item)); - return static_cast(item)-> - val_str_binary_from_native(to); - } -}; - - -static Item_char_typecast_func_handler_inet6_to_binary - item_char_typecast_func_handler_inet6_to_binary; - - -bool Type_handler_inet6:: - Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) const -{ - if (item->cast_charset() == &my_charset_bin) - { - item->fix_length_and_dec_native_to_binary(Inet6::binary_length()); - item->set_func_handler(&item_char_typecast_func_handler_inet6_to_binary); - return false; - } - item->fix_length_and_dec_str(); - return false; -} - - -bool -Type_handler_inet6::character_or_binary_string_to_native(THD *thd, - const String *str, - Native *to) const -{ - if (str->charset() == &my_charset_bin) - { - // Convert from a binary string - if (str->length() != Inet6::binary_length() || - to->copy(str->ptr(), str->length())) - { - thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - name().ptr(), - ErrConvString(str).ptr()); - return true; - } - return false; - } - // Convert from a character string - Inet6_null tmp(*str); - if (tmp.is_null()) - thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - name().ptr(), ErrConvString(str).ptr()); - return tmp.is_null() || tmp.to_native(to); -} - - -bool -Type_handler_inet6::Item_save_in_value(THD *thd, Item *item, - st_value *value) const -{ - value->m_type= DYN_COL_STRING; - String *str= item->val_str(&value->m_string); - if (str != &value->m_string && !item->null_value) - { - // "item" returned a non-NULL value - if (Inet6_null(*str).is_null()) - { - /* - The value was not-null, but conversion to INET6 failed: - SELECT a, DECODE_ORACLE(inet6col, 'garbage', '', '::01', '01') - FROM t1; - */ - thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - name().ptr(), ErrConvString(str).ptr()); - value->m_type= DYN_COL_NULL; - return true; - } - // "item" returned a non-NULL value, and it was a valid INET6 - value->m_string.set(str->ptr(), str->length(), str->charset()); - } - return check_null(item, value); -} - - -void Type_handler_inet6::Item_param_setup_conversion(THD *thd, - Item_param *param) const -{ - param->setup_conversion_string(thd, thd->variables.character_set_client); -} - - -void Type_handler_inet6::make_sort_key_part(uchar *to, Item *item, - const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const -{ - DBUG_ASSERT(item->type_handler() == this); - NativeBufferInet6 tmp; - item->val_native_result(current_thd, &tmp); - if (item->maybe_null()) - { - if (item->null_value) - { - memset(to, 0, Inet6::binary_length() + 1); - return; - } - *to++= 1; - } - DBUG_ASSERT(!item->null_value); - DBUG_ASSERT(Inet6::binary_length() == tmp.length()); - DBUG_ASSERT(Inet6::binary_length() == sort_field->length); - memcpy(to, tmp.ptr(), tmp.length()); -} - -uint -Type_handler_inet6::make_packed_sort_key_part(uchar *to, Item *item, - const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const -{ - DBUG_ASSERT(item->type_handler() == this); - NativeBufferInet6 tmp; - item->val_native_result(current_thd, &tmp); - if (item->maybe_null()) - { - if (item->null_value) - { - *to++=0; - return 0; - } - *to++= 1; - } - DBUG_ASSERT(!item->null_value); - DBUG_ASSERT(Inet6::binary_length() == tmp.length()); - DBUG_ASSERT(Inet6::binary_length() == sort_field->length); - memcpy(to, tmp.ptr(), tmp.length()); - return tmp.length(); -} - -void Type_handler_inet6::sort_length(THD *thd, - const Type_std_attributes *item, - SORT_FIELD_ATTR *attr) const -{ - attr->original_length= attr->length= Inet6::binary_length(); - attr->suffix_length= 0; -} - - -cmp_item *Type_handler_inet6::make_cmp_item(THD *thd, CHARSET_INFO *cs) const -{ - return new (thd->mem_root) cmp_item_inet6; -} - - - -in_vector * -Type_handler_inet6::make_in_vector(THD *thd, const Item_func_in *func, - uint nargs) const -{ - return new (thd->mem_root) in_inet6(thd, nargs); -} - - -Item *Type_handler_inet6::create_typecast_item(THD *thd, Item *item, - const Type_cast_attributes &attr) - const -{ - return new (thd->mem_root) Item_typecast_inet6(thd, item); -} - - -Item_cache *Type_handler_inet6::Item_get_cache(THD *thd, const Item *item) const -{ - return new (thd->mem_root) Item_cache_inet6(thd); -} - - -Item_copy *Type_handler_inet6::create_item_copy(THD *thd, Item *item) const -{ - return new (thd->mem_root) Item_copy_inet6(thd, item); -} - - -Item * -Type_handler_inet6::make_const_item_for_comparison(THD *thd, - Item *src, - const Item *cmp) const -{ - Inet6_null tmp(src); - if (tmp.is_null()) - return new (thd->mem_root) Item_null(thd, src->name.str); - return new (thd->mem_root) Item_literal_inet6(thd, tmp); -} - - -Item *Field_inet6::get_equal_const_item(THD *thd, const Context &ctx, - Item *const_item) -{ - Inet6_null tmp(const_item); - if (tmp.is_null()) - return NULL; - return new (thd->mem_root) Item_literal_inet6(thd, tmp); -} - - -Field * -Type_handler_inet6::make_table_field_from_def( - TABLE_SHARE *share, - MEM_ROOT *mem_root, - const LEX_CSTRING *name, - const Record_addr &addr, - const Bit_addr &bit, - const Column_definition_attributes *attr, - uint32 flags) const -{ - return new (mem_root) Field_inet6(name, addr); -} - - -Field *Type_handler_inet6::make_table_field(MEM_ROOT *root, - const LEX_CSTRING *name, - const Record_addr &addr, - const Type_all_attributes &attr, - TABLE_SHARE *share) const -{ - return new (root) Field_inet6(name, addr); -} - - -Field *Type_handler_inet6::make_conversion_table_field(MEM_ROOT *root, - TABLE *table, - uint metadata, - const Field *target) - const -{ - const Record_addr tmp(NULL, Bit_addr(true)); - return new (table->in_use->mem_root) Field_inet6(&empty_clex_str, tmp); -} - - -bool Type_handler_inet6::partition_field_check(const LEX_CSTRING &field_name, - Item *item_expr) const -{ - if (item_expr->cmp_type() != STRING_RESULT) - { - my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0)); - return true; - } - return false; -} - - -bool -Type_handler_inet6::partition_field_append_value( - String *to, - Item *item_expr, - CHARSET_INFO *field_cs, - partition_value_print_mode_t mode) - const -{ - StringBufferInet6 inet6str; - Inet6_null inet6(item_expr); - if (inet6.is_null()) - { - my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); - return true; - } - return inet6.to_string(&inet6str) || - to->append('\'') || - to->append(inet6str) || - to->append('\''); -} - - -/***************************************************************/ - - -class Type_collection_inet: public Type_collection -{ - const Type_handler *aggregate_common(const Type_handler *a, - const Type_handler *b) const - { - if (a == b) - return a; - return NULL; - } - const Type_handler *aggregate_if_string(const Type_handler *a, - const Type_handler *b) const - { - static const Type_aggregator::Pair agg[]= - { - {&type_handler_inet6, &type_handler_null, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_varchar, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_string, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_tiny_blob, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_blob, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_medium_blob, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_long_blob, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_hex_hybrid, &type_handler_inet6}, - {NULL,NULL,NULL} - }; - return Type_aggregator::find_handler_in_array(agg, a, b, true); - } -public: - const Type_handler *aggregate_for_result(const Type_handler *a, - const Type_handler *b) - const override - { - const Type_handler *h; - if ((h= aggregate_common(a, b)) || - (h= aggregate_if_string(a, b))) - return h; - return NULL; - } - - const Type_handler *aggregate_for_min_max(const Type_handler *a, - const Type_handler *b) - const override - { - return aggregate_for_result(a, b); - } - - const Type_handler *aggregate_for_comparison(const Type_handler *a, - const Type_handler *b) - const override - { - if (const Type_handler *h= aggregate_common(a, b)) - return h; - static const Type_aggregator::Pair agg[]= - { - {&type_handler_inet6, &type_handler_null, &type_handler_inet6}, - {&type_handler_inet6, &type_handler_long_blob, &type_handler_inet6}, - {NULL,NULL,NULL} - }; - return Type_aggregator::find_handler_in_array(agg, a, b, true); - } - - const Type_handler *aggregate_for_num_op(const Type_handler *a, - const Type_handler *b) - const override - { - return NULL; - } - - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - if (type_handler_inet6.name().eq(name)) - return &type_handler_inet6; - return NULL; - } -}; - - -const Type_collection *Type_handler_inet6::type_collection() const -{ - static Type_collection_inet type_collection_inet; - return &type_collection_inet; + static Name def(STRING_WITH_LEN("::")); + return def; } diff --git a/plugin/type_inet/sql_type_inet.h b/plugin/type_inet/sql_type_inet.h index 109718637c6..b0560f1ffe0 100644 --- a/plugin/type_inet/sql_type_inet.h +++ b/plugin/type_inet/sql_type_inet.h @@ -1,8 +1,8 @@ #ifndef SQL_TYPE_INET_H #define SQL_TYPE_INET_H -/* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. +/* Copyright (c) 2011,2013, Oracle and/or its affiliates. Copyright (c) 2014 MariaDB Foundation - Copyright (c) 2019 MariaDB Corporation + Copyright (c) 2019,2021 MariaDB Corporation This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -31,14 +31,20 @@ static const size_t IN6_ADDR_NUM_WORDS= IN6_ADDR_SIZE / 2; */ static const uint IN6_ADDR_MAX_CHAR_LENGTH= 8 * 4 + 7; +#include "sql_type_fixedbin_storage.h" -class NativeBufferInet6: public NativeBuffer +class Inet6: public FixedBinTypeStorage { +public: + using FixedBinTypeStorage::FixedBinTypeStorage; + bool ascii_to_fbt(const char *str, size_t str_length); + size_t to_string(char *dst, size_t dstsize) const; + static const Name &default_value(); }; -class StringBufferInet6: public StringBuffer -{ -}; + +#include "sql_type_fixedbin.h" +typedef FixedBinTypeBundle Inet6Bundle; /***********************************************************************/ @@ -132,898 +138,4 @@ public: } }; - -class Inet6 -{ -protected: - char m_buffer[IN6_ADDR_SIZE]; - bool make_from_item(Item *item, bool warn); - bool ascii_to_ipv6(const char *str, size_t str_length); - bool character_string_to_ipv6(const char *str, size_t str_length, - CHARSET_INFO *cs) - { - if (cs->state & MY_CS_NONASCII) - { - char tmp[IN6_ADDR_MAX_CHAR_LENGTH]; - String_copier copier; - uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp), - cs, str, str_length); - return ascii_to_ipv6(tmp, length); - } - return ascii_to_ipv6(str, str_length); - } - bool make_from_character_or_binary_string(const String *str, bool warn); - bool binary_to_ipv6(const char *str, size_t length) - { - if (length != sizeof(m_buffer)) - return true; - memcpy(m_buffer, str, length); - return false; - } - - Inet6() { } - -public: - static uint binary_length() { return IN6_ADDR_SIZE; } - /** - Non-abbreviated syntax is 8 groups, up to 4 digits each, - plus 7 delimiters between the groups. - Abbreviated syntax is even shorter. - */ - static uint max_char_length() { return IN6_ADDR_MAX_CHAR_LENGTH; } - - static bool only_zero_bytes(const char *ptr, uint length) - { - for (uint i= 0 ; i < length; i++) - { - if (ptr[i] != 0) - return false; - } - return true; - } - - /* - Check at Item's fix_fields() time if "item" can return a nullable value - on conversion to INET6, or conversion produces a NOT NULL INET6 value. - */ - static bool fix_fields_maybe_null_on_conversion_to_inet6(Item *item); - -public: - - Inet6(Item *item, bool *error, bool warn= true) - { - *error= make_from_item(item, warn); - } - void to_binary(char *str, size_t str_size) const - { - DBUG_ASSERT(str_size >= sizeof(m_buffer)); - memcpy(str, m_buffer, sizeof(m_buffer)); - } - bool to_binary(String *to) const - { - return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin); - } - bool to_native(Native *to) const - { - return to->copy(m_buffer, sizeof(m_buffer)); - } - size_t to_string(char *dst, size_t dstsize) const; - bool to_string(String *to) const - { - to->set_charset(&my_charset_latin1); - if (to->alloc(INET6_ADDRSTRLEN)) - return true; - to->length((uint32) to_string((char*) to->ptr(), INET6_ADDRSTRLEN)); - return false; - } - bool is_v4compat() const - { - static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size"); - return IN6_IS_ADDR_V4COMPAT((struct in6_addr *) m_buffer); - } - bool is_v4mapped() const - { - static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size"); - return IN6_IS_ADDR_V4MAPPED((struct in6_addr *) m_buffer); - } - int cmp(const char *str, size_t length) const - { - DBUG_ASSERT(length == sizeof(m_buffer)); - return memcmp(m_buffer, str, length); - } - int cmp(const Binary_string &other) const - { - return cmp(other.ptr(), other.length()); - } - int cmp(const Inet6 &other) const - { - return memcmp(m_buffer, other.m_buffer, sizeof(m_buffer)); - } -}; - - -class Inet6_zero: public Inet6 -{ -public: - Inet6_zero() - { - bzero(&m_buffer, sizeof(m_buffer)); - } -}; - - -class Inet6_null: public Inet6, public Null_flag -{ -public: - // Initialize from a text representation - Inet6_null(const char *str, size_t length, CHARSET_INFO *cs) - :Null_flag(character_string_to_ipv6(str, length, cs)) - { } - Inet6_null(const String &str) - :Inet6_null(str.ptr(), str.length(), str.charset()) - { } - // Initialize from a binary representation - Inet6_null(const char *str, size_t length) - :Null_flag(binary_to_ipv6(str, length)) - { } - Inet6_null(const Binary_string &str) - :Inet6_null(str.ptr(), str.length()) - { } - // Initialize from an Item - Inet6_null(Item *item, bool warn= true) - :Null_flag(make_from_item(item, warn)) - { } -public: - const Inet6& to_inet6() const - { - DBUG_ASSERT(!is_null()); - return *this; - } - void to_binary(char *str, size_t str_size) const - { - to_inet6().to_binary(str, str_size); - } - bool to_binary(String *to) const - { - return to_inet6().to_binary(to); - } - size_t to_string(char *dst, size_t dstsize) const - { - return to_inet6().to_string(dst, dstsize); - } - bool to_string(String *to) const - { - return to_inet6().to_string(to); - } - bool is_v4compat() const - { - return to_inet6().is_v4compat(); - } - bool is_v4mapped() const - { - return to_inet6().is_v4mapped(); - } -}; - - -class Type_std_attributes_inet6: public Type_std_attributes -{ -public: - Type_std_attributes_inet6() - :Type_std_attributes( - Type_numeric_attributes(Inet6::max_char_length(), 0, true), - DTCollation_numeric()) - { } -}; - - -class Type_handler_inet6: public Type_handler -{ - bool character_or_binary_string_to_native(THD *thd, const String *str, - Native *to) const; -public: - ~Type_handler_inet6() override {} - - const Type_collection *type_collection() const override; - const Name &default_value() const override - { - static Name def(STRING_WITH_LEN("::")); - return def; - } - protocol_send_type_t protocol_send_type() const override - { - return PROTOCOL_SEND_STRING; - } - bool Item_append_extended_type_info(Send_field_extended_metadata *to, - const Item *item) const override - { - return to->set_data_type_name(name().lex_cstring()); - } - - enum_field_types field_type() const override - { - return MYSQL_TYPE_STRING; - } - - Item_result result_type() const override - { - return STRING_RESULT; - } - - Item_result cmp_type() const override - { - return STRING_RESULT; - } - - enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) - const override - { - return DYN_COL_STRING; - } - - uint32 max_display_length_for_field(const Conv_source &src) const override - { - return Inet6::max_char_length(); - } - - const Type_handler *type_handler_for_comparison() const override - { - return this; - } - - int - stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override - { - DBUG_ASSERT(field->type_handler() == this); - Inet6_null ni(item); // Convert Item to INET6 - if (ni.is_null()) - return 0; - NativeBufferInet6 tmp; - if (field->val_native(&tmp)) - { - DBUG_ASSERT(0); - return 0; - } - return -ni.cmp(tmp); - } - CHARSET_INFO *charset_for_protocol(const Item *item) const override - { - return item->collation.collation; - } - - bool is_scalar_type() const override { return true; } - bool is_val_native_ready() const override { return true; } - bool can_return_int() const override { return false; } - bool can_return_decimal() const override { return false; } - bool can_return_real() const override { return false; } - bool can_return_str() const override { return true; } - bool can_return_text() const override { return true; } - bool can_return_date() const override { return false; } - bool can_return_time() const override { return false; } - bool convert_to_binary_using_val_native() const override { return true; } - - decimal_digits_t Item_time_precision(THD *thd, Item *item) const override - { - return 0; - } - decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override - { - return 0; - } - decimal_digits_t Item_decimal_scale(const Item *item) const override - { - return 0; - } - decimal_digits_t Item_decimal_precision(const Item *item) const override - { - /* - This will be needed if we ever allow cast from INET6 to DECIMAL. - Decimal precision of INET6 is 39 digits: - 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' = - 340282366920938463463374607431768211456 = 39 digits - */ - return 39; - } - - /* - Returns how many digits a divisor adds into a division result. - See Item::divisor_precision_increment() in item.h for more comments. - */ - decimal_digits_t Item_divisor_precision_increment(const Item *) const override - { - return 0; - } - /** - Makes a temporary table Field to handle numeric aggregate functions, - e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc. - */ - Field *make_num_distinct_aggregator_field(MEM_ROOT *, - const Item *) const override - { - DBUG_ASSERT(0); - return 0; - } - Field *make_conversion_table_field(MEM_ROOT *root, - TABLE *TABLE, - uint metadata, - const Field *target) const override; - // Fix attributes after the parser - bool Column_definition_fix_attributes(Column_definition *c) const override - { - c->length= Inet6::max_char_length(); - return false; - } - - bool Column_definition_prepare_stage1(THD *thd, - MEM_ROOT *mem_root, - Column_definition *def, - handler *file, - ulonglong table_flags, - const Column_derived_attributes - *derived_attr) - const override - { - def->prepare_stage1_simple(&my_charset_numeric); - return false; - } - - bool Column_definition_redefine_stage1(Column_definition *def, - const Column_definition *dup, - const handler *file) - const override - { - def->redefine_stage1_common(dup, file); - def->set_compression_method(dup->compression_method()); - def->create_length_to_internal_length_string(); - return false; - } - - bool Column_definition_prepare_stage2(Column_definition *def, - handler *file, - ulonglong table_flags) const override - { - def->pack_flag= FIELDFLAG_BINARY; - return false; - } - - bool partition_field_check(const LEX_CSTRING &field_name, - Item *item_expr) const override; - - bool partition_field_append_value(String *to, - Item *item_expr, - CHARSET_INFO *field_cs, - partition_value_print_mode_t mode) - const override; - - Field *make_table_field(MEM_ROOT *root, - const LEX_CSTRING *name, - const Record_addr &addr, - const Type_all_attributes &attr, - TABLE_SHARE *table) const override; - - Field * - make_table_field_from_def(TABLE_SHARE *share, - MEM_ROOT *mem_root, - const LEX_CSTRING *name, - const Record_addr &addr, - const Bit_addr &bit, - const Column_definition_attributes *attr, - uint32 flags) const override; - void - Column_definition_attributes_frm_pack(const Column_definition_attributes *def, - uchar *buff) const override - { - def->frm_pack_basic(buff); - def->frm_pack_charset(buff); - } - bool - Column_definition_attributes_frm_unpack(Column_definition_attributes *def, - TABLE_SHARE *share, - const uchar *buffer, - LEX_CUSTRING *gis_options) - const override - { - def->frm_unpack_basic(buffer); - return def->frm_unpack_charset(share, buffer); - } - void make_sort_key_part(uchar *to, Item *item, - const SORT_FIELD_ATTR *sort_field, - Sort_param *param) - const override; - uint make_packed_sort_key_part(uchar *to, Item *item, - const SORT_FIELD_ATTR *sort_field, - Sort_param *param) const override; - void sort_length(THD *thd, - const Type_std_attributes *item, - SORT_FIELD_ATTR *attr) const override; - uint32 max_display_length(const Item *item) const override - { - return Inet6::max_char_length(); - } - uint32 calc_pack_length(uint32 length) const override - { - return Inet6::binary_length(); - } - void Item_update_null_value(Item *item) const override - { - NativeBufferInet6 tmp; - item->val_native(current_thd, &tmp); - } - bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override; - void Item_param_setup_conversion(THD *thd, Item_param *param) const override; - void Item_param_set_param_func(Item_param *param, - uchar **pos, ulong len) const override - { - param->set_param_str(pos, len); - } - bool Item_param_set_from_value(THD *thd, - Item_param *param, - const Type_all_attributes *attr, - const st_value *val) const override - { - param->unsigned_flag= false;//QQ - param->setup_conversion_string(thd, attr->collation.collation); - /* - Exact value of max_length is not known unless data is converted to - charset of connection, so we have to set it later. - */ - return param->set_str(val->m_string.ptr(), val->m_string.length(), - attr->collation.collation, - attr->collation.collation); - } - bool Item_param_val_native(THD *thd, Item_param *item, Native *to) - const override - { - StringBufferInet6 buffer; - String *str= item->val_str(&buffer); - if (!str) - return true; - Inet6_null tmp(*str); - return tmp.is_null() || tmp.to_native(to); - } - bool Item_send(Item *item, Protocol *p, st_value *buf) const override - { - return Item_send_str(item, p, buf); - } - int Item_save_in_field(Item *item, Field *field, bool no_conversions) - const override - { - if (field->type_handler() == this) - { - NativeBuffer tmp; - bool rc= item->val_native(current_thd, &tmp); - if (rc || item->null_value) - return set_field_to_null_with_conversions(field, no_conversions); - field->set_notnull(); - return field->store_native(tmp); - } - return item->save_str_in_field(field, no_conversions); - } - - String *print_item_value(THD *thd, Item *item, String *str) const override - { - StringBufferInet6 buf; - String *result= item->val_str(&buf); - /* - TODO: This should eventually use one of these notations: - 1. CAST('::' AS INET6) - Problem: CAST is not supported as a NAME_CONST() argument. - 2. INET6':: - Problem: This syntax is not supported by the parser yet. - */ - return !result || - str->realloc(result->length() + 2) || - str->append(STRING_WITH_LEN("'")) || - str->append(result->ptr(), result->length()) || - str->append(STRING_WITH_LEN("'")) ? - NULL : - str; - } - - /** - Check if - WHERE expr=value AND expr=const - can be rewritten as: - WHERE const=value AND expr=const - - "this" is the comparison handler that is used by "target". - - @param target - the predicate expr=value, - whose "expr" argument will be replaced to "const". - @param target_expr - the target's "expr" which will be replaced to "const". - @param target_value - the target's second argument, it will remain unchanged. - @param source - the equality predicate expr=const (or expr<=>const) - that can be used to rewrite the "target" part - (under certain conditions, see the code). - @param source_expr - the source's "expr". It should be exactly equal to - the target's "expr" to make condition rewrite possible. - @param source_const - the source's "const" argument, it will be inserted - into "target" instead of "expr". - */ - bool - can_change_cond_ref_to_const(Item_bool_func2 *target, - Item *target_expr, Item *target_value, - Item_bool_func2 *source, - Item *source_expr, Item *source_const) - const override - { - /* - WHERE COALESCE(inet6_col)='::1' AND COALESCE(inet6_col)=CONCAT(a); --> - WHERE COALESCE(inet6_col)='::1' AND '::1'=CONCAT(a); - */ - return target->compare_type_handler() == source->compare_type_handler(); - } - bool - subquery_type_allows_materialization(const Item *inner, - const Item *outer, bool) const override - { - /* - Example: - SELECT * FROM t1 WHERE a IN (SELECT inet6col FROM t1 GROUP BY inet6col); - Allow materialization only if the outer column is also INET6. - This can be changed for more relaxed rules in the future. - */ - DBUG_ASSERT(inner->type_handler() == this); - return outer->type_handler() == this; - } - /** - Make a simple constant replacement item for a constant "src", - so the new item can futher be used for comparison with "cmp", e.g.: - src = cmp -> replacement = cmp - - "this" is the type handler that is used to compare "src" and "cmp". - - @param thd - current thread, for mem_root - @param src - The item that we want to replace. It's a const item, - but it can be complex enough to calculate on every row. - @param cmp - The src's comparand. - @retval - a pointer to the created replacement Item - @retval - NULL, if could not create a replacement (e.g. on EOM). - NULL is also returned for ROWs, because instead of replacing - a Item_row to a new Item_row, Type_handler_row just replaces - its elements. - */ - Item *make_const_item_for_comparison(THD *thd, - Item *src, - const Item *cmp) const override; - Item_cache *Item_get_cache(THD *thd, const Item *item) const override; - - Item *create_typecast_item(THD *thd, Item *item, - const Type_cast_attributes &attr) const override; - - Item_copy *create_item_copy(THD *thd, Item *item) const override; - int cmp_native(const Native &a, const Native &b) const override - { - DBUG_ASSERT(a.length() == Inet6::binary_length()); - DBUG_ASSERT(b.length() == Inet6::binary_length()); - return memcmp(a.ptr(), b.ptr(), Inet6::binary_length()); - } - bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override - { - return cmp->set_cmp_func_native(thd); - } - bool Item_const_eq(const Item_const *a, const Item_const *b, - bool binary_cmp) const override - { - return false;//QQ - } - bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, - Item *a, Item *b) const override - { - Inet6_null na(a); - Inet6_null nb(b); - return !na.is_null() && !nb.is_null() && !na.cmp(nb); - } - bool Item_hybrid_func_fix_attributes(THD *thd, - const LEX_CSTRING &name, - Type_handler_hybrid_field_type *h, - Type_all_attributes *attr, - Item **items, - uint nitems) const override - { - attr->Type_std_attributes::operator=(Type_std_attributes_inet6()); - h->set_handler(this); - /* - If some of the arguments cannot be safely converted to "INET6 NOT NULL", - then mark the entire function nullability as NULL-able. - Otherwise, keep the generic nullability calculated by earlier stages: - - either by the most generic way in Item_func::fix_fields() - - or by Item_func_xxx::fix_length_and_dec() before the call of - Item_hybrid_func_fix_attributes() - IFNULL() is special. It does not need to test args[0]. - */ - uint first= dynamic_cast(attr) ? 1 : 0; - for (uint i= first; i < nitems; i++) - { - if (Inet6::fix_fields_maybe_null_on_conversion_to_inet6(items[i])) - { - attr->set_type_maybe_null(true); - break; - } - } - return false; - } - bool Item_func_min_max_fix_attributes(THD *thd, - Item_func_min_max *func, - Item **items, - uint nitems) const override - { - return Item_hybrid_func_fix_attributes(thd, func->func_name_cstring(), - func, func, items, nitems); - - } - bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override - { - func->Type_std_attributes::operator=(Type_std_attributes_inet6()); - func->set_handler(this); - return false; - } - bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - - bool Item_val_native_with_conversion(THD *thd, Item *item, - Native *to) const override - { - if (item->type_handler() == this) - return item->val_native(thd, to); // No conversion needed - StringBufferInet6 buffer; - String *str= item->val_str(&buffer); - return str ? character_or_binary_string_to_native(thd, str, to) : true; - } - bool Item_val_native_with_conversion_result(THD *thd, Item *item, - Native *to) const override - { - if (item->type_handler() == this) - return item->val_native_result(thd, to); // No conversion needed - StringBufferInet6 buffer; - String *str= item->str_result(&buffer); - return str ? character_or_binary_string_to_native(thd, str, to) : true; - } - - bool Item_val_bool(Item *item) const override - { - NativeBufferInet6 tmp; - if (item->val_native(current_thd, &tmp)) - return false; - return !Inet6::only_zero_bytes(tmp.ptr(), tmp.length()); - } - void Item_get_date(THD *thd, Item *item, - Temporal::Warn *buff, MYSQL_TIME *ltime, - date_mode_t fuzzydate) const override - { - set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); - } - - longlong Item_val_int_signed_typecast(Item *item) const override - { - DBUG_ASSERT(0); - return 0; - } - - longlong Item_val_int_unsigned_typecast(Item *item) const override - { - DBUG_ASSERT(0); - return 0; - } - - String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) - const override - { - NativeBufferInet6 tmp; - if ((item->null_value= item->arguments()[0]->val_native(current_thd, &tmp))) - return NULL; - DBUG_ASSERT(tmp.length() == Inet6::binary_length()); - if (str->set_hex(tmp.ptr(), tmp.length())) - { - str->length(0); - str->set_charset(item->collation.collation); - } - return str; - } - - String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *item, - String *str) const override - { - NativeBufferInet6 native; - if (item->val_native(current_thd, &native)) - { - DBUG_ASSERT(item->null_value); - return NULL; - } - DBUG_ASSERT(native.length() == Inet6::binary_length()); - Inet6_null tmp(native.ptr(), native.length()); - return tmp.is_null() || tmp.to_string(str) ? NULL : str; - } - double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) - const override - { - return 0; - } - longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) - const override - { - return 0; - } - my_decimal * - Item_func_hybrid_field_type_val_decimal(Item_func_hybrid_field_type *, - my_decimal *to) const override - { - my_decimal_set_zero(to); - return to; - } - void Item_func_hybrid_field_type_get_date(THD *, - Item_func_hybrid_field_type *, - Temporal::Warn *, - MYSQL_TIME *to, - date_mode_t fuzzydate) - const override - { - set_zero_time(to, MYSQL_TIMESTAMP_TIME); - } - // WHERE is Item_func_min_max_val_native??? - String *Item_func_min_max_val_str(Item_func_min_max *func, String *str) - const override - { - Inet6_null tmp(func); - return tmp.is_null() || tmp.to_string(str) ? NULL : str; - } - double Item_func_min_max_val_real(Item_func_min_max *) const override - { - return 0; - } - longlong Item_func_min_max_val_int(Item_func_min_max *) const override - { - return 0; - } - my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, - my_decimal *to) const override - { - my_decimal_set_zero(to); - return to; - } - bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, - MYSQL_TIME *to, date_mode_t fuzzydate) - const override - { - set_zero_time(to, MYSQL_TIMESTAMP_TIME); - return false; - } - - bool - Item_func_between_fix_length_and_dec(Item_func_between *func) const override - { - return false; - } - longlong Item_func_between_val_int(Item_func_between *func) const override - { - return func->val_int_cmp_native(); - } - - cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override; - - in_vector *make_in_vector(THD *thd, const Item_func_in *func, - uint nargs) const override; - - bool Item_func_in_fix_comparator_compatible_types(THD *thd, - Item_func_in *func) - const override - { - if (func->compatible_types_scalar_bisection_possible()) - { - return func->value_list_convert_const_to_int(thd) || - func->fix_for_scalar_comparison_using_bisection(thd); - } - return - func->fix_for_scalar_comparison_using_cmp_items(thd, - 1U << (uint) STRING_RESULT); - } - bool - Item_func_round_fix_length_and_dec(Item_func_round *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - bool - Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - - bool Item_func_abs_fix_length_and_dec(Item_func_abs *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - - bool Item_func_neg_fix_length_and_dec(Item_func_neg *func) const override - { - return Item_func_or_sum_illegal_param(func); - } - - bool - Item_func_signed_fix_length_and_dec(Item_func_signed *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) - const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) - const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) - const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) - const override; - bool - Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) - const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_plus_fix_length_and_dec(Item_func_plus *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_minus_fix_length_and_dec(Item_func_minus *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_mul_fix_length_and_dec(Item_func_mul *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_div_fix_length_and_dec(Item_func_div *item) const override - { - return Item_func_or_sum_illegal_param(item); - } - bool - Item_func_mod_fix_length_and_dec(Item_func_mod *item) const override - { - return Item_func_or_sum_illegal_param(item); - } -}; - - -extern MYSQL_PLUGIN_IMPORT Type_handler_inet6 type_handler_inet6; - - #endif /* SQL_TYPE_INET_H */ diff --git a/sql/sql_string.h b/sql/sql_string.h index b398d4437ee..a8c29eba22c 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -484,6 +484,11 @@ public: if (str.Alloced_length) Alloced_length= (uint32) (str.Alloced_length - offset); } + LEX_CSTRING to_lex_cstring() const + { + LEX_CSTRING tmp= {Ptr, str_length}; + return tmp; + } inline LEX_CSTRING *get_value(LEX_CSTRING *res) { res->str= Ptr; diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h new file mode 100644 index 00000000000..546d585cc69 --- /dev/null +++ b/sql/sql_type_fixedbin.h @@ -0,0 +1,1913 @@ +#ifndef SQL_TYPE_FIXEDBIN_H +#define SQL_TYPE_FIXEDBIN_H +/* Copyright (c) 2019,2021 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + This is a common code for plugin (?) types that are generally + handled like strings, but have their own fixed size on-disk binary storage + format and their own (variable size) canonical string representation. + + Examples are INET6 and UUID types. +*/ + +#define MYSQL_SERVER +#include "sql_class.h" // THD, SORT_FIELD_ATTR +#include "opt_range.h" // SEL_ARG, null_element +#include "sql_type_fixedbin_storage.h" + +/***********************************************************************/ + + +template +class FixedBinTypeBundle +{ +public: + class Fbt: public FbtImpl + { + protected: + using FbtImpl::m_buffer; + bool make_from_item(Item *item, bool warn) + { + if (item->type_handler() == type_handler_fbt()) + { + Native tmp(m_buffer, sizeof(m_buffer)); + bool rc= item->val_native(current_thd, &tmp); + if (rc) + return true; + DBUG_ASSERT(tmp.length() == sizeof(m_buffer)); + if (tmp.ptr() != m_buffer) + memcpy(m_buffer, tmp.ptr(), sizeof(m_buffer)); + return false; + } + StringBuffer tmp; + String *str= item->val_str(&tmp); + return str ? make_from_character_or_binary_string(str, warn) : true; + } + + bool character_string_to_fbt(const char *str, size_t str_length, + CHARSET_INFO *cs) + { + if (cs->state & MY_CS_NONASCII) + { + char tmp[FbtImpl::max_char_length()+1]; + String_copier copier; + uint length= copier.well_formed_copy(&my_charset_latin1, tmp, sizeof(tmp), + cs, str, str_length); + return FbtImpl::ascii_to_fbt(tmp, length); + } + return FbtImpl::ascii_to_fbt(str, str_length); + } + bool make_from_character_or_binary_string(const String *str, bool warn) + { + if (str->charset() != &my_charset_bin) + { + bool rc= character_string_to_fbt(str->ptr(), str->length(), + str->charset()); + if (rc && warn) + current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + type_handler_fbt()->name().ptr(), ErrConvString(str).ptr()); + return rc; + } + if (str->length() != sizeof(m_buffer)) + { + if (warn) + current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + type_handler_fbt()->name().ptr(), ErrConvString(str).ptr()); + return true; + } + DBUG_ASSERT(str->ptr() != m_buffer); + memcpy(m_buffer, str->ptr(), sizeof(m_buffer)); + return false; + } + bool binary_to_fbt(const char *str, size_t length) + { + if (length != sizeof(m_buffer)) + return true; + memcpy(m_buffer, str, length); + return false; + } + + Fbt() { } + + public: + + static Fbt zero() + { + Fbt fbt; + fbt.set_zero(); + return fbt; + } + + static Fbt record_to_memory(const char *ptr) + { + Fbt fbt; + FbtImpl::record_to_memory(fbt.m_buffer, ptr); + return fbt; + } + /* + Check at Item's fix_fields() time if "item" can return a nullable value + on conversion to Fbt, or conversion produces a NOT NULL Fbt value. + */ + static bool fix_fields_maybe_null_on_conversion_to_fbt(Item *item) + { + if (item->maybe_null()) + return true; + if (item->type_handler() == type_handler_fbt()) + return false; + if (!item->const_item() || item->is_expensive()) + return true; + return Fbt_null(item, false).is_null(); + } + + public: + + Fbt(Item *item, bool *error, bool warn= true) + { + *error= make_from_item(item, warn); + } + void to_record(char *str, size_t str_size) const + { + DBUG_ASSERT(str_size >= sizeof(m_buffer)); + FbtImpl::memory_to_record(str, m_buffer); + } + bool to_binary(String *to) const + { + return to->copy(m_buffer, sizeof(m_buffer), &my_charset_bin); + } + bool to_native(Native *to) const + { + return to->copy(m_buffer, sizeof(m_buffer)); + } + bool to_string(String *to) const + { + to->set_charset(&my_charset_latin1); + if (to->alloc(FbtImpl::max_char_length()+1)) + return true; + to->length((uint32) FbtImpl::to_string(const_cast(to->ptr()), + FbtImpl::max_char_length()+1)); + return false; + } + int cmp(const Binary_string &other) const + { + return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring()); + } + int cmp(const Fbt &other) const + { + return FbtImpl::cmp(FbtImpl::to_lex_cstring(), other.to_lex_cstring()); + } + }; + + class Fbt_null: public Fbt, public Null_flag + { + public: + // Initialize from a text representation + Fbt_null(const char *str, size_t length, CHARSET_INFO *cs) + :Null_flag(Fbt::character_string_to_fbt(str, length, cs)) { } + Fbt_null(const String &str) + :Fbt_null(str.ptr(), str.length(), str.charset()) { } + // Initialize from a binary representation + Fbt_null(const char *str, size_t length) + :Null_flag(Fbt::binary_to_fbt(str, length)) { } + Fbt_null(const Binary_string &str) + :Fbt_null(str.ptr(), str.length()) { } + // Initialize from an Item + Fbt_null(Item *item, bool warn= true) + :Null_flag(Fbt::make_from_item(item, warn)) { } + public: + const Fbt& to_fbt() const + { + DBUG_ASSERT(!is_null()); + return *this; + } + void to_record(char *str, size_t str_size) const + { + to_fbt().to_record(str, str_size); + } + bool to_binary(String *to) const + { + return to_fbt().to_binary(to); + } + size_t to_string(char *dst, size_t dstsize) const + { + return to_fbt().to_string(dst, dstsize); + } + bool to_string(String *to) const + { + return to_fbt().to_string(to); + } + }; + + class Type_std_attributes_fbt: public Type_std_attributes + { + public: + Type_std_attributes_fbt() + :Type_std_attributes( + Type_numeric_attributes(FbtImpl::max_char_length(), 0, true), + DTCollation_numeric()) + { } + }; + + class Type_handler_fbt: public Type_handler + { + bool character_or_binary_string_to_native(THD *thd, const String *str, + Native *to) const + { + if (str->charset() == &my_charset_bin) + { + // Convert from a binary string + if (str->length() != FbtImpl::binary_length() || + to->copy(str->ptr(), str->length())) + { + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + return true; + } + return false; + } + // Convert from a character string + Fbt_null tmp(*str); + if (tmp.is_null()) + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + return tmp.is_null() || tmp.to_native(to); + } + + public: + ~Type_handler_fbt() override {} + + const Type_collection *type_collection() const override + { + static Type_collection_fbt type_collection_fbt; + return &type_collection_fbt; + } + + const Name &default_value() const override + { + return FbtImpl::default_value(); + } + ulong KEY_pack_flags(uint column_nr) const override + { + return FbtImpl::KEY_pack_flags(column_nr); + } + protocol_send_type_t protocol_send_type() const override + { + return PROTOCOL_SEND_STRING; + } + bool Item_append_extended_type_info(Send_field_extended_metadata *to, + const Item *item) const override + { + return to->set_data_type_name(name().lex_cstring()); + } + + enum_field_types field_type() const override + { + return MYSQL_TYPE_STRING; + } + + Item_result result_type() const override + { + return STRING_RESULT; + } + + Item_result cmp_type() const override + { + return STRING_RESULT; + } + + enum_dynamic_column_type dyncol_type(const Type_all_attributes *attr) + const override + { + return DYN_COL_STRING; + } + + uint32 max_display_length_for_field(const Conv_source &src) const override + { + return FbtImpl::max_char_length(); + } + + const Type_handler *type_handler_for_comparison() const override + { + return this; + } + + int stored_field_cmp_to_item(THD *thd, Field *field, Item *item) const override + { + DBUG_ASSERT(field->type_handler() == this); + Fbt_null ni(item); // Convert Item to Fbt + if (ni.is_null()) + return 0; + NativeBuffer tmp; + if (field->val_native(&tmp)) + { + DBUG_ASSERT(0); + return 0; + } + return -ni.cmp(tmp); + } + CHARSET_INFO *charset_for_protocol(const Item *item) const override + { + return item->collation.collation; + } + + bool is_scalar_type() const override { return true; } + bool is_val_native_ready() const override { return true; } + bool can_return_int() const override { return false; } + bool can_return_decimal() const override { return false; } + bool can_return_real() const override { return false; } + bool can_return_str() const override { return true; } + bool can_return_text() const override { return true; } + bool can_return_date() const override { return false; } + bool can_return_time() const override { return false; } + bool convert_to_binary_using_val_native() const override { return true; } + + decimal_digits_t Item_time_precision(THD *thd, Item *item) const override + { + return 0; + } + decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override + { + return 0; + } + decimal_digits_t Item_decimal_scale(const Item *item) const override + { + return 0; + } + decimal_digits_t Item_decimal_precision(const Item *item) const override + { + /* This will be needed if we ever allow cast from Fbt to DECIMAL. */ + return (FbtImpl::binary_length()*8+7)/10*3; // = bytes to decimal digits + } + + /* + Returns how many digits a divisor adds into a division result. + See Item::divisor_precision_increment() in item.h for more comments. + */ + decimal_digits_t Item_divisor_precision_increment(const Item *) const override + { + return 0; + } + /** + Makes a temporary table Field to handle numeric aggregate functions, + e.g. SUM(DISTINCT expr), AVG(DISTINCT expr), etc. + */ + Field *make_num_distinct_aggregator_field(MEM_ROOT *, const Item *) const override + { + DBUG_ASSERT(0); + return 0; + } + Field *make_conversion_table_field(MEM_ROOT *root, TABLE *table, uint metadata, + const Field *target) const override + { + const Record_addr tmp(NULL, Bit_addr(true)); + return new (table->in_use->mem_root) Field_fbt(&empty_clex_str, tmp); + } + // Fix attributes after the parser + bool Column_definition_fix_attributes(Column_definition *c) const override + { + c->length= FbtImpl::max_char_length(); + return false; + } + + bool Column_definition_prepare_stage1(THD *thd, MEM_ROOT *mem_root, + Column_definition *def, handler *file, + ulonglong table_flags, + const Column_derived_attributes *derived_attr) + const override + { + def->prepare_stage1_simple(&my_charset_numeric); + return false; + } + + bool Column_definition_redefine_stage1(Column_definition *def, + const Column_definition *dup, + const handler *file) const override + { + def->redefine_stage1_common(dup, file); + def->set_compression_method(dup->compression_method()); + def->create_length_to_internal_length_string(); + return false; + } + + bool Column_definition_prepare_stage2(Column_definition *def, handler *file, + ulonglong table_flags) const override + { + def->pack_flag= FIELDFLAG_BINARY; + return false; + } + + bool partition_field_check(const LEX_CSTRING &field_name, + Item *item_expr) const override + { + if (item_expr->cmp_type() != STRING_RESULT) + { + my_error(ER_WRONG_TYPE_COLUMN_VALUE_ERROR, MYF(0)); + return true; + } + return false; + } + + bool partition_field_append_value(String *to, Item *item_expr, + CHARSET_INFO *field_cs, + partition_value_print_mode_t mode) + const override + { + StringBuffer fbtstr; + Fbt_null fbt(item_expr); + if (fbt.is_null()) + { + my_error(ER_PARTITION_FUNCTION_IS_NOT_ALLOWED, MYF(0)); + return true; + } + return fbt.to_string(&fbtstr) || + to->append('\'') || + to->append(fbtstr) || + to->append('\''); + } + + Field *make_table_field(MEM_ROOT *root, const LEX_CSTRING *name, + const Record_addr &addr, + const Type_all_attributes &attr, + TABLE_SHARE *table) const override + { + return new (root) Field_fbt(name, addr); + } + + Field * make_table_field_from_def(TABLE_SHARE *share, MEM_ROOT *mem_root, + const LEX_CSTRING *name, const Record_addr &addr, + const Bit_addr &bit, + const Column_definition_attributes *attr, + uint32 flags) const override + { + return new (mem_root) Field_fbt(name, addr); + } + void Column_definition_attributes_frm_pack(const Column_definition_attributes *def, + uchar *buff) const override + { + def->frm_pack_basic(buff); + def->frm_pack_charset(buff); + } + bool Column_definition_attributes_frm_unpack(Column_definition_attributes *def, + TABLE_SHARE *share, const uchar *buffer, + LEX_CUSTRING *gis_options) + const override + { + def->frm_unpack_basic(buffer); + return def->frm_unpack_charset(share, buffer); + } + void make_sort_key_part(uchar *to, Item *item, const SORT_FIELD_ATTR *sort_field, + Sort_param *param) const override + { + DBUG_ASSERT(item->type_handler() == this); + NativeBuffer tmp; + item->val_native_result(current_thd, &tmp); + if (item->maybe_null()) + { + if (item->null_value) + { + memset(to, 0, FbtImpl::binary_length() + 1); + return; + } + *to++= 1; + } + DBUG_ASSERT(!item->null_value); + DBUG_ASSERT(FbtImpl::binary_length() == tmp.length()); + DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length); + FbtImpl::memory_to_record((char*) to, tmp.ptr()); + } + uint make_packed_sort_key_part(uchar *to, Item *item, + const SORT_FIELD_ATTR *sort_field, + Sort_param *param) const override + { + DBUG_ASSERT(item->type_handler() == this); + NativeBuffer tmp; + item->val_native_result(current_thd, &tmp); + if (item->maybe_null()) + { + if (item->null_value) + { + *to++=0; + return 0; + } + *to++= 1; + } + DBUG_ASSERT(!item->null_value); + DBUG_ASSERT(FbtImpl::binary_length() == tmp.length()); + DBUG_ASSERT(FbtImpl::binary_length() == sort_field->length); + FbtImpl::memory_to_record((char*) to, tmp.ptr()); + return tmp.length(); + } + void sort_length(THD *thd, const Type_std_attributes *item, + SORT_FIELD_ATTR *attr) const override + { + attr->original_length= attr->length= FbtImpl::binary_length(); + attr->suffix_length= 0; + } + uint32 max_display_length(const Item *item) const override + { + return FbtImpl::max_char_length(); + } + uint32 calc_pack_length(uint32 length) const override + { + return FbtImpl::binary_length(); + } + void Item_update_null_value(Item *item) const override + { + NativeBuffer tmp; + item->val_native(current_thd, &tmp); + } + bool Item_save_in_value(THD *thd, Item *item, st_value *value) const override + { + value->m_type= DYN_COL_STRING; + String *str= item->val_str(&value->m_string); + if (str != &value->m_string && !item->null_value) + { + // "item" returned a non-NULL value + if (Fbt_null(*str).is_null()) + { + /* + The value was not-null, but conversion to FBT failed: + SELECT a, DECODE_ORACLE(fbtcol, 'garbage', '', '::01', '01') + FROM t1; + */ + thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, + name().ptr(), ErrConvString(str).ptr()); + value->m_type= DYN_COL_NULL; + return true; + } + // "item" returned a non-NULL value, and it was a valid FBT + value->m_string.set(str->ptr(), str->length(), str->charset()); + } + return check_null(item, value); + } + void Item_param_setup_conversion(THD *thd, Item_param *param) const override + { + param->setup_conversion_string(thd, thd->variables.character_set_client); + } + void Item_param_set_param_func(Item_param *param, + uchar **pos, ulong len) const override + { + param->set_param_str(pos, len); + } + bool Item_param_set_from_value(THD *thd, Item_param *param, + const Type_all_attributes *attr, + const st_value *val) const override + { + param->unsigned_flag= false; + param->setup_conversion_string(thd, attr->collation.collation); + /* + Exact value of max_length is not known unless fbt is converted to + charset of connection, so we have to set it later. + */ + return param->set_str(val->m_string.ptr(), val->m_string.length(), + attr->collation.collation, + attr->collation.collation); + } + bool Item_param_val_native(THD *thd, Item_param *item, Native *to) + const override + { + StringBuffer buffer; + String *str= item->val_str(&buffer); + if (!str) + return true; + Fbt_null tmp(*str); + return tmp.is_null() || tmp.to_native(to); + } + bool Item_send(Item *item, Protocol *p, st_value *buf) const override + { + return Item_send_str(item, p, buf); + } + int Item_save_in_field(Item *item, Field *field, bool no_conversions) + const override + { + if (field->type_handler() == this) + { + NativeBuffer tmp; + bool rc= item->val_native(current_thd, &tmp); + if (rc || item->null_value) + return set_field_to_null_with_conversions(field, no_conversions); + field->set_notnull(); + return field->store_native(tmp); + } + return item->save_str_in_field(field, no_conversions); + } + + String *print_item_value(THD *thd, Item *item, String *str) const override + { + StringBuffer buf; + String *result= item->val_str(&buf); + /* + TODO: This should eventually use one of these notations: + 1. CAST('xxx' AS Fbt) + Problem: CAST is not supported as a NAME_CONST() argument. + 2. Fbt'xxx' + Problem: This syntax is not supported by the parser yet. + */ + return !result || str->realloc(result->length() + 2) || + str->append(STRING_WITH_LEN("'")) || + str->append(result->ptr(), result->length()) || + str->append(STRING_WITH_LEN("'")) ? nullptr : str; + } + + /** + Check if + WHERE expr=value AND expr=const + can be rewritten as: + WHERE const=value AND expr=const + + "this" is the comparison handler that is used by "target". + + @param target - the predicate expr=value, + whose "expr" argument will be replaced to "const". + @param target_expr - the target's "expr" which will be replaced to "const". + @param target_value - the target's second argument, it will remain unchanged. + @param source - the equality predicate expr=const (or expr<=>const) + that can be used to rewrite the "target" part + (under certain conditions, see the code). + @param source_expr - the source's "expr". It should be exactly equal to + the target's "expr" to make condition rewrite possible. + @param source_const - the source's "const" argument, it will be inserted + into "target" instead of "expr". + */ + bool can_change_cond_ref_to_const(Item_bool_func2 *target, Item *target_expr, + Item *target_value, Item_bool_func2 *source, + Item *source_expr, Item *source_const) + const override + { + /* + WHERE COALESCE(col)='xxx' AND COALESCE(col)=CONCAT(a); --> + WHERE COALESCE(col)='xxx' AND 'xxx'=CONCAT(a); + */ + return target->compare_type_handler() == source->compare_type_handler(); + } + bool subquery_type_allows_materialization(const Item *inner, + const Item *outer, bool) const override + { + /* + Example: + SELECT * FROM t1 WHERE a IN (SELECT col FROM t1 GROUP BY col); + Allow materialization only if the outer column is also FBT. + This can be changed for more relaxed rules in the future. + */ + DBUG_ASSERT(inner->type_handler() == this); + return outer->type_handler() == this; + } + /** + Make a simple constant replacement item for a constant "src", + so the new item can futher be used for comparison with "cmp", e.g.: + src = cmp -> replacement = cmp + + "this" is the type handler that is used to compare "src" and "cmp". + + @param thd - current thread, for mem_root + @param src - The item that we want to replace. It's a const item, + but it can be complex enough to calculate on every row. + @param cmp - The src's comparand. + @retval - a pointer to the created replacement Item + @retval - NULL, if could not create a replacement (e.g. on EOM). + NULL is also returned for ROWs, because instead of replacing + a Item_row to a new Item_row, Type_handler_row just replaces + its elements. + */ + Item *make_const_item_for_comparison(THD *thd, Item *src, + const Item *cmp) const override + { + Fbt_null tmp(src); + if (tmp.is_null()) + return new (thd->mem_root) Item_null(thd, src->name.str); + return new (thd->mem_root) Item_literal_fbt(thd, tmp); + } + Item_cache *Item_get_cache(THD *thd, const Item *item) const override + { + return new (thd->mem_root) Item_cache_fbt(thd); + } + + Item *create_typecast_item(THD *thd, Item *item, + const Type_cast_attributes &attr) const override + { + return new (thd->mem_root) Item_typecast_fbt(thd, item); + } + Item_copy *create_item_copy(THD *thd, Item *item) const override + { + return new (thd->mem_root) Item_copy_fbt(thd, item); + } + int cmp_native(const Native &a, const Native &b) const override + { + return FbtImpl::cmp(a.to_lex_cstring(), b.to_lex_cstring()); + } + bool set_comparator_func(THD *thd, Arg_comparator *cmp) const override + { + return cmp->set_cmp_func_native(thd); + } + bool Item_const_eq(const Item_const *a, const Item_const *b, + bool binary_cmp) const override + { + return false; + } + bool Item_eq_value(THD *thd, const Type_cmp_attributes *attr, + Item *a, Item *b) const override + { + Fbt_null na(a), nb(b); + return !na.is_null() && !nb.is_null() && !na.cmp(nb); + } + bool Item_hybrid_func_fix_attributes(THD *thd, const LEX_CSTRING &name, + Type_handler_hybrid_field_type *h, + Type_all_attributes *attr, + Item **items, uint nitems) const override + { + attr->Type_std_attributes::operator=(Type_std_attributes_fbt()); + h->set_handler(this); + /* + If some of the arguments cannot be safely converted to "FBT NOT NULL", + then mark the entire function nullability as NULL-able. + Otherwise, keep the generic nullability calculated by earlier stages: + - either by the most generic way in Item_func::fix_fields() + - or by Item_func_xxx::fix_length_and_dec() before the call of + Item_hybrid_func_fix_attributes() + IFNULL() is special. It does not need to test args[0]. + */ + uint first= dynamic_cast(attr) ? 1 : 0; + for (uint i= first; i < nitems; i++) + { + if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(items[i])) + { + attr->set_type_maybe_null(true); + break; + } + } + return false; + } + bool Item_func_min_max_fix_attributes(THD *thd, Item_func_min_max *func, + Item **items, uint nitems) const override + { + return Item_hybrid_func_fix_attributes(thd, func->func_name_cstring(), + func, func, items, nitems); + + } + bool Item_sum_hybrid_fix_length_and_dec(Item_sum_hybrid *func) const override + { + func->Type_std_attributes::operator=(Type_std_attributes_fbt()); + func->set_handler(this); + return false; + } + bool Item_sum_sum_fix_length_and_dec(Item_sum_sum *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_sum_avg_fix_length_and_dec(Item_sum_avg *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_sum_variance_fix_length_and_dec(Item_sum_variance *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_val_native_with_conversion(THD *thd, Item *item, + Native *to) const override + { + if (item->type_handler() == this) + return item->val_native(thd, to); // No conversion needed + StringBuffer buffer; + String *str= item->val_str(&buffer); + return str ? character_or_binary_string_to_native(thd, str, to) : true; + } + bool Item_val_native_with_conversion_result(THD *thd, Item *item, + Native *to) const override + { + if (item->type_handler() == this) + return item->val_native_result(thd, to); // No conversion needed + StringBuffer buffer; + String *str= item->str_result(&buffer); + return str ? character_or_binary_string_to_native(thd, str, to) : true; + } + + bool Item_val_bool(Item *item) const override + { + NativeBuffer tmp; + if (item->val_native(current_thd, &tmp)) + return false; + return !Fbt::only_zero_bytes(tmp.ptr(), tmp.length()); + } + void Item_get_date(THD *thd, Item *item, Temporal::Warn *buff, + MYSQL_TIME *ltime, date_mode_t fuzzydate) const override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + } + + longlong Item_val_int_signed_typecast(Item *item) const override + { + DBUG_ASSERT(0); + return 0; + } + + longlong Item_val_int_unsigned_typecast(Item *item) const override + { + DBUG_ASSERT(0); + return 0; + } + + String *Item_func_hex_val_str_ascii(Item_func_hex *item, String *str) + const override + { + NativeBuffer tmp; + if ((item->null_value= item->arguments()[0]->val_native(current_thd, &tmp))) + return nullptr; + DBUG_ASSERT(tmp.length() == FbtImpl::binary_length()); + if (str->set_hex(tmp.ptr(), tmp.length())) + { + str->length(0); + str->set_charset(item->collation.collation); + } + return str; + } + + String *Item_func_hybrid_field_type_val_str(Item_func_hybrid_field_type *item, + String *str) const override + { + NativeBuffer native; + if (item->val_native(current_thd, &native)) + { + DBUG_ASSERT(item->null_value); + return nullptr; + } + DBUG_ASSERT(native.length() == FbtImpl::binary_length()); + Fbt_null tmp(native.ptr(), native.length()); + return tmp.is_null() || tmp.to_string(str) ? nullptr : str; + } + double Item_func_hybrid_field_type_val_real(Item_func_hybrid_field_type *) + const override + { + return 0; + } + longlong Item_func_hybrid_field_type_val_int(Item_func_hybrid_field_type *) + const override + { + return 0; + } + my_decimal * + Item_func_hybrid_field_type_val_decimal(Item_func_hybrid_field_type *, + my_decimal *to) const override + { + my_decimal_set_zero(to); + return to; + } + void Item_func_hybrid_field_type_get_date(THD *, + Item_func_hybrid_field_type *, + Temporal::Warn *, + MYSQL_TIME *to, + date_mode_t fuzzydate) + const override + { + set_zero_time(to, MYSQL_TIMESTAMP_TIME); + } + // WHERE is Item_func_min_max_val_native??? + String *Item_func_min_max_val_str(Item_func_min_max *func, String *str) + const override + { + Fbt_null tmp(func); + return tmp.is_null() || tmp.to_string(str) ? nullptr : str; + } + double Item_func_min_max_val_real(Item_func_min_max *) const override + { + return 0; + } + longlong Item_func_min_max_val_int(Item_func_min_max *) const override + { + return 0; + } + my_decimal *Item_func_min_max_val_decimal(Item_func_min_max *, + my_decimal *to) const override + { + my_decimal_set_zero(to); + return to; + } + bool Item_func_min_max_get_date(THD *thd, Item_func_min_max*, MYSQL_TIME *to, + date_mode_t fuzzydate) const override + { + set_zero_time(to, MYSQL_TIMESTAMP_TIME); + return false; + } + + bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override + { + return false; + } + longlong Item_func_between_val_int(Item_func_between *func) const override + { + return func->val_int_cmp_native(); + } + + cmp_item *make_cmp_item(THD *thd, CHARSET_INFO *cs) const override + { + return new (thd->mem_root) cmp_item_fbt; + } + + in_vector *make_in_vector(THD *thd, const Item_func_in *func, + uint nargs) const override + { + return new (thd->mem_root) in_fbt(thd, nargs); + } + + bool Item_func_in_fix_comparator_compatible_types(THD *thd, + Item_func_in *func) + const override + { + if (func->compatible_types_scalar_bisection_possible()) + { + return func->value_list_convert_const_to_int(thd) || + func->fix_for_scalar_comparison_using_bisection(thd); + } + return + func->fix_for_scalar_comparison_using_cmp_items(thd, + 1U << (uint) STRING_RESULT); + } + bool Item_func_round_fix_length_and_dec(Item_func_round *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + bool Item_func_int_val_fix_length_and_dec(Item_func_int_val *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_abs_fix_length_and_dec(Item_func_abs *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_neg_fix_length_and_dec(Item_func_neg *func) const override + { + return Item_func_or_sum_illegal_param(func); + } + + bool Item_func_signed_fix_length_and_dec(Item_func_signed *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_unsigned_fix_length_and_dec(Item_func_unsigned *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_double_typecast_fix_length_and_dec(Item_double_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_float_typecast_fix_length_and_dec(Item_float_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_decimal_typecast_fix_length_and_dec(Item_decimal_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_char_typecast_fix_length_and_dec(Item_char_typecast *item) + const override + { + if (item->cast_charset() == &my_charset_bin) + { + static Item_char_typecast_func_handler_fbt_to_binary + item_char_typecast_func_handler_fbt_to_binary; + item->fix_length_and_dec_native_to_binary(FbtImpl::binary_length()); + item->set_func_handler(&item_char_typecast_func_handler_fbt_to_binary); + return false; + } + item->fix_length_and_dec_str(); + return false; + } + + bool Item_time_typecast_fix_length_and_dec(Item_time_typecast *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_date_typecast_fix_length_and_dec(Item_date_typecast *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_datetime_typecast_fix_length_and_dec(Item_datetime_typecast *item) + const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_plus_fix_length_and_dec(Item_func_plus *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_minus_fix_length_and_dec(Item_func_minus *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_mul_fix_length_and_dec(Item_func_mul *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_div_fix_length_and_dec(Item_func_div *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + bool Item_func_mod_fix_length_and_dec(Item_func_mod *item) const override + { + return Item_func_or_sum_illegal_param(item); + } + }; + + class cmp_item_fbt: public cmp_item_scalar + { + Fbt m_native; + public: + cmp_item_fbt() + :cmp_item_scalar(), + m_native(Fbt::zero()) + { } + void store_value(Item *item) override + { + m_native= Fbt(item, &m_null_value); + } + int cmp_not_null(const Value *val) override + { + DBUG_ASSERT(!val->is_null()); + DBUG_ASSERT(val->is_string()); + Fbt_null tmp(val->m_string); + DBUG_ASSERT(!tmp.is_null()); + return m_native.cmp(tmp); + } + int cmp(Item *arg) override + { + Fbt_null tmp(arg); + return m_null_value || tmp.is_null() ? UNKNOWN : m_native.cmp(tmp) != 0; + } + int compare(cmp_item *ci) override + { + cmp_item_fbt *tmp= static_cast(ci); + DBUG_ASSERT(!m_null_value); + DBUG_ASSERT(!tmp->m_null_value); + return m_native.cmp(tmp->m_native); + } + cmp_item *make_same(THD *thd) override + { + return new (thd->mem_root) cmp_item_fbt(); + } + }; + + class Field_fbt: public Field + { + static void set_min_value(char *ptr) + { + memset(ptr, 0, FbtImpl::binary_length()); + } + static void set_max_value(char *ptr) + { + memset(ptr, 0xFF, FbtImpl::binary_length()); + } + void store_warning(const ErrConv &str, + Sql_condition::enum_warning_level level) + { + if (get_thd()->count_cuted_fields <= CHECK_FIELD_EXPRESSION) + return; + const TABLE_SHARE *s= table->s; + static const Name type_name= type_handler_fbt()->name(); + get_thd()->push_warning_truncated_value_for_field(level, type_name.ptr(), + str.ptr(), s ? s->db.str : nullptr, s ? s->table_name.str : nullptr, + field_name.str); + } + int set_null_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_null(); + return 1; + } + int set_min_value_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_min_value((char*) ptr); + return 1; + } + int set_max_value_with_warn(const ErrConv &str) + { + store_warning(str, Sql_condition::WARN_LEVEL_WARN); + set_max_value((char*) ptr); + return 1; + } + int store_fbt_null_with_warn(const Fbt_null &fbt, + const ErrConvString &err) + { + DBUG_ASSERT(marked_for_write_or_computed()); + if (fbt.is_null()) + return maybe_null() ? set_null_with_warn(err) + : set_min_value_with_warn(err); + fbt.to_record((char *) ptr, FbtImpl::binary_length()); + return 0; + } + + public: + Field_fbt(const LEX_CSTRING *field_name_arg, const Record_addr &rec) + :Field(rec.ptr(), FbtImpl::max_char_length(), + rec.null_ptr(), rec.null_bit(), Field::NONE, field_name_arg) + { + flags|= BINARY_FLAG | UNSIGNED_FLAG; + } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + uint32 max_display_length() const override { return field_length; } + bool str_needs_quotes() const override { return true; } + const DTCollation &dtcollation() const override + { + static DTCollation_numeric c; + return c; + } + CHARSET_INFO *charset(void) const override { return &my_charset_numeric; } + const CHARSET_INFO *sort_charset(void) const override { return &my_charset_bin; } + /** + This makes client-server protocol convert the value according + to @@character_set_client. + */ + bool binary() const override { return false; } + enum ha_base_keytype key_type() const override { return HA_KEYTYPE_BINARY; } + + bool is_equal(const Column_definition &new_field) const override + { + return new_field.type_handler() == type_handler(); + } + bool eq_def(const Field *field) const override + { + return Field::eq_def(field); + } + double pos_in_interval(Field *min, Field *max) override + { + return pos_in_interval_val_str(min, max, 0); + } + int cmp(const uchar *a, const uchar *b) const override + { return memcmp(a, b, pack_length()); } + + void sort_string(uchar *to, uint length) override + { + DBUG_ASSERT(length == pack_length()); + memcpy(to, ptr, length); + } + uint32 pack_length() const override + { + return FbtImpl::binary_length(); + } + uint pack_length_from_metadata(uint field_metadata) const override + { + return FbtImpl::binary_length(); + } + + void sql_type(String &str) const override + { + static Name name= type_handler_fbt()->name(); + str.set_ascii(name.ptr(), name.length()); + } + + void make_send_field(Send_field *to) override + { + Field::make_send_field(to); + to->set_data_type_name(type_handler_fbt()->name().lex_cstring()); + } + + bool validate_value_in_record(THD *thd, const uchar *record) const override + { + return false; + } + + bool val_native(Native *to) override + { + DBUG_ASSERT(marked_for_read()); + if (to->alloc(FbtImpl::binary_length())) + return true; + to->length(FbtImpl::binary_length()); + FbtImpl::record_to_memory((char*) to->ptr(), (const char*) ptr); + return false; + } + + Fbt to_fbt() const + { + DBUG_ASSERT(marked_for_read()); + return Fbt::record_to_memory((const char*) ptr); + } + + String *val_str(String *val_buffer, String *) override + { + return to_fbt().to_string(val_buffer) ? NULL : val_buffer; + } + + my_decimal *val_decimal(my_decimal *to) override + { + DBUG_ASSERT(marked_for_read()); + my_decimal_set_zero(to); + return to; + } + + longlong val_int() override + { + DBUG_ASSERT(marked_for_read()); + return 0; + } + + double val_real() override + { + DBUG_ASSERT(marked_for_read()); + return 0; + } + + bool get_date(MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + DBUG_ASSERT(marked_for_read()); + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + + bool val_bool(void) override + { + DBUG_ASSERT(marked_for_read()); + return !Fbt::only_zero_bytes((const char *) ptr, FbtImpl::binary_length()); + } + + int store_native(const Native &value) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + DBUG_ASSERT(value.length() == FbtImpl::binary_length()); + FbtImpl::memory_to_record((char*) ptr, value.ptr()); + return 0; + } + + int store(const char *str, size_t length, CHARSET_INFO *cs) override + { + return cs == &my_charset_bin ? store_binary(str, length) + : store_text(str, length, cs); + } + + int store_text(const char *str, size_t length, CHARSET_INFO *cs) override + { + return store_fbt_null_with_warn(Fbt_null(str, length, cs), + ErrConvString(str, length, cs)); + } + + int store_binary(const char *str, size_t length) override + { + return store_fbt_null_with_warn(Fbt_null(str, length), + ErrConvString(str, length, + &my_charset_bin)); + } + + int store_hex_hybrid(const char *str, size_t length) override + { + return Field_fbt::store_binary(str, length); + } + + int store_decimal(const my_decimal *num) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvDecimal(num)); + } + + int store(longlong nr, bool unsigned_flag) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn( + ErrConvInteger(Longlong_hybrid(nr, unsigned_flag))); + } + + int store(double nr) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvDouble(nr)); + } + + int store_time_dec(const MYSQL_TIME *ltime, uint dec) override + { + DBUG_ASSERT(marked_for_write_or_computed()); + return set_min_value_with_warn(ErrConvTime(ltime)); + } + + /*** Field conversion routines ***/ + int store_field(Field *from) override + { + // INSERT INTO t1 (fbt_field) SELECT different_field_type FROM t2; + return from->save_in_field(this); + } + int save_in_field(Field *to) override + { + // INSERT INTO t2 (different_field_type) SELECT fbt_field FROM t1; + if (to->charset() == &my_charset_bin && + dynamic_cast + (to->type_handler())) + { + NativeBuffer res; + val_native(&res); + return to->store(res.ptr(), res.length(), &my_charset_bin); + } + return save_in_field_str(to); + } + Copy_func *get_copy_func(const Field *from) const override + { + // ALTER to FBT from another field + return do_field_string; + } + + Copy_func *get_copy_func_to(const Field *to) const override + { + if (type_handler() == to->type_handler()) + { + // ALTER from FBT to FBT + DBUG_ASSERT(pack_length() == to->pack_length()); + DBUG_ASSERT(charset() == to->charset()); + DBUG_ASSERT(sort_charset() == to->sort_charset()); + return Field::do_field_eq; + } + // ALTER from FBT to another fbt type + if (to->charset() == &my_charset_bin && + dynamic_cast + (to->type_handler())) + { + /* + ALTER from FBT to a binary string type, e.g.: + BINARY, TINYBLOB, BLOB, MEDIUMBLOB, LONGBLOB + */ + return do_field_fbt_native_to_binary; + } + return do_field_string; + } + + static void do_field_fbt_native_to_binary(Copy_field *copy) + { + NativeBuffer res; + copy->from_field->val_native(&res); + copy->to_field->store(res.ptr(), res.length(), &my_charset_bin); + } + + bool memcpy_field_possible(const Field *from) const override + { + // INSERT INTO t1 (fbt_field) SELECT field2 FROM t2; + return type_handler() == from->type_handler(); + } + enum_conv_type rpl_conv_type_from(const Conv_source &source, + const Relay_log_info *rli, + const Conv_param ¶m) const override + { + if (type_handler() == source.type_handler() || + (source.type_handler() == &type_handler_string && + source.type_handler()->max_display_length_for_field(source) == + FbtImpl::binary_length())) + return rpl_conv_type_from_same_data_type(source.metadata(), rli, param); + return CONV_TYPE_IMPOSSIBLE; + } + + /*** Optimizer routines ***/ + bool test_if_equality_guarantees_uniqueness(const Item *const_item) const override + { + /* + This condition: + WHERE fbt_field=const + should return a single distinct value only, + as comparison is done according to FBT. + */ + return true; + } + bool can_be_substituted_to_equal_item(const Context &ctx, + const Item_equal *item_equal) + override + { + switch (ctx.subst_constraint()) { + case ANY_SUBST: + return ctx.compare_type_handler() == item_equal->compare_type_handler(); + case IDENTITY_SUBST: + return true; + } + return false; + } + Item *get_equal_const_item(THD *thd, const Context &ctx, + Item *const_item) override + { + Fbt_null tmp(const_item); + if (tmp.is_null()) + return NULL; + return new (thd->mem_root) Item_literal_fbt(thd, tmp); + } + bool can_optimize_keypart_ref(const Item_bool_func *cond, + const Item *item) const override + { + /* + Mixing of two different non-traditional types is currently prevented. + This may change in the future. + */ + DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()-> + is_traditional_scalar_type() || + item->type_handler() == type_handler()); + return true; + } + /** + Test if Field can use range optimizer for a standard comparison operation: + <=, <, =, <=>, >, >= + Note, this method does not cover spatial operations. + */ + bool can_optimize_range(const Item_bool_func *cond, + const Item *item, + bool is_eq_func) const override + { + // See the DBUG_ASSERT comment in can_optimize_keypart_ref() + DBUG_ASSERT(item->type_handler()->type_handler_base_or_self()-> + is_traditional_scalar_type() || + item->type_handler() == type_handler()); + return true; + } + void hash(ulong *nr, ulong *nr2) override + { + if (is_null()) + *nr^= (*nr << 1) | 1; + else + FbtImpl::hash_record(ptr, nr, nr2); + } + SEL_ARG *get_mm_leaf(RANGE_OPT_PARAM *prm, KEY_PART *key_part, + const Item_bool_func *cond, + scalar_comparison_op op, Item *value) override + { + DBUG_ENTER("Field_fbt::get_mm_leaf"); + if (!can_optimize_scalar_range(prm, key_part, cond, op, value)) + DBUG_RETURN(0); + int err= value->save_in_field_no_warnings(this, 1); + if ((op != SCALAR_CMP_EQUAL && is_real_null()) || err < 0) + DBUG_RETURN(&null_element); + if (err > 0) + { + if (op == SCALAR_CMP_EQ || op == SCALAR_CMP_EQUAL) + DBUG_RETURN(new (prm->mem_root) SEL_ARG_IMPOSSIBLE(this)); + DBUG_RETURN(NULL); /* Cannot infer anything */ + } + DBUG_RETURN(stored_field_make_mm_leaf(prm, key_part, op, value)); + } + bool can_optimize_hash_join(const Item_bool_func *cond, + const Item *item) const override + { + return can_optimize_keypart_ref(cond, item); + } + bool can_optimize_group_min_max(const Item_bool_func *cond, + const Item *const_item) const override + { + return true; + } + + uint row_pack_length() const override { return pack_length(); } + + Binlog_type_info binlog_type_info() const override + { + DBUG_ASSERT(type() == binlog_type()); + return Binlog_type_info_fixed_string(Field_fbt::binlog_type(), + FbtImpl::binary_length(), &my_charset_bin); + } + + uchar *pack(uchar *to, const uchar *from, uint max_length) override + { + DBUG_PRINT("debug", ("Packing field '%s'", field_name.str)); + return FbtImpl::pack(to, from, max_length); + } + + const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) override + { + return FbtImpl::unpack(to, from, from_end, param_data); + } + + uint max_packed_col_length(uint max_length) override + { + return StringPack::max_packed_col_length(max_length); + } + + uint packed_col_length(const uchar *fbt_ptr, uint length) override + { + return StringPack::packed_col_length(fbt_ptr, length); + } + + uint size_of() const override { return sizeof(*this); } + }; + + class Item_typecast_fbt: public Item_func + { + public: + Item_typecast_fbt(THD *thd, Item *a) :Item_func(thd, a) {} + + const Type_handler *type_handler() const override + { return type_handler_fbt(); } + + enum Functype functype() const override { return CHAR_TYPECAST_FUNC; } + bool eq(const Item *item, bool binary_cmp) const override + { + if (this == item) + return true; + if (item->type() != FUNC_ITEM || + functype() != ((Item_func*)item)->functype()) + return false; + if (type_handler() != item->type_handler()) + return false; + Item_typecast_fbt *cast= (Item_typecast_fbt*) item; + return args[0]->eq(cast->args[0], binary_cmp); + } + LEX_CSTRING func_name_cstring() const override + { + static Name name= type_handler_fbt()->name(); + size_t len= 9+name.length()+1; + char *buf= (char*)current_thd->alloc(len); + strmov(strmov(buf, "cast_as_"), name.ptr()); + return { buf, len }; + } + void print(String *str, enum_query_type query_type) override + { + str->append(STRING_WITH_LEN("cast(")); + args[0]->print(str, query_type); + str->append(STRING_WITH_LEN(" as ")); + str->append(type_handler_fbt()->name().lex_cstring()); + str->append(')'); + } + bool fix_length_and_dec() override + { + Type_std_attributes::operator=(Type_std_attributes_fbt()); + if (Fbt::fix_fields_maybe_null_on_conversion_to_fbt(args[0])) + set_maybe_null(); + return false; + } + String *val_str(String *to) override + { + Fbt_null tmp(args[0]); + return (null_value= tmp.is_null() || tmp.to_string(to)) ? NULL : to; + } + longlong val_int() override + { + return 0; + } + double val_real() override + { + return 0; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) override + { + Fbt_null tmp(args[0]); + return null_value= tmp.is_null() || tmp.to_native(to); + } + Item *get_copy(THD *thd) override + { return get_item_copy(thd, this); } + }; + + class Item_cache_fbt: public Item_cache + { + NativeBuffer m_value; + public: + Item_cache_fbt(THD *thd) + :Item_cache(thd, type_handler_fbt()) { } + Item *get_copy(THD *thd) + { return get_item_copy(thd, this); } + bool cache_value() + { + if (!example) + return false; + value_cached= true; + null_value_inside= null_value= + example->val_native_with_conversion_result(current_thd, + &m_value, type_handler()); + return true; + } + String* val_str(String *to) + { + if (!has_value()) + return NULL; + Fbt_null tmp(m_value.ptr(), m_value.length()); + return tmp.is_null() || tmp.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) + { + if (!has_value()) + return NULL; + my_decimal_set_zero(to); + return to; + } + longlong val_int() + { + if (!has_value()) + return 0; + return 0; + } + double val_real() + { + if (!has_value()) + return 0; + return 0; + } + longlong val_datetime_packed(THD *thd) + { + DBUG_ASSERT(0); + if (!has_value()) + return 0; + return 0; + } + longlong val_time_packed(THD *thd) + { + DBUG_ASSERT(0); + if (!has_value()) + return 0; + return 0; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) + { + if (!has_value()) + return true; + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) + { + if (!has_value()) + return true; + return to->copy(m_value.ptr(), m_value.length()); + } + }; + + class Item_literal_fbt: public Item_literal + { + Fbt m_value; + public: + Item_literal_fbt(THD *thd) + :Item_literal(thd), + m_value(Fbt::zero()) + { } + Item_literal_fbt(THD *thd, const Fbt &value) + :Item_literal(thd), + m_value(value) + { } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + longlong val_int() override + { + return 0; + } + double val_real() override + { + return 0; + } + String *val_str(String *to) override + { + return m_value.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return false; + } + bool val_native(THD *thd, Native *to) override + { + return m_value.to_native(to); + } + void print(String *str, enum_query_type query_type) override + { + StringBuffer tmp; + tmp.append(type_handler_fbt()->name().lex_cstring()); + my_caseup_str(&my_charset_latin1, tmp.c_ptr()); + str->append(tmp); + str->append('\''); + m_value.to_string(&tmp); + str->append(tmp); + str->append('\''); + } + Item *get_copy(THD *thd) override + { return get_item_copy(thd, this); } + + // Non-overriding methods + void set_value(const Fbt &value) + { + m_value= value; + } + }; + + class Item_copy_fbt: public Item_copy + { + NativeBuffer m_value; + public: + Item_copy_fbt(THD *thd, Item *item_arg): Item_copy(thd, item_arg) {} + + bool val_native(THD *thd, Native *to) override + { + if (null_value) + return true; + return to->copy(m_value.ptr(), m_value.length()); + } + String *val_str(String *to) override + { + if (null_value) + return NULL; + Fbt_null tmp(m_value.ptr(), m_value.length()); + return tmp.is_null() || tmp.to_string(to) ? NULL : to; + } + my_decimal *val_decimal(my_decimal *to) override + { + my_decimal_set_zero(to); + return to; + } + double val_real() override + { + return 0; + } + longlong val_int() override + { + return 0; + } + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) override + { + set_zero_time(ltime, MYSQL_TIMESTAMP_TIME); + return null_value; + } + void copy() override + { + null_value= item->val_native(current_thd, &m_value); + DBUG_ASSERT(null_value == item->null_value); + } + int save_in_field(Field *field, bool no_conversions) override + { + return Item::save_in_field(field, no_conversions); + } + Item *get_copy(THD *thd) override + { return get_item_copy(thd, this); } + }; + + class in_fbt :public in_vector + { + Fbt m_value; + static int cmp_fbt(void *cmp_arg, Fbt *a, Fbt *b) + { + return a->cmp(*b); + } + public: + in_fbt(THD *thd, uint elements) + :in_vector(thd, elements, sizeof(Fbt), (qsort2_cmp) cmp_fbt, 0), + m_value(Fbt::zero()) + { } + const Type_handler *type_handler() const override + { + return type_handler_fbt(); + } + void set(uint pos, Item *item) override + { + Fbt *buff= &((Fbt *) base)[pos]; + Fbt_null value(item); + if (value.is_null()) + *buff= Fbt::zero(); + else + *buff= value; + } + uchar *get_value(Item *item) override + { + Fbt_null value(item); + if (value.is_null()) + return 0; + m_value= value; + return (uchar *) &m_value; + } + Item* create_item(THD *thd) override + { + return new (thd->mem_root) Item_literal_fbt(thd); + } + void value_to_item(uint pos, Item *item) override + { + const Fbt &buff= (((Fbt*) base)[pos]); + static_cast(item)->set_value(buff); + } + }; + + class Item_char_typecast_func_handler_fbt_to_binary: + public Item_handled_func::Handler_str + { + public: + const Type_handler *return_type_handler(const Item_handled_func *item) + const override + { + if (item->max_length > MAX_FIELD_VARCHARLENGTH) + return Type_handler::blob_type_handler(item->max_length); + if (item->max_length > 255) + return &type_handler_varchar; + return &type_handler_string; + } + bool fix_length_and_dec(Item_handled_func *xitem) const override + { + return false; + } + String *val_str(Item_handled_func *item, String *to) const override + { + DBUG_ASSERT(dynamic_cast(item)); + return static_cast(item)-> + val_str_binary_from_native(to); + } + }; + + class Type_collection_fbt: public Type_collection + { + const Type_handler *aggregate_common(const Type_handler *a, + const Type_handler *b) const + { + if (a == b) + return a; + return NULL; + } + const Type_handler *aggregate_if_string(const Type_handler *a, + const Type_handler *b) const + { + static const Type_aggregator::Pair agg[]= + { + {type_handler_fbt(), &type_handler_null, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_varchar, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_string, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_tiny_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_medium_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_hex_hybrid, type_handler_fbt()}, + {NULL,NULL,NULL} + }; + return Type_aggregator::find_handler_in_array(agg, a, b, true); + } + public: + const Type_handler *aggregate_for_result(const Type_handler *a, + const Type_handler *b) + const override + { + const Type_handler *h; + if ((h= aggregate_common(a, b)) || + (h= aggregate_if_string(a, b))) + return h; + return NULL; + } + + const Type_handler *aggregate_for_min_max(const Type_handler *a, + const Type_handler *b) + const override + { + return aggregate_for_result(a, b); + } + + const Type_handler *aggregate_for_comparison(const Type_handler *a, + const Type_handler *b) + const override + { + if (const Type_handler *h= aggregate_common(a, b)) + return h; + static const Type_aggregator::Pair agg[]= + { + {type_handler_fbt(), &type_handler_null, type_handler_fbt()}, + {type_handler_fbt(), &type_handler_long_blob, type_handler_fbt()}, + {NULL,NULL,NULL} + }; + return Type_aggregator::find_handler_in_array(agg, a, b, true); + } + + const Type_handler *aggregate_for_num_op(const Type_handler *a, + const Type_handler *b) + const override + { + return NULL; + } + + const Type_handler *handler_by_name(const LEX_CSTRING &name) const override + { + if (type_handler_fbt()->name().eq(name)) + return type_handler_fbt(); + return NULL; + } + }; + static Type_handler_fbt *type_handler_fbt() + { + static Type_handler_fbt th; + return &th; + } +}; + +#endif /* SQL_TYPE_FIXEDBIN_H */ diff --git a/sql/sql_type_fixedbin_storage.h b/sql/sql_type_fixedbin_storage.h new file mode 100644 index 00000000000..6e18335bd4c --- /dev/null +++ b/sql/sql_type_fixedbin_storage.h @@ -0,0 +1,173 @@ +#ifndef SQL_TYPE_FIXEDBIN_STORAGE +#define SQL_TYPE_FIXEDBIN_STORAGE +/* Copyright (c) 2019,2021 MariaDB Corporation + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */ + +/* + This is a common code for plugin (?) types that are generally + handled like strings, but have their own fixed size on-disk binary storage + format and their own (variable size) canonical string representation. + + Examples are INET6 and UUID types. + + The MariaDB server uses three binary representations of a data type: + + 1. In-memory binary representation (user visible) + This representation: + - can be used in INSERT..VALUES (X'AABBCC') + - can be used in WHERE conditions: WHERE c1=X'AABBCC' + - is returned by CAST(x AS BINARY(N)) + - is returned by Field::val_native() and Item::val_native() + + 2. In-record binary representation (user invisible) + This representation: + - is used in records (is pointed by Field::ptr) + - must be comparable by memcmp() + + 3. Binlog binary (row) representation + Usually, for string data types the binlog representation + is based on the in-record representation with trailing byte compression: + - trailing space compression for text string data types + - trailing zero compression for binary string data types + + We have to have separate in-memory and in-record representations + because we use HA_KEYTYPE_BINARY for indexing. The engine API + does not have a way to pass a comparison function as a parameter. + + The default implementation below assumes that: + - the in-memory and in-record representations are equal + - the binlog representation is compatible with BINARY(N) + This is OK for simple data types, like INET6. + + Data type implementations that need different representations + can override the default implementation (like e.g. UUID does). +*/ + +/***********************************************************************/ + +template +class FixedBinTypeStorage +{ +protected: + // The buffer that stores the in-memory binary representation + char m_buffer[NATIVE_LEN]; + + // Non-initializing constructor + FixedBinTypeStorage() + { } + + FixedBinTypeStorage & set_zero() + { + bzero(&m_buffer, sizeof(m_buffer)); + return *this; + } +public: + + // Initialize from the in-memory binary representation + FixedBinTypeStorage(const char *str, size_t length) + { + if (length != binary_length()) + set_zero(); + else + memcpy(&m_buffer, str, sizeof(m_buffer)); + } + + // Return the buffer with the in-memory representation + Lex_cstring to_lex_cstring() const + { + return Lex_cstring(m_buffer, sizeof(m_buffer)); + } + + static constexpr uint binary_length() { return NATIVE_LEN; } + static constexpr uint max_char_length() { return MAX_CHAR_LEN; } + + // Compare the in-memory binary representations of two values + static int cmp(const LEX_CSTRING &a, const LEX_CSTRING &b) + { + DBUG_ASSERT(a.length == binary_length()); + DBUG_ASSERT(b.length == binary_length()); + return memcmp(a.str, b.str, b.length); + } + + /* + Convert from the in-memory to the in-record representation. + Used in Field::store_native(). + */ + static void memory_to_record(char *to, const char *from) + { + memcpy(to, from, NATIVE_LEN); + } + /* + Convert from the in-record to the in-memory representation + Used in Field::val_native(). + */ + static void record_to_memory(char *to, const char *from) + { + memcpy(to, from, NATIVE_LEN); + } + + /* + Hash the in-record representation + Used in Field::hash(). + */ + static void hash_record(const uchar *ptr, ulong *nr, ulong *nr2) + { + my_charset_bin.hash_sort(ptr, binary_length(), nr, nr2); + } + + static bool only_zero_bytes(const char *ptr, size_t length) + { + for (uint i= 0 ; i < length; i++) + { + if (ptr[i] != 0) + return false; + } + return true; + } + + static ulong KEY_pack_flags(uint column_nr) + { + /* + Return zero by default. A particular data type can override + this method return some flags, e.g. HA_PACK_KEY to enable + key prefix compression. + */ + return 0; + } + + /* + Convert from the in-record to the binlog representation. + Used in Field::pack(), and in filesort to store the addon fields. + By default, do what BINARY(N) does. + */ + static uchar *pack(uchar *to, const uchar *from, uint max_length) + { + return StringPack(&my_charset_bin, binary_length()).pack(to, from, max_length); + } + + /* + Convert from the in-binary-log to the in-record representation. + Used in Field::unpack(). + By default, do what BINARY(N) does. + */ + static const uchar *unpack(uchar *to, const uchar *from, const uchar *from_end, + uint param_data) + { + return StringPack(&my_charset_bin, binary_length()).unpack(to, from, from_end, + param_data); + } + +}; +#endif /* SQL_TYPE_FIXEDBIN_STORAGE */ From e404315258bb20378eef6a70857c1d16d1da9638 Mon Sep 17 00:00:00 2001 From: Nayuta Yanagisawa Date: Tue, 23 Aug 2022 19:53:59 +0900 Subject: [PATCH 30/31] Fix wrong diff introduced by merge commit Many Spider tests were broken by the merge commit, 36d173e. --- storage/spider/spd_db_conn.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/storage/spider/spd_db_conn.cc b/storage/spider/spd_db_conn.cc index 62a02bf7de4..c34dea18adf 100644 --- a/storage/spider/spd_db_conn.cc +++ b/storage/spider/spd_db_conn.cc @@ -9376,6 +9376,8 @@ int spider_db_open_item_ident( } else { if (!use_fields) { + if (!(field = spider->field_exchange(field))) + DBUG_RETURN(ER_SPIDER_COND_SKIP_NUM); if (str) { if ((error_num = share->dbton_share[dbton_id]-> @@ -9384,16 +9386,12 @@ int spider_db_open_item_ident( DBUG_RETURN(error_num); } } else { - if (!(field = spider->field_exchange(field))) - DBUG_RETURN(ER_SPIDER_COND_SKIP_NUM); if (str) { SPIDER_FIELD_CHAIN *field_chain = fields->get_next_field_chain(); SPIDER_FIELD_HOLDER *field_holder = field_chain->field_holder; spider = field_holder->spider; share = spider->share; - field = spider->field_exchange(field); - DBUG_ASSERT(field); if ((error_num = share->dbton_share[dbton_id]-> append_column_name_with_alias(str, field->field_index, field_holder->alias->ptr(), field_holder->alias->length()))) From 5b4c832c7ebd59d9f5a5e7feef570568e20e5b86 Mon Sep 17 00:00:00 2001 From: Thirunarayanan Balathandayuthapani Date: Tue, 23 Aug 2022 19:38:24 +0530 Subject: [PATCH 31/31] MDEV-29314 Assertion `n_fields > n_cols' failed in dict_index_t::init_change_cols - Newly created InnoDB fulltext index does have only one column and doesn't associate with primary key fields during DDL. init_change_cols() has strict assertion that number of fields should be greater than number of collation change columns. --- .../suite/innodb/r/change_column_collation.result | 14 ++++++++++++++ .../suite/innodb/t/change_column_collation.test | 10 ++++++++++ storage/innobase/include/dict0mem.h | 2 +- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/mysql-test/suite/innodb/r/change_column_collation.result b/mysql-test/suite/innodb/r/change_column_collation.result index b1771b2bd00..94dfa77bbd0 100644 --- a/mysql-test/suite/innodb/r/change_column_collation.result +++ b/mysql-test/suite/innodb/r/change_column_collation.result @@ -95,3 +95,17 @@ ALTER TABLE t1 MODIFY msg_2 VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci, ALGORITHM=inplace; ERROR 0A000: ALGORITHM=INPLACE is not supported. Reason: Cannot change column type. Try ALGORITHM=COPY DROP TABLE t1; +# +# MDEV-29314 Assertion `n_fields > n_cols' failed +# in dict_index_t::init_change_cols +# +CREATE TABLE t (a VARCHAR(16) COLLATE utf8_bin, +FULLTEXT (a)) ENGINE=InnoDB COLLATE utf8_unicode_520_ci; +ALTER TABLE t MODIFY COLUMN a VARCHAR(512); +SHOW CREATE TABLE t; +Table Create Table +t CREATE TABLE `t` ( + `a` varchar(512) COLLATE utf8mb3_unicode_520_ci DEFAULT NULL, + FULLTEXT KEY `a` (`a`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_unicode_520_ci +DROP TABLE t; diff --git a/mysql-test/suite/innodb/t/change_column_collation.test b/mysql-test/suite/innodb/t/change_column_collation.test index f63d1974c72..9b811830d3f 100644 --- a/mysql-test/suite/innodb/t/change_column_collation.test +++ b/mysql-test/suite/innodb/t/change_column_collation.test @@ -122,4 +122,14 @@ ALTER TABLE t1 MODIFY msg_2 VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci, ALGORITHM=inplace; DROP TABLE t1; +--echo # +--echo # MDEV-29314 Assertion `n_fields > n_cols' failed +--echo # in dict_index_t::init_change_cols +--echo # +CREATE TABLE t (a VARCHAR(16) COLLATE utf8_bin, + FULLTEXT (a)) ENGINE=InnoDB COLLATE utf8_unicode_520_ci; +ALTER TABLE t MODIFY COLUMN a VARCHAR(512); +SHOW CREATE TABLE t; +DROP TABLE t; + --source include/wait_until_count_sessions.inc diff --git a/storage/innobase/include/dict0mem.h b/storage/innobase/include/dict0mem.h index bd8872019cb..111f0efbe0b 100644 --- a/storage/innobase/include/dict0mem.h +++ b/storage/innobase/include/dict0mem.h @@ -1345,7 +1345,7 @@ public: @param n_cols number of columns whose collation is changing */ void init_change_cols(unsigned n_cols) { - ut_ad(n_fields > n_cols); + ut_ad(n_fields > n_cols || type & DICT_FTS); change_col_info= static_cast (mem_heap_zalloc(heap, sizeof(col_info))); change_col_info->n_cols= n_cols;