summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rw-r--r--sql/field.cc36
-rw-r--r--sql/field.h10
-rw-r--r--sql/item.cc11
-rw-r--r--sql/item.h3
-rw-r--r--sql/item_jsonfunc.cc32
-rw-r--r--sql/item_jsonfunc.h19
-rw-r--r--sql/item_strfunc.h3
-rw-r--r--sql/rpl_utility_server.cc2
-rw-r--r--sql/sql_type.cc91
-rw-r--r--sql/sql_type.h46
-rw-r--r--sql/sql_type_json.cc224
-rw-r--r--sql/sql_type_json.h143
-rw-r--r--sql/sql_yacc.yy2
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);
}
;