diff options
Diffstat (limited to 'sql')
-rw-r--r-- | sql/field.cc | 36 | ||||
-rw-r--r-- | sql/field.h | 10 | ||||
-rw-r--r-- | sql/item.cc | 11 | ||||
-rw-r--r-- | sql/item.h | 3 | ||||
-rw-r--r-- | sql/item_jsonfunc.cc | 32 | ||||
-rw-r--r-- | sql/item_jsonfunc.h | 19 | ||||
-rw-r--r-- | sql/item_strfunc.h | 3 | ||||
-rw-r--r-- | sql/rpl_utility_server.cc | 2 | ||||
-rw-r--r-- | sql/sql_type.cc | 91 | ||||
-rw-r--r-- | sql/sql_type.h | 46 | ||||
-rw-r--r-- | sql/sql_type_json.cc | 224 | ||||
-rw-r--r-- | sql/sql_type_json.h | 143 | ||||
-rw-r--r-- | sql/sql_yacc.yy | 2 |
13 files changed, 523 insertions, 99 deletions
diff --git a/sql/field.cc b/sql/field.cc index 2c768527ced..8fa3bbd538c 100644 --- a/sql/field.cc +++ b/sql/field.cc @@ -7277,6 +7277,19 @@ bool Field_longstr::send(Protocol *protocol) } +const Type_handler *Field_string::type_handler() const +{ + if (is_var_string()) + return &type_handler_var_string; + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_string_json will provide its own Field_string_json. + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return &type_handler_string_json; + return &type_handler_string; +} + /* Copy a string and fill with space */ int Field_string::store(const char *from, size_t length,CHARSET_INFO *cs) @@ -7776,6 +7789,20 @@ en_fieldtype Field_string::tmp_engine_column_type(bool use_packed_rows) const const uint Field_varstring::MAX_SIZE= UINT_MAX16; + +const Type_handler *Field_varstring::type_handler() const +{ + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_varchar_json will provide its own Field_varstring_json + and Field_varstring_compressed_json + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return &type_handler_varchar_json; + return &type_handler_varchar; +} + + /** Save the field metadata for varstring fields. @@ -8897,6 +8924,15 @@ void Field_blob::sort_string(uchar *to,uint length) */ const Type_handler *Field_blob::type_handler() const { + /* + This is a temporary solution and will be fixed soon (in 10.9?). + Type_handler_*blob_json will provide its own Field_blob_json + and Field_blob_compressed_json. + */ + if (Type_handler_json_common::has_json_valid_constraint(this)) + return Type_handler_json_common:: + json_blob_type_handler_by_length_bytes(packlength); + switch (packlength) { case 1: return &type_handler_tiny_blob; case 2: return &type_handler_blob; diff --git a/sql/field.h b/sql/field.h index 7ea0ce6e479..34446b2befe 100644 --- a/sql/field.h +++ b/sql/field.h @@ -4010,12 +4010,7 @@ public: NONE, field_name_arg, collation), can_alter_field_type(1) {}; - const Type_handler *type_handler() const override - { - if (is_var_string()) - return &type_handler_var_string; - return &type_handler_string; - } + const Type_handler *type_handler() const override; enum ha_base_keytype key_type() const override { return binary() ? HA_KEYTYPE_BINARY : HA_KEYTYPE_TEXT; } en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override; @@ -4134,8 +4129,7 @@ public: share->varchar_fields++; } - const Type_handler *type_handler() const override - { return &type_handler_varchar; } + const Type_handler *type_handler() const override; en_fieldtype tmp_engine_column_type(bool use_packed_rows) const override { return FIELD_VARCHAR; diff --git a/sql/item.cc b/sql/item.cc index c661722ead4..3d98031db11 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -3091,17 +3091,6 @@ Item_field::Item_field(THD *thd, Item_field *item) } -bool Item_field::is_json_type() -{ - if (!field->check_constraint || - field->check_constraint->expr->type() != FUNC_ITEM) - return FALSE; - - Item_func *f= (Item_func *) field->check_constraint->expr; - return f->functype() == Item_func::JSON_VALID_FUNC; -} - - void Item_field::set_field(Field *field_par) { field=result_field=field_par; // for easy coding with fields diff --git a/sql/item.h b/sql/item.h index cffee740beb..cab3d3fca02 100644 --- a/sql/item.h +++ b/sql/item.h @@ -1868,7 +1868,6 @@ public: table during query processing (grouping and so on) */ virtual bool is_result_field() { return 0; } - virtual bool is_json_type() { return false; } virtual bool is_bool_literal() const { return false; } /* This is to handle printing of default values */ virtual bool need_parentheses_in_default() { return false; } @@ -3427,7 +3426,6 @@ public: my_decimal *val_decimal_result(my_decimal *) override; bool val_bool_result() override; bool is_null_result() override; - bool is_json_type() override; bool send(Protocol *protocol, st_value *buffer) override; Load_data_outvar *get_load_data_outvar() override { return this; } bool load_data_set_null(THD *thd, const Load_data_param *param) override @@ -5463,7 +5461,6 @@ public: { return ref ? (*ref)->get_typelib() : NULL; } - bool is_json_type() override { return (*ref)->is_json_type(); } bool walk(Item_processor processor, bool walk_subquery, void *arg) override { diff --git a/sql/item_jsonfunc.cc b/sql/item_jsonfunc.cc index ddf5fc32ea4..81003be4656 100644 --- a/sql/item_jsonfunc.cc +++ b/sql/item_jsonfunc.cc @@ -1,4 +1,4 @@ -/* Copyright (c) 2016, 2020, MariaDB Corporation. +/* Copyright (c) 2016, 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 @@ -1441,6 +1441,32 @@ null_return: } +/* + This reproduces behavior according to the former + Item_func_conv_charset::is_json_type() which returned args[0]->is_json_type(). + JSON functions with multiple string input with different character sets + wrap some arguments into Item_func_conv_charset. So the former + Item_func_conv_charset::is_json_type() took the JSON propery from args[0], + i.e. from the original argument before the conversion. + This is probably not always correct because an *explicit* + `CONVERT(arg USING charset)` is actually a general purpose string + expression, not a JSON expression. +*/ +static bool is_json_type(const Item *item) +{ + for ( ; ; ) + { + if (Type_handler_json_common::is_json_type_handler(item->type_handler())) + return true; + const Item_func_conv_charset *func; + if (!(func= dynamic_cast<const Item_func_conv_charset*>(item))) + return false; + item= func->arguments()[0]; + } + return false; +} + + static int append_json_value(String *str, Item *item, String *tmp_val) { if (item->type_handler()->is_bool_type()) @@ -1469,7 +1495,7 @@ static int append_json_value(String *str, Item *item, String *tmp_val) String *sv= item->val_json(tmp_val); if (item->null_value) goto append_null; - if (item->is_json_type()) + if (is_json_type(item)) return str->append(sv->ptr(), sv->length()); if (item->result_type() == STRING_RESULT) @@ -1515,7 +1541,7 @@ static int append_json_value_from_field(String *str, String *sv= f->val_str(tmp_val, key + offset); if (f->is_null_in_record(key)) goto append_null; - if (i->is_json_type()) + if (is_json_type(i)) return str->append(sv->ptr(), sv->length()); if (i->result_type() == STRING_RESULT) diff --git a/sql/item_jsonfunc.h b/sql/item_jsonfunc.h index d5c385b81fe..9472e184124 100644 --- a/sql/item_jsonfunc.h +++ b/sql/item_jsonfunc.h @@ -24,7 +24,7 @@ #include "item_cmpfunc.h" // Item_bool_func #include "item_strfunc.h" // Item_str_func #include "item_sum.h" - +#include "sql_type_json.h" class json_path_with_flags { @@ -123,12 +123,9 @@ public: :Item_str_func(thd, a, b) { } Item_json_func(THD *thd, List<Item> &list) :Item_str_func(thd, list) { } - bool is_json_type() override { return true; } - void make_send_field(THD *thd, Send_field *tmp_field) override + const Type_handler *type_handler() const override { - Item_str_func::make_send_field(thd, tmp_field); - static const Lex_cstring fmt(STRING_WITH_LEN("json")); - tmp_field->set_format_name(fmt); + return Type_handler_json_common::json_type_handler(max_length); } }; @@ -562,7 +559,10 @@ public: } Item_func_json_arrayagg(THD *thd, Item_func_json_arrayagg *item) : Item_func_group_concat(thd, item) {} - bool is_json_type() override { return true; } + const Type_handler *type_handler() const override + { + return Type_handler_json_common::json_type_handler_sum(this); + } const char *func_name() const override { return "json_arrayagg("; } enum Sumfunctype sum_func() const override { return JSON_ARRAYAGG_FUNC; } @@ -587,16 +587,13 @@ public: } Item_func_json_objectagg(THD *thd, Item_func_json_objectagg *item); - bool is_json_type() override { return true; } void cleanup() override; enum Sumfunctype sum_func() const override {return JSON_OBJECTAGG_FUNC;} const char *func_name() const override { return "json_objectagg"; } const Type_handler *type_handler() const override { - if (too_big_for_varchar()) - return &type_handler_blob; - return &type_handler_varchar; + return Type_handler_json_common::json_type_handler_sum(this); } void clear() override; bool add() override; diff --git a/sql/item_strfunc.h b/sql/item_strfunc.h index 36300e57f03..11d543dc474 100644 --- a/sql/item_strfunc.h +++ b/sql/item_strfunc.h @@ -3,7 +3,7 @@ /* Copyright (c) 2000, 2011, Oracle and/or its affiliates. - Copyright (c) 2009, 2019, MariaDB + Copyright (c) 2009, 2021, MariaDB 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 @@ -1454,7 +1454,6 @@ public: (cs->mbmaxlen > 1 || !(cs->state & MY_CS_NONASCII)))); } } - bool is_json_type() { return args[0]->is_json_type(); } String *val_str(String *); longlong val_int() { diff --git a/sql/rpl_utility_server.cc b/sql/rpl_utility_server.cc index 8110b142e74..ccad7bd0709 100644 --- a/sql/rpl_utility_server.cc +++ b/sql/rpl_utility_server.cc @@ -549,6 +549,8 @@ Field_longstr::rpl_conv_type_from(const Conv_source &source, binlog_type() == MYSQL_TYPE_VARCHAR_COMPRESSED || binlog_type() == MYSQL_TYPE_BLOB_COMPRESSED) same_type= binlog_type() == source.real_field_type(); + else if (Type_handler_json_common::is_json_type_handler(type_handler())) + same_type= type_handler()->type_handler_base() == source.type_handler(); else same_type= type_handler() == source.type_handler(); diff --git a/sql/sql_type.cc b/sql/sql_type.cc index c1801c1ae3e..7cfa7862f2b 100644 --- a/sql/sql_type.cc +++ b/sql/sql_type.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2015, 2020, MariaDB + Copyright (c) 2015, 2021, MariaDB 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 @@ -1758,16 +1758,21 @@ const Type_handler *Type_handler_typelib::cast_to_int_type_handler() const bool Type_handler_hybrid_field_type::aggregate_for_result(const Type_handler *other) { - const Type_handler *hres; - const Type_collection *c; - if (!(c= Type_handler::type_collection_for_aggregation(m_type_handler, other)) || - !(hres= c->aggregate_for_result(m_type_handler, other))) - hres= type_handler_data-> - m_type_aggregator_for_result.find_handler(m_type_handler, other); - if (!hres) - return true; - m_type_handler= hres; - return false; + Type_handler_pair tp(m_type_handler, other); + do + { + const Type_handler *hres; + const Type_collection *c; + if (((c= Type_handler::type_collection_for_aggregation(tp.a(), tp.b())) && + (hres= c->aggregate_for_result(tp.a(), tp.b()))) || + (hres= type_handler_data-> + m_type_aggregator_for_result.find_handler(tp.a(), tp.b()))) + { + m_type_handler= hres; + return false; + } + } while (tp.to_base()); + return true; } @@ -1971,26 +1976,29 @@ Type_collection_std::aggregate_for_comparison(const Type_handler *ha, bool Type_handler_hybrid_field_type::aggregate_for_min_max(const Type_handler *h) { - const Type_handler *hres; - const Type_collection *c; - if (!(c= Type_handler::type_collection_for_aggregation(m_type_handler, h))|| - !(hres= c->aggregate_for_min_max(m_type_handler, h))) + Type_handler_pair tp(m_type_handler, h); + do { - /* - For now we suppose that these two expressions: - - LEAST(type1, type2) - - COALESCE(type1, type2) - return the same data type (or both expressions return error) - if type1 and/or type2 are non-traditional. - This may change in the future. - */ - hres= type_handler_data-> - m_type_aggregator_for_result.find_handler(m_type_handler, h); - } - if (!hres) - return true; - m_type_handler= hres; - return false; + const Type_handler *hres; + const Type_collection *c; + if (((c= Type_handler::type_collection_for_aggregation(tp.a(), tp.b())) && + (hres= c->aggregate_for_min_max(tp.a(), tp.b()))) || + (hres= type_handler_data-> + m_type_aggregator_for_result.find_handler(tp.a(), tp.b()))) + { + /* + For now we suppose that these two expressions: + - LEAST(type1, type2) + - COALESCE(type1, type2) + return the same data type (or both expressions return error) + if type1 and/or type2 are non-traditional. + This may change in the future. + */ + m_type_handler= hres; + return false; + } + } while (tp.to_base()); + return true; } @@ -2129,15 +2137,20 @@ Type_handler_hybrid_field_type::aggregate_for_num_op(const Type_aggregator *agg, const Type_handler *h0, const Type_handler *h1) { - const Type_handler *hres; - const Type_collection *c; - if (!(c= Type_handler::type_collection_for_aggregation(h0, h1)) || - !(hres= c->aggregate_for_num_op(h0, h1))) - hres= agg->find_handler(h0, h1); - if (!hres) - return true; - m_type_handler= hres; - return false; + Type_handler_pair tp(h0, h1); + do + { + const Type_handler *hres; + const Type_collection *c; + if (((c= Type_handler::type_collection_for_aggregation(tp.a(), tp.b())) && + (hres= c->aggregate_for_num_op(tp.a(), tp.b()))) || + (hres= agg->find_handler(tp.a(), tp.b()))) + { + m_type_handler= hres; + return false; + } + } while (tp.to_base()); + return true; } diff --git a/sql/sql_type.h b/sql/sql_type.h index aa1fe1a9f7e..8ea5b9ddde1 100644 --- a/sql/sql_type.h +++ b/sql/sql_type.h @@ -2,7 +2,7 @@ #define SQL_TYPE_H_INCLUDED /* Copyright (c) 2015 MariaDB Foundation. - Copyright (c) 2015, 2020, MariaDB Corporation. + Copyright (c) 2015, 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 @@ -45,6 +45,7 @@ class Item_param; class Item_cache; class Item_copy; class Item_func_or_sum; +class Item_sum; class Item_sum_hybrid; class Item_sum_sum; class Item_sum_avg; @@ -3764,6 +3765,14 @@ public: incompatible data type. */ virtual bool is_param_long_data_type() const { return false; } + /* + The base type handler "this" is derived from. + "This" inherits aggregation rules from the base type handler. + */ + virtual const Type_handler *type_handler_base() const + { + return NULL; + } virtual const Type_handler *type_handler_for_comparison() const= 0; virtual const Type_handler *type_handler_for_native_format() const { @@ -7471,6 +7480,41 @@ public: const Type_handler *h0, const Type_handler *h1); }; + +class Type_handler_pair +{ + const Type_handler *m_a; + const Type_handler *m_b; +public: + Type_handler_pair(const Type_handler *a, + const Type_handler *b) + :m_a(a), m_b(b) + { } + const Type_handler *a() const { return m_a; } + const Type_handler *b() const { return m_b; } + /* + Change both handlers to their parent data type handlers, if available. + For example, VARCHAR/JSON -> VARCHAR. + @returns The number of handlers changed (0,1 or 2). + */ + bool to_base() + { + bool rc= false; + const Type_handler *na= m_a->type_handler_base(); + const Type_handler *nb= m_b->type_handler_base(); + if (na) + { + m_a= na; rc= true; + } + if (nb) + { + m_b= nb; rc= true; + } + return rc; + } +}; + + /* Helper template to simplify creating builtin types with names. Plugin types inherit from Type_handler_xxx types that do not set the name in diff --git a/sql/sql_type_json.cc b/sql/sql_type_json.cc index a804366ec03..c12b868e6b9 100644 --- a/sql/sql_type_json.cc +++ b/sql/sql_type_json.cc @@ -1,5 +1,5 @@ /* - Copyright (c) 2019, MariaDB + Copyright (c) 2019, 2021 MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -20,17 +20,111 @@ #include "sql_class.h" -Type_handler_json_longtext type_handler_json_longtext; +Named_type_handler<Type_handler_string_json> + type_handler_string_json("char/json"); + +Named_type_handler<Type_handler_varchar_json> + type_handler_varchar_json("varchar/json"); + +Named_type_handler<Type_handler_tiny_blob_json> + type_handler_tiny_blob_json("tinyblob/json"); + +Named_type_handler<Type_handler_blob_json> + type_handler_blob_json("blob/json"); + +Named_type_handler<Type_handler_medium_blob_json> + type_handler_medium_blob_json("mediumblob/json"); + +Named_type_handler<Type_handler_long_blob_json> + type_handler_long_blob_json("longblob/json"); + + +// Convert general purpose string type handlers to their JSON counterparts +const Type_handler * +Type_handler_json_common::json_type_handler_from_generic(const Type_handler *th) +{ + // Test in the order of likelyhood. + if (th == &type_handler_long_blob) + return &type_handler_long_blob_json; + if (th == &type_handler_varchar) + return &type_handler_varchar_json; + if (th == &type_handler_blob) + return &type_handler_blob_json; + if (th == &type_handler_tiny_blob) + return &type_handler_tiny_blob_json; + if (th == &type_handler_medium_blob) + return &type_handler_medium_blob_json; + if (th == &type_handler_string) + return &type_handler_string_json; + DBUG_ASSERT(is_json_type_handler(th)); + return th; +} + + +/* + This method resembles what Type_handler::string_type_handler() + does for general purpose string type handlers. +*/ +const Type_handler * +Type_handler_json_common::json_type_handler(uint max_octet_length) +{ + if (max_octet_length >= 16777216) + return &type_handler_long_blob_json; + else if (max_octet_length >= 65536) + return &type_handler_medium_blob_json; + else if (max_octet_length >= MAX_FIELD_VARCHARLENGTH) + return &type_handler_blob_json; + return &type_handler_varchar_json; +} + + +/* + This method resembles what Field_blob::type_handler() + does for general purpose BLOB type handlers. +*/ +const Type_handler * +Type_handler_json_common::json_blob_type_handler_by_length_bytes(uint len) +{ + switch (len) { + case 1: return &type_handler_tiny_blob_json; + case 2: return &type_handler_blob_json; + case 3: return &type_handler_medium_blob_json; + } + return &type_handler_long_blob_json; +} + + +/* + This method resembles what Item_sum_group_concat::type_handler() + does for general purpose string type handlers. +*/ +const Type_handler * +Type_handler_json_common::json_type_handler_sum(const Item_sum *item) +{ + if (item->too_big_for_varchar()) + return &type_handler_blob_json; + return &type_handler_varchar_json; +} + + +bool Type_handler_json_common::has_json_valid_constraint(const Field *field) +{ + return field->check_constraint && + field->check_constraint->expr && + field->check_constraint->expr->type() == Item::FUNC_ITEM && + static_cast<const Item_func *>(field->check_constraint->expr)-> + functype() == Item_func::JSON_VALID_FUNC; +} /** Create JSON_VALID(field_name) expression */ + Virtual_column_info * -Type_handler_json_longtext::make_json_valid_expr(THD *thd, - const LEX_CSTRING *field_name) - const +Type_handler_json_common::make_json_valid_expr(THD *thd, + const LEX_CSTRING *field_name) { Lex_ident_sys_st str; Item *field, *expr; @@ -46,12 +140,118 @@ Type_handler_json_longtext::make_json_valid_expr(THD *thd, } -bool Type_handler_json_longtext:: - Column_definition_validate_check_constraint(THD *thd, - Column_definition * c) const +bool Type_handler_json_common::make_json_valid_expr_if_needed(THD *thd, + Column_definition *c) +{ + return !c->check_constraint && + !(c->check_constraint= make_json_valid_expr(thd, &c->field_name)); +} + + +class Type_collection_json: public Type_collection +{ + const Type_handler *aggregate_common(const Type_handler *a, + const Type_handler *b) const + { + if (a == b) + return a; + if (a == &type_handler_null) + return b; + if (b == &type_handler_null) + return a; + return NULL; + } + + /* + Aggregate two JSON type handlers for result. + If one of the handlers is not JSON, NULL is returned. + */ + const Type_handler *aggregate_json_for_result(const Type_handler *a, + const Type_handler *b) const + { + if (!Type_handler_json_common::is_json_type_handler(a) || + !Type_handler_json_common::is_json_type_handler(b)) + return NULL; + // Here we have two JSON data types. Let's aggregate their base types. + const Type_handler *a0= a->type_handler_base(); + const Type_handler *b0= b->type_handler_base(); + // Base types are expected to belong to type_collection_std: + DBUG_ASSERT(a0->type_collection() == type_handler_null.type_collection()); + DBUG_ASSERT(b0->type_collection() == type_handler_null.type_collection()); + const Type_handler *c= a0->type_collection()->aggregate_for_result(a0, b0); + return Type_handler_json_common::json_type_handler_from_generic(c); + } +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_json_for_result(a, b))) + return h; + /* + One of the types is not JSON. + Let the caller aggregate according to the derived rules: + COALESCE(VARCHAR/JSON, TEXT) -> COALESCE(VARCHAR, TEXT) + */ + return NULL; + } + + const Type_handler *aggregate_for_min_max(const Type_handler *a, + const Type_handler *b) + const override + { + /* + No JSON specific rules. + Let the caller aggregate according to the derived rules: + LEAST(VARCHAR/JSON, TEXT/JSON) -> LEAST(VARCHAR, TEXT) + */ + return NULL; + } + + const Type_handler *aggregate_for_comparison(const Type_handler *a, + const Type_handler *b) + const override + { + /* + All JSON types return &type_handler_long_blob + in type_handler_for_comparison(). We should not get here. + */ + DBUG_ASSERT(0); + return NULL; + } + + const Type_handler *aggregate_for_num_op(const Type_handler *a, + const Type_handler *b) + const override + { + /* + No JSON specific rules. + Let the caller aggregate according to the derived rules: + (VARCHAR/JSON + TEXT/JSON) -> (VARCHAR + TEXT) + */ + 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; + } +}; + + +const Type_collection *Type_handler_json_common::type_collection() { - if (!c->check_constraint && - !(c->check_constraint= make_json_valid_expr(thd, &c->field_name))) - return true; - return Type_handler::Column_definition_validate_check_constraint(thd, c); + static Type_collection_json type_collection_json; + return &type_collection_json; } diff --git a/sql/sql_type_json.h b/sql/sql_type_json.h index 6c4ee8cb2eb..4a394809a06 100644 --- a/sql/sql_type_json.h +++ b/sql/sql_type_json.h @@ -1,7 +1,7 @@ #ifndef SQL_TYPE_JSON_INCLUDED #define SQL_TYPE_JSON_INCLUDED /* - Copyright (c) 2019, MariaDB + Copyright (c) 2019, 2021 MariaDB This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License @@ -21,18 +21,145 @@ #include "mariadb.h" #include "sql_type.h" -class Type_handler_json_longtext: public Type_handler_long_blob + +class Type_handler_json_common +{ +public: + static Virtual_column_info *make_json_valid_expr(THD *thd, + const LEX_CSTRING *field_name); + static bool make_json_valid_expr_if_needed(THD *thd, Column_definition *c); + static bool set_format_name(Send_field_extended_metadata *to) + { + static const Lex_cstring fmt(STRING_WITH_LEN("json")); + return to->set_format_name(fmt); + } + static const Type_handler *json_type_handler(uint max_octet_length); + static const Type_handler *json_blob_type_handler_by_length_bytes(uint len); + static const Type_handler *json_type_handler_sum(const Item_sum *sum); + static const Type_handler *json_type_handler_from_generic(const Type_handler *th); + static bool has_json_valid_constraint(const Field *field); + static const Type_collection *type_collection(); + static bool is_json_type_handler(const Type_handler *handler) + { + return handler->type_collection() == type_collection(); + } +}; + + +template <class BASE, const Named_type_handler<BASE> &thbase> +class Type_handler_general_purpose_string_to_json: + public BASE, + public Type_handler_json_common { - Virtual_column_info *make_json_valid_expr(THD *thd, - const LEX_CSTRING *field_name) - const; public: - virtual ~Type_handler_json_longtext() {} + const Type_handler *type_handler_base() const override + { + return &thbase; + } + const Type_collection *type_collection() const override + { + return Type_handler_json_common::type_collection(); + } bool Column_definition_validate_check_constraint(THD *thd, - Column_definition *c) const; + Column_definition *c) + const override + { + return make_json_valid_expr_if_needed(thd, c) || + BASE::Column_definition_validate_check_constraint(thd, c); + } + bool Column_definition_data_type_info_image(Binary_string *to, + const Column_definition &def) + const override + { + /* + Override the inherited method to avoid JSON type handlers writing any + extended metadata to FRM. JSON type handlers are currently detected + only by CHECK(JSON_VALID()) constraint. This may change in the future + to do write extended metadata to FRM, for more reliable detection. + */ + return false; + } + + bool Item_append_extended_type_info(Send_field_extended_metadata *to, + const Item *item) const + { + return set_format_name(to); // Send "format=json" in the protocol + } + + bool Item_hybrid_func_fix_attributes(THD *thd, + const char *name, + Type_handler_hybrid_field_type *hybrid, + Type_all_attributes *attr, + Item **items, uint nitems) + const override + { + if (BASE::Item_hybrid_func_fix_attributes(thd, name, hybrid, attr, + items, nitems)) + return true; + /* + The above call can change the type handler on "hybrid", e.g. + choose a proper BLOB type handler according to the calculated max_length. + Convert general purpose string type handler to its JSON counterpart. + This makes hybrid functions preserve JSON data types, e.g.: + COALESCE(json_expr1, json_expr2) -> JSON + */ + hybrid->set_handler(json_type_handler_from_generic(hybrid->type_handler())); + return false; + } }; + +class Type_handler_string_json: + public Type_handler_general_purpose_string_to_json<Type_handler_string, + type_handler_string> +{ }; + + +class Type_handler_varchar_json: + public Type_handler_general_purpose_string_to_json<Type_handler_varchar, + type_handler_varchar> +{ }; + +class Type_handler_tiny_blob_json: + public Type_handler_general_purpose_string_to_json<Type_handler_tiny_blob, + type_handler_tiny_blob> +{ }; + +class Type_handler_blob_json: + public Type_handler_general_purpose_string_to_json<Type_handler_blob, + type_handler_blob> +{ }; + + +class Type_handler_medium_blob_json: + public Type_handler_general_purpose_string_to_json<Type_handler_medium_blob, + type_handler_medium_blob> +{ }; + +class Type_handler_long_blob_json: + public Type_handler_general_purpose_string_to_json<Type_handler_long_blob, + type_handler_long_blob> +{ }; + + + +extern MYSQL_PLUGIN_IMPORT + Named_type_handler<Type_handler_string_json> type_handler_string_json; + +extern MYSQL_PLUGIN_IMPORT + Named_type_handler<Type_handler_varchar_json> type_handler_varchar_json; + extern MYSQL_PLUGIN_IMPORT - Type_handler_json_longtext type_handler_json_longtext; + Named_type_handler<Type_handler_tiny_blob_json> type_handler_tiny_blob_json; + +extern MYSQL_PLUGIN_IMPORT + Named_type_handler<Type_handler_blob_json> type_handler_blob_json; + +extern MYSQL_PLUGIN_IMPORT + Named_type_handler<Type_handler_medium_blob_json> type_handler_medium_blob_json; + +extern MYSQL_PLUGIN_IMPORT + Named_type_handler<Type_handler_long_blob_json> type_handler_long_blob_json; + #endif // SQL_TYPE_JSON_INCLUDED diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy index 93663e32158..7554fdebec1 100644 --- a/sql/sql_yacc.yy +++ b/sql/sql_yacc.yy @@ -6431,7 +6431,7 @@ field_type_lob: | JSON_SYM opt_compressed { Lex->charset= &my_charset_utf8mb4_bin; - $$.set(&type_handler_json_longtext); + $$.set(&type_handler_long_blob_json); } ; |