From cb364a78d6be7938a77ff8774a95035b8f703c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Tue, 4 Jul 2023 15:24:57 +0300 Subject: [PATCH 01/10] MDEV-31619 dict_stats_persistent_storage_check() may show garbage during --bootstrap dict_stats_persistent_storage_check(): Do not output errmsg if opt_bootstrap holds, because the message buffer would likely be uninitialized. --- storage/innobase/dict/dict0stats.cc | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/storage/innobase/dict/dict0stats.cc b/storage/innobase/dict/dict0stats.cc index b5291ccbd87..e8dd976f8d2 100644 --- a/storage/innobase/dict/dict0stats.cc +++ b/storage/innobase/dict/dict0stats.cc @@ -514,15 +514,17 @@ static bool dict_stats_persistent_storage_check(bool dict_already_locked) dict_sys.unlock(); } - if (ret != DB_SUCCESS && ret != DB_STATS_DO_NOT_EXIST) { - ib::error() << errstr; - return(false); - } else if (ret == DB_STATS_DO_NOT_EXIST) { + switch (ret) { + case DB_SUCCESS: + return true; + default: + if (!opt_bootstrap) { + ib::error() << errstr; + } + /* fall through */ + case DB_STATS_DO_NOT_EXIST: return false; } - /* else */ - - return(true); } /** Executes a given SQL statement using the InnoDB internal SQL parser. From f6e488cc6dde2b7dd890fb2ecad753678ec1da76 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 17 Jan 2023 10:58:00 +0100 Subject: [PATCH 02/10] MDEV-26506 Over-quoted JSON when combining JSON_ARRAYAGG with JSON_OBJECT add the test case --- mysql-test/main/type_json.result | 18 ++++++++++++++++++ mysql-test/main/type_json.test | 14 ++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/mysql-test/main/type_json.result b/mysql-test/main/type_json.result index 681bd42c4e3..431a7f138f6 100644 --- a/mysql-test/main/type_json.result +++ b/mysql-test/main/type_json.result @@ -159,5 +159,23 @@ def j 250 (format=json) 9437283 16 Y 0 39 33 j {"a": {"b":"c"}} # +# MDEV-26506 Over-quoted JSON when combining JSON_ARRAYAGG with JSON_OBJECT +# +# maintain JSON property through internal temporary tables +create table t1 (a varchar(30)); +insert into t1 values ('root'); +select json_object('attr2',o) from (select a, json_arrayagg(json_object('attr1', a)) as o from t1) u; +json_object('attr2',o) +{"attr2": [{"attr1": "root"}]} +drop table t1; +create view v1 as select json_object(_latin1 'a', _latin1'b') as v1_json; +select v1_json from v1; +v1_json +{"a": "b"} +select json_arrayagg(v1_json) from v1; +json_arrayagg(v1_json) +[{"a": "b"}] +drop view v1; +# # End of 10.5 tests # diff --git a/mysql-test/main/type_json.test b/mysql-test/main/type_json.test index 38754ba6e1e..8effe78f803 100644 --- a/mysql-test/main/type_json.test +++ b/mysql-test/main/type_json.test @@ -121,6 +121,20 @@ SELECT json_object('a', (SELECT json_objectagg(b, c) FROM (SELECT 'b','c') d)) A --disable_ps_protocol --enable_view_protocol +--echo # +--echo # MDEV-26506 Over-quoted JSON when combining JSON_ARRAYAGG with JSON_OBJECT +--echo # +--echo # maintain JSON property through internal temporary tables +create table t1 (a varchar(30)); +insert into t1 values ('root'); +select json_object('attr2',o) from (select a, json_arrayagg(json_object('attr1', a)) as o from t1) u; +drop table t1; + +create view v1 as select json_object(_latin1 'a', _latin1'b') as v1_json; +select v1_json from v1; +select json_arrayagg(v1_json) from v1; +drop view v1; + --echo # --echo # End of 10.5 tests --echo # From a4817e1520de799ae55f27444a50be54b59b09bc Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 27 Jun 2023 14:09:52 +0200 Subject: [PATCH 03/10] cleanup: String::strstr() const --- sql/sql_string.cc | 6 +++--- sql/sql_string.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/sql/sql_string.cc b/sql/sql_string.cc index 7a8aa4ef0f4..208805b91ba 100644 --- a/sql/sql_string.cc +++ b/sql/sql_string.cc @@ -677,7 +677,7 @@ bool String::append_with_prefill(const char *s,uint32 arg_length, } -int Binary_string::strstr(const char *search, uint32 search_length, uint32 offset) +int Binary_string::strstr(const char *search, uint32 search_length, uint32 offset) const { if (search_length + offset <= str_length) { @@ -703,7 +703,7 @@ skip: return -1; } -int Binary_string::strstr(const Binary_string &s, uint32 offset) +int Binary_string::strstr(const Binary_string &s, uint32 offset) const { return strstr(s.ptr(), s.length(), offset); } @@ -712,7 +712,7 @@ int Binary_string::strstr(const Binary_string &s, uint32 offset) ** Search string from end. Offset is offset to the end of string */ -int Binary_string::strrstr(const Binary_string &s, uint32 offset) +int Binary_string::strrstr(const Binary_string &s, uint32 offset) const { if (s.length() <= offset && offset <= str_length) { diff --git a/sql/sql_string.h b/sql/sql_string.h index dbb4760ab34..d112a8842b9 100644 --- a/sql/sql_string.h +++ b/sql/sql_string.h @@ -406,10 +406,10 @@ public: } // Returns offset to substring or -1 - int strstr(const Binary_string &search, uint32 offset=0); - int strstr(const char *search, uint32 search_length, uint32 offset=0); + int strstr(const Binary_string &search, uint32 offset=0) const; + int strstr(const char *search, uint32 search_length, uint32 offset=0) const; // Returns offset to substring or -1 - int strrstr(const Binary_string &search, uint32 offset=0); + int strrstr(const Binary_string &search, uint32 offset=0) const; /* The following append operations do not extend the strings and in production From 5bf80af06eca4372af1e73c680eb59a4e6501f1c Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Tue, 16 May 2023 22:26:57 +0200 Subject: [PATCH 04/10] cleanup: remove Type_collection::handler_by_name() it's a hard-coded geometry-specific method, move it into geometry-specific function. Will go away in the future. --- plugin/type_mysql_json/type.cc | 7 ------- plugin/type_mysql_timestamp/plugin.cc | 5 ----- plugin/type_test/plugin.cc | 4 ---- sql/sql_type.cc | 10 +--------- sql/sql_type.h | 1 - sql/sql_type_fixedbin.h | 7 ------- sql/sql_type_geom.cc | 2 +- sql/sql_type_geom.h | 3 ++- sql/sql_type_json.cc | 14 -------------- 9 files changed, 4 insertions(+), 49 deletions(-) diff --git a/plugin/type_mysql_json/type.cc b/plugin/type_mysql_json/type.cc index 2b3c415f346..9cb69c8aa28 100644 --- a/plugin/type_mysql_json/type.cc +++ b/plugin/type_mysql_json/type.cc @@ -176,13 +176,6 @@ public: { return NULL; } - - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - if (type_handler_mysql_json.name().eq(name)) - return &type_handler_mysql_json; - return NULL; - } }; const Type_collection *Type_handler_mysql_json::type_collection() const diff --git a/plugin/type_mysql_timestamp/plugin.cc b/plugin/type_mysql_timestamp/plugin.cc index f361ab6c0eb..c148f6dc067 100644 --- a/plugin/type_mysql_timestamp/plugin.cc +++ b/plugin/type_mysql_timestamp/plugin.cc @@ -31,11 +31,6 @@ protected: return NULL; } public: - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - return NULL; - } - const Type_handler *aggregate_for_result(const Type_handler *h1, const Type_handler *h2) const override diff --git a/plugin/type_test/plugin.cc b/plugin/type_test/plugin.cc index 4c26c35f976..a649ebb3e41 100644 --- a/plugin/type_test/plugin.cc +++ b/plugin/type_test/plugin.cc @@ -26,10 +26,6 @@ protected: const Type_handler *aggregate_common(const Type_handler *h1, const Type_handler *h2) const; public: - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - return NULL; - } const Type_handler *aggregate_for_result(const Type_handler *h1, const Type_handler *h2) const override; diff --git a/sql/sql_type.cc b/sql/sql_type.cc index a09d2648757..827eefba3fd 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -94,10 +94,6 @@ Vers_type_trx vers_type_trx; class Type_collection_std: public Type_collection { public: - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - return NULL; - } const Type_handler *aggregate_for_result(const Type_handler *a, const Type_handler *b) const override @@ -137,10 +133,6 @@ public: { return false; } - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - return NULL; - } const Type_handler *aggregate_for_result(const Type_handler *a, const Type_handler *b) const override @@ -212,7 +204,7 @@ Type_handler::handler_by_name(THD *thd, const LEX_CSTRING &name) } #ifdef HAVE_SPATIAL - const Type_handler *ha= type_collection_geometry.handler_by_name(name); + const Type_handler *ha= Type_collection_geometry_handler_by_name(name); if (ha) return ha; #endif diff --git a/sql/sql_type.h b/sql/sql_type.h index 479f924a727..471fe519847 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -7415,7 +7415,6 @@ class Type_collection public: virtual ~Type_collection() = default; virtual bool init(Type_handler_data *) { return false; } - virtual const Type_handler *handler_by_name(const LEX_CSTRING &name) const= 0; virtual const Type_handler *aggregate_for_result(const Type_handler *h1, const Type_handler *h2) const= 0; diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index 093587c4deb..6163512d08b 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -1892,13 +1892,6 @@ public: { 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() { diff --git a/sql/sql_type_geom.cc b/sql/sql_type_geom.cc index 5732ae47217..c555022a99d 100644 --- a/sql/sql_type_geom.cc +++ b/sql/sql_type_geom.cc @@ -67,7 +67,7 @@ Type_handler_geometry::type_handler_geom_by_type(uint type) const Type_handler * -Type_collection_geometry::handler_by_name(const LEX_CSTRING &name) const +Type_collection_geometry_handler_by_name(const LEX_CSTRING &name) { if (type_handler_point.name().eq(name)) return &type_handler_point; diff --git a/sql/sql_type_geom.h b/sql/sql_type_geom.h index 3bc25808bc3..f3e34526f26 100644 --- a/sql/sql_type_geom.h +++ b/sql/sql_type_geom.h @@ -297,7 +297,6 @@ class Type_collection_geometry: public Type_collection #endif public: bool init(Type_handler_data *data) override; - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override; const Type_handler *aggregate_for_result(const Type_handler *a, const Type_handler *b) const override; @@ -316,6 +315,8 @@ public: }; extern Type_collection_geometry type_collection_geometry; +const Type_handler * +Type_collection_geometry_handler_by_name(const LEX_CSTRING &name); #include "field.h" diff --git a/sql/sql_type_json.cc b/sql/sql_type_json.cc index c12b868e6b9..27072de2d55 100644 --- a/sql/sql_type_json.cc +++ b/sql/sql_type_json.cc @@ -233,20 +233,6 @@ public: */ return NULL; } - - const Type_handler *handler_by_name(const LEX_CSTRING &name) const override - { - /* - Name resolution is not needed yet. - JSON is not fully pluggable at the moment: - - It is parsed using a hard-coded rule in sql_yacc.yy - - It does not store extended data type information into - FRM file yet. JSON is detected by CHECK(JSON_VALID(col)) - and this detection is also hard-coded. - This will change in the future. - */ - return NULL; - } }; From bbe44da24013aaa27baa07a0ab6d218d968e038d Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Sun, 25 Jun 2023 16:38:57 +0200 Subject: [PATCH 05/10] cleanup: udt in sql_yac.yy --- sql/sql_yacc.yy | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 439f2a86052..062b6366246 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -1323,6 +1323,7 @@ bool my_yyoverflow(short **a, YYSTYPE **b, size_t *yystacksize); sp_opt_label BIN_NUM TEXT_STRING_filesystem opt_constraint constraint opt_ident sp_block_label sp_control_label opt_place opt_db + udt_name %type IDENT_sys @@ -6296,23 +6297,19 @@ qualified_field_type: } ; +udt_name: + IDENT_sys { $$= $1; } + | reserved_keyword_udt { $$= $1; } + | non_reserved_keyword_udt { $$= $1; } + ; + field_type_all: field_type_numeric | field_type_temporal | field_type_string | field_type_lob | field_type_misc - | IDENT_sys float_options srid_option - { - if (Lex->set_field_type_udt(&$$, $1, $2)) - MYSQL_YYABORT; - } - | reserved_keyword_udt float_options srid_option - { - if (Lex->set_field_type_udt(&$$, $1, $2)) - MYSQL_YYABORT; - } - | non_reserved_keyword_udt float_options srid_option + | udt_name float_options srid_option { if (Lex->set_field_type_udt(&$$, $1, $2)) MYSQL_YYABORT; @@ -11468,17 +11465,7 @@ cast_type: } | cast_type_numeric { $$= $1; Lex->charset= NULL; } | cast_type_temporal { $$= $1; Lex->charset= NULL; } - | IDENT_sys - { - if (Lex->set_cast_type_udt(&$$, $1)) - MYSQL_YYABORT; - } - | reserved_keyword_udt - { - if (Lex->set_cast_type_udt(&$$, $1)) - MYSQL_YYABORT; - } - | non_reserved_keyword_udt + | udt_name { if (Lex->set_cast_type_udt(&$$, $1)) MYSQL_YYABORT; From 238cfcc7290c7d5805bd7e747408e9186e0cc5bb Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 26 Jun 2023 17:22:26 +0200 Subject: [PATCH 06/10] cleanup: remove unused and unlinkable method it was calling Fbt::to_string(char*, size_t) which didn't exist --- sql/sql_type_fixedbin.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index 6163512d08b..a825fe269ca 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -200,10 +200,6 @@ public: { 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); From c09b1583f56c2a3d4ca8899979dbcbad92a3bf1e Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 26 Jun 2023 14:33:35 +0200 Subject: [PATCH 07/10] cleanup: remove FixedBinTypeBundle now the main class is the Type_handler_fbt. It contains everything and generates objects of all other classes as needed. --- plugin/type_inet/item_inetfunc.cc | 10 +- plugin/type_inet/plugin.cc | 2 +- plugin/type_inet/sql_type_inet.h | 2 +- sql/sql_type_fixedbin.h | 2146 +++++++++++++++-------------- 4 files changed, 1083 insertions(+), 1077 deletions(-) diff --git a/plugin/type_inet/item_inetfunc.cc b/plugin/type_inet/item_inetfunc.cc index b23ae04a861..1bdae5e896b 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; } - Inet6Bundle::Fbt_null ipv6(*tmp.string()); + Type_handler_inet6::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; } - Inet6Bundle::Fbt_null ipv6(static_cast(*tmp.string())); + Type_handler_inet6::Fbt_null ipv6(static_cast(*tmp.string())); if (!ipv6.is_null()) { ipv6.to_string(buffer); @@ -221,10 +221,10 @@ longlong Item_func_is_ipv4::val_int() return !tmp.is_null() && !Inet4_null(*tmp.string()).is_null(); } -class IP6 : public Inet6Bundle::Fbt_null +class IP6 : public Type_handler_inet6::Fbt_null { public: - IP6(Item* arg) : Inet6Bundle::Fbt_null(arg) {} + IP6(Item* arg) : Type_handler_inet6::Fbt_null(arg) {} bool is_v4compat() const { static_assert(sizeof(in6_addr) == IN6_ADDR_SIZE, "unexpected in6_addr size"); @@ -246,7 +246,7 @@ longlong Item_func_is_ipv6::val_int() { DBUG_ASSERT(fixed()); String_ptr_and_buffer tmp(args[0]); - return !tmp.is_null() && !Inet6Bundle::Fbt_null(*tmp.string()).is_null(); + return !tmp.is_null() && !Type_handler_inet6::Fbt_null(*tmp.string()).is_null(); } /** diff --git a/plugin/type_inet/plugin.cc b/plugin/type_inet/plugin.cc index 0b57e2bec1f..bb7603d5abe 100644 --- a/plugin/type_inet/plugin.cc +++ b/plugin/type_inet/plugin.cc @@ -24,7 +24,7 @@ static struct st_mariadb_data_type plugin_descriptor_type_inet6= { MariaDB_DATA_TYPE_INTERFACE_VERSION, - Inet6Bundle::type_handler_fbt() + Type_handler_inet6::singleton() }; diff --git a/plugin/type_inet/sql_type_inet.h b/plugin/type_inet/sql_type_inet.h index 69836ca215b..2c1865a7cef 100644 --- a/plugin/type_inet/sql_type_inet.h +++ b/plugin/type_inet/sql_type_inet.h @@ -44,7 +44,7 @@ public: #include "sql_type_fixedbin.h" -typedef FixedBinTypeBundle Inet6Bundle; +typedef Type_handler_fbt Type_handler_inet6; /***********************************************************************/ diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index a825fe269ca..fb9ec6a5a5a 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -32,8 +32,10 @@ template -class FixedBinTypeBundle +class Type_handler_fbt: public Type_handler { + /* =[ internal helper classes ]=============================== */ + public: class Fbt: public FbtImpl { @@ -41,7 +43,7 @@ public: using FbtImpl::m_buffer; bool make_from_item(Item *item, bool warn) { - if (item->type_handler() == type_handler_fbt()) + if (item->type_handler() == singleton()) { Native tmp(m_buffer, sizeof(m_buffer)); bool rc= item->val_native(current_thd, &tmp); @@ -78,14 +80,14 @@ public: str->charset()); if (rc && warn) current_thd->push_warning_wrong_value(Sql_condition::WARN_LEVEL_WARN, - type_handler_fbt()->name().ptr(), ErrConvString(str).ptr()); + singleton()->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()); + singleton()->name().ptr(), ErrConvString(str).ptr()); return true; } DBUG_ASSERT(str->ptr() != m_buffer); @@ -125,7 +127,7 @@ public: { if (item->maybe_null()) return true; - if (item->type_handler() == type_handler_fbt()) + if (item->type_handler() == singleton()) return false; if (!item->const_item() || item->is_expensive()) return true; @@ -206,6 +208,76 @@ public: } }; + /* =[ API classes ]=========================================== */ + + 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[]= + { + {singleton(), &type_handler_null, singleton()}, + {singleton(), &type_handler_varchar, singleton()}, + {singleton(), &type_handler_string, singleton()}, + {singleton(), &type_handler_tiny_blob, singleton()}, + {singleton(), &type_handler_blob, singleton()}, + {singleton(), &type_handler_medium_blob, singleton()}, + {singleton(), &type_handler_long_blob, singleton()}, + {singleton(), &type_handler_hex_hybrid, singleton()}, + {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[]= + { + {singleton(), &type_handler_null, singleton()}, + {singleton(), &type_handler_long_blob, singleton()}, + {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; + } + }; + class Type_std_attributes_fbt: public Type_std_attributes { public: @@ -216,847 +288,66 @@ public: { } }; - class Type_handler_fbt: public Type_handler + class Item_literal_fbt: public Item_literal { - 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); - } - + Fbt m_value; public: - ~Type_handler_fbt() override {} - - const Type_collection *type_collection() const override + 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 { - static Type_collection_fbt type_collection_fbt; - return &type_collection_fbt; + return singleton(); } - - 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 + longlong val_int() override { return 0; } - decimal_digits_t Item_datetime_precision(THD *thd, Item *item) const override + double val_real() override { return 0; } - decimal_digits_t Item_decimal_scale(const Item *item) const override + String *val_str(String *to) override { - return 0; + return m_value.to_string(to) ? NULL : to; } - decimal_digits_t Item_decimal_precision(const Item *item) const override + my_decimal *val_decimal(my_decimal *to) 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 + my_decimal_set_zero(to); + return to; } - - /* - 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, - String *) 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, - String *) 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 + bool get_date(THD *thd, MYSQL_TIME *ltime, date_mode_t fuzzydate) 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 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(singleton()->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); } - bool Item_func_between_fix_length_and_dec(Item_func_between *func) const override + // Non-overriding methods + void set_value(const Fbt &value) { - 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(); + m_value= value; } }; @@ -1076,7 +367,7 @@ public: 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(); + static const Name type_name= singleton()->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); @@ -1119,7 +410,7 @@ public: } const Type_handler *type_handler() const override { - return type_handler_fbt(); + return singleton(); } uint32 max_display_length() const override { return field_length; } bool str_needs_quotes() const override { return true; } @@ -1168,14 +459,14 @@ public: void sql_type(String &str) const override { - static Name name= type_handler_fbt()->name(); + static Name name= singleton()->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()); + to->set_data_type_name(singleton()->name().lex_cstring()); } bool validate_value_in_record(THD *thd, const uchar *record) const override @@ -1491,13 +782,171 @@ public: uint size_of() const override { return sizeof(*this); } }; + + 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 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 singleton(); + } + 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_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 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 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(); } + { return singleton(); } enum Functype functype() const override { return CHAR_TYPECAST_FUNC; } bool eq(const Item *item, bool binary_cmp) const override @@ -1514,7 +963,7 @@ public: } LEX_CSTRING func_name_cstring() const override { - static Name name= type_handler_fbt()->name(); + static Name name= singleton()->name(); size_t len= 9+name.length()+1; char *buf= (char*)current_thd->alloc(len); strmov(strmov(buf, "cast_as_"), name.ptr()); @@ -1525,7 +974,7 @@ public: 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(singleton()->name().lex_cstring()); str->append(')'); } bool fix_length_and_dec() override @@ -1572,7 +1021,7 @@ public: NativeBuffer m_value; public: Item_cache_fbt(THD *thd) - :Item_cache(thd, type_handler_fbt()) { } + :Item_cache(thd, singleton()) { } Item *get_copy(THD *thd) { return get_item_copy(thd, this); } bool cache_value() @@ -1640,256 +1089,813 @@ public: } }; - class Item_literal_fbt: public Item_literal + /* =[ methods ]=============================================== */ +private: + + bool character_or_binary_string_to_native(THD *thd, const String *str, + Native *to) const { - 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 + if (str->charset() == &my_charset_bin) { - 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) + // 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 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); - } - }; + // 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); + } - class Type_collection_fbt: public Type_collection +public: + ~Type_handler_fbt() override {} + + const Type_collection *type_collection() const override { - 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; - } + static Type_collection_fbt type_collection_fbt; + return &type_collection_fbt; + } - const Type_handler *aggregate_for_min_max(const Type_handler *a, - const Type_handler *b) + 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, + String *) 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, + String *) 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 aggregate_for_result(a, b); - } + { + 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; + } - 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); - } + 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(); + } - const Type_handler *aggregate_for_num_op(const Type_handler *a, - const Type_handler *b) + 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) { - return NULL; + 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; } - }; - static Type_handler_fbt *type_handler_fbt() + 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); + } + + static Type_handler_fbt *singleton() { static Type_handler_fbt th; return &th; From af38a8b4383858d47bcc576e5a8bfc47b84c6f59 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 26 Jun 2023 17:47:47 +0200 Subject: [PATCH 08/10] cleanup: move Type_collection_fbt<> template out of Type_handler_fbt<> --- sql/sql_type_fixedbin.h | 149 +++++++++++++++++++++------------------- 1 file changed, 78 insertions(+), 71 deletions(-) diff --git a/sql/sql_type_fixedbin.h b/sql/sql_type_fixedbin.h index fb9ec6a5a5a..134e76e8cb5 100644 --- a/sql/sql_type_fixedbin.h +++ b/sql/sql_type_fixedbin.h @@ -31,7 +31,9 @@ /***********************************************************************/ -template +template class Type_collection_fbt; + +template > class Type_handler_fbt: public Type_handler { /* =[ internal helper classes ]=============================== */ @@ -210,74 +212,6 @@ public: /* =[ API classes ]=========================================== */ - 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[]= - { - {singleton(), &type_handler_null, singleton()}, - {singleton(), &type_handler_varchar, singleton()}, - {singleton(), &type_handler_string, singleton()}, - {singleton(), &type_handler_tiny_blob, singleton()}, - {singleton(), &type_handler_blob, singleton()}, - {singleton(), &type_handler_medium_blob, singleton()}, - {singleton(), &type_handler_long_blob, singleton()}, - {singleton(), &type_handler_hex_hybrid, singleton()}, - {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[]= - { - {singleton(), &type_handler_null, singleton()}, - {singleton(), &type_handler_long_blob, singleton()}, - {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; - } - }; - class Type_std_attributes_fbt: public Type_std_attributes { public: @@ -1120,8 +1054,7 @@ public: const Type_collection *type_collection() const override { - static Type_collection_fbt type_collection_fbt; - return &type_collection_fbt; + return TypeCollectionImpl::singleton(); } const Name &default_value() const override @@ -1902,4 +1835,78 @@ public: } }; +template +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::singleton(), &type_handler_null, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_varchar, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_string, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_tiny_blob, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_blob, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_medium_blob, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_long_blob, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_hex_hybrid, Type_handler_fbt::singleton()}, + {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::singleton(), &type_handler_null, Type_handler_fbt::singleton()}, + {Type_handler_fbt::singleton(), &type_handler_long_blob, Type_handler_fbt::singleton()}, + {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; + } + + static Type_collection_fbt *singleton() + { + static Type_collection_fbt tc; + return &tc; + } +}; + #endif /* SQL_TYPE_FIXEDBIN_H */ From f6ecadfee87da7f3cd9c5d334b3183425397a025 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 3 Jul 2023 18:18:02 +0200 Subject: [PATCH 09/10] fix ASAN+safemalloc builds debug_sync refactoring introduced a statically instantiated object debug_sync_global of the structure st_debug_sync_globals. st_debug_sync_globals includes Hash_set<> which allocates memory in the constructor. sf_malloc() calls _my_thread_var()->dbug_id which is pthread_getspecific(THR_KEY_mysys), and THR_KEY_mysys is 0 before pthread_key_create(). pthread_getspecific(0) returns a valid pointer, not EINVAL. And safemalloc dereferences it. let's statically initialize THR_KEY_mysys to -1, this makes pthread_getspecific(THR_KEY_mysys) to fail before pthread_key_create() is called. followup for 8885225de66 --- mysys/my_thr_init.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mysys/my_thr_init.c b/mysys/my_thr_init.c index fd8a99c2196..2e8decd7d06 100644 --- a/mysys/my_thr_init.c +++ b/mysys/my_thr_init.c @@ -23,7 +23,7 @@ #include #include -pthread_key(struct st_my_thread_var*, THR_KEY_mysys); +pthread_key(struct st_my_thread_var*, THR_KEY_mysys=-1); mysql_mutex_t THR_LOCK_malloc, THR_LOCK_open, THR_LOCK_lock, THR_LOCK_myisam, THR_LOCK_heap, THR_LOCK_net, THR_LOCK_charset, THR_LOCK_threads, From 46b79b8cd1c8becbc9fffad503a584e0edaf91a3 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 3 Jul 2023 22:05:02 +0200 Subject: [PATCH 10/10] MDEV-30084 Shutdown hangs in some tests debug-only issue. the test was doing set debug_sync='now SIGNAL go3'; ... set debug_sync='reset'; which translated into add "go3" to the hash of active signals pthread_broadcast to wake up waiting threads ... clear the hash of active signals as a result a waiting thread was awoken, but the hash was emptied before the thread checked if its signal was in the hash. so the thread didn't find its signal and went back to sleep. let's wait until the awoken thread has completely finished disconnecting and was added to the thread cache. --- mysql-test/main/kill_debug.result | 1 + mysql-test/main/kill_debug.test | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/mysql-test/main/kill_debug.result b/mysql-test/main/kill_debug.result index 061e7602383..0c910906b92 100644 --- a/mysql-test/main/kill_debug.result +++ b/mysql-test/main/kill_debug.result @@ -234,6 +234,7 @@ connection default; set debug_sync='now WAIT_FOR go0'; set debug_sync='found_killee SIGNAL go1 WAIT_FOR go2'; kill $id; +select variable_value into @threads_cached from information_schema.global_status where variable_name='threads_cached'; set debug_sync='now SIGNAL go3'; drop table t1; set debug_sync='reset'; diff --git a/mysql-test/main/kill_debug.test b/mysql-test/main/kill_debug.test index 32a764004e3..6bade1d8d90 100644 --- a/mysql-test/main/kill_debug.test +++ b/mysql-test/main/kill_debug.test @@ -313,6 +313,12 @@ connection default; set debug_sync='now WAIT_FOR go0'; set debug_sync='found_killee SIGNAL go1 WAIT_FOR go2'; evalp kill $id; +select variable_value into @threads_cached from information_schema.global_status where variable_name='threads_cached'; set debug_sync='now SIGNAL go3'; +if (`select @@thread_handling != 'pool-of-threads'`) { + # cannot check that a thread was added to thread pool on windows, but the test works there w/o the wait + let wait_condition= select variable_value>@threads_cached from information_schema.global_status where variable_name='threads_cached'; + source include/wait_condition.inc; +} drop table t1; set debug_sync='reset';