1 /*
2 Copyright (c) 2020 MariaDB Foundation
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; version 2 of the License.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software
15 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
16
17 #include <mysql/plugin_data_type.h>
18 #include <my_global.h>
19 #include <sql_type.h>
20 #include <field.h>
21 #include <mysqld_error.h>
22 #include "mysql_json.h"
23
24 const LEX_CSTRING empty_clex_str= {"", 0};
25
26 class Type_handler_mysql_json: public Type_handler_blob
27 {
28 public:
29 Field *make_conversion_table_field(MEM_ROOT *, TABLE *, uint, const Field *)
30 const override;
31 const Type_collection *type_collection() const override;
32 Field *make_table_field_from_def(TABLE_SHARE *, MEM_ROOT *,
33 const LEX_CSTRING *, const Record_addr &,
34 const Bit_addr &,
35 const Column_definition_attributes *,
36 uint32) const override;
37 Field *make_table_field(MEM_ROOT *, const LEX_CSTRING *,
38 const Record_addr &, const Type_all_attributes &,
39 TABLE_SHARE *) const override;
40 void Column_definition_reuse_fix_attributes(THD *thd,
41 Column_definition *def,
42 const Field *field) const override;
43 };
44
45 Type_handler_mysql_json type_handler_mysql_json;
46
47
48 class Field_mysql_json: public Field_blob
49 {
50 public:
Field_mysql_json(uchar * ptr_arg,uchar * null_ptr_arg,uchar null_bit_arg,enum utype unireg_check_arg,const LEX_CSTRING * field_name_arg,TABLE_SHARE * share,uint blob_pack_length,const DTCollation & collation)51 Field_mysql_json(uchar *ptr_arg, uchar *null_ptr_arg,
52 uchar null_bit_arg, enum utype unireg_check_arg,
53 const LEX_CSTRING *field_name_arg, TABLE_SHARE *share,
54 uint blob_pack_length, const DTCollation &collation)
55 : Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
56 field_name_arg, share, blob_pack_length,
57 &my_charset_utf8mb4_bin)
58 {}
59
60 String *val_str(String *val_buffer, String *val_str);
type_handler() const61 const Type_handler *type_handler() const { return &type_handler_mysql_json; }
62 bool parse_mysql(String *dest, const char *data, size_t length) const;
send(Protocol * protocol)63 bool send(Protocol *protocol) { return Field::send(protocol); }
sql_type(String & s) const64 void sql_type(String &s) const
65 { s.set_ascii(STRING_WITH_LEN("json /* MySQL 5.7 */")); }
66 /* this will make ALTER TABLE to consider it different from built-in field */
compression_method() const67 Compression_method *compression_method() const { return (Compression_method*)1; }
68 };
69
make_conversion_table_field(MEM_ROOT * root,TABLE * table,uint metadata,const Field * target) const70 Field *Type_handler_mysql_json::make_conversion_table_field(MEM_ROOT *root,
71 TABLE *table, uint metadata, const Field *target) const
72 {
73 uint pack_length= metadata & 0x00ff;
74 if (pack_length < 1 || pack_length > 4)
75 return NULL; // Broken binary log?
76 return new (root)
77 Field_mysql_json(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
78 table->s, pack_length, target->charset());
79 }
80
make_table_field_from_def(TABLE_SHARE * share,MEM_ROOT * root,const LEX_CSTRING * name,const Record_addr & addr,const Bit_addr & bit,const Column_definition_attributes * attr,uint32 flags) const81 Field *Type_handler_mysql_json::make_table_field_from_def(TABLE_SHARE *share,
82 MEM_ROOT *root, const LEX_CSTRING *name,
83 const Record_addr &addr, const Bit_addr &bit,
84 const Column_definition_attributes *attr, uint32 flags) const
85 {
86 return new (root) Field_mysql_json(addr.ptr(), addr.null_ptr(),
87 addr.null_bit(), attr->unireg_check, name, share,
88 attr->pack_flag_to_pack_length(), attr->charset);
89 }
90
91 void Type_handler_mysql_json::
Column_definition_reuse_fix_attributes(THD * thd,Column_definition * def,const Field * field) const92 Column_definition_reuse_fix_attributes(THD *thd,
93 Column_definition *def,
94 const Field *field) const
95 {
96 Type_handler_blob::Column_definition_reuse_fix_attributes(thd, def, field);
97 def->decimals= 0;
98 }
99
100
101
make_table_field(MEM_ROOT * root,const LEX_CSTRING * name,const Record_addr & addr,const Type_all_attributes & attr,TABLE_SHARE * share) const102 Field *Type_handler_mysql_json::make_table_field(MEM_ROOT *root,
103 const LEX_CSTRING *name, const Record_addr &addr,
104 const Type_all_attributes &attr, TABLE_SHARE *share) const
105 {
106 return new (root) Field_mysql_json(addr.ptr(), addr.null_ptr(),
107 addr.null_bit(), Field::NONE, name, share, 2, attr.collation);
108 }
109
110
val_str(String * val_buffer,String * val_ptr)111 String *Field_mysql_json::val_str(String *val_buffer, String *val_ptr)
112 {
113 String *raw_value= Field_blob::val_str(val_buffer, val_ptr);
114 String data;
115
116 data.copy(*raw_value);
117
118 val_ptr->length(0);
119 if (parse_mysql(val_ptr, data.ptr(), data.length()))
120 {
121 val_ptr->length(0);
122 my_printf_error(ER_UNKNOWN_ERROR,
123 "Error parsing MySQL JSON format, please dump this table from MySQL "
124 "and then restore it to be able to use it in MariaDB.", MYF(0));
125 }
126 return val_ptr;
127 }
128
parse_mysql(String * dest,const char * data,size_t length) const129 bool Field_mysql_json::parse_mysql(String *dest,
130 const char *data, size_t length) const
131 {
132 if (!data)
133 return false;
134
135 /* Each JSON blob must start with a type specifier. */
136 if (length < 2)
137 return true;
138
139 if (parse_mysql_json_value(dest, static_cast<JSONB_TYPES>(data[0]),
140 reinterpret_cast<const uchar*>(data) + 1,
141 length - 1, 0))
142 return true;
143
144 return false;
145 }
146
147 class Type_collection_mysql_json: public Type_collection
148 {
149 public:
aggregate_for_result(const Type_handler * a,const Type_handler * b) const150 const Type_handler *aggregate_for_result(const Type_handler *a,
151 const Type_handler *b)
152 const override
153 {
154 if (a == b)
155 return a;
156 return NULL;
157 }
158
aggregate_for_min_max(const Type_handler * a,const Type_handler * b) const159 const Type_handler *aggregate_for_min_max(const Type_handler *a,
160 const Type_handler *b)
161 const override
162 {
163 return aggregate_for_result(a, b);
164 }
165
aggregate_for_comparison(const Type_handler * a,const Type_handler * b) const166 const Type_handler *aggregate_for_comparison(const Type_handler *a,
167 const Type_handler *b)
168 const override
169 {
170 return aggregate_for_result(a, b);
171 }
172
aggregate_for_num_op(const Type_handler * a,const Type_handler * b) const173 const Type_handler *aggregate_for_num_op(const Type_handler *a,
174 const Type_handler *b)
175 const override
176 {
177 return NULL;
178 }
179
handler_by_name(const LEX_CSTRING & name) const180 const Type_handler *handler_by_name(const LEX_CSTRING &name) const override
181 {
182 if (type_handler_mysql_json.name().eq(name))
183 return &type_handler_mysql_json;
184 return NULL;
185 }
186 };
187
type_collection() const188 const Type_collection *Type_handler_mysql_json::type_collection() const
189 {
190 static Type_collection_mysql_json type_collection_mysql_json;
191 return &type_collection_mysql_json;
192 }
193
194 static struct st_mariadb_data_type plugin_descriptor_type_mysql_json=
195 {
196 MariaDB_DATA_TYPE_INTERFACE_VERSION,
197 &type_handler_mysql_json
198 };
199
maria_declare_plugin(type_mysql_json)200 maria_declare_plugin(type_mysql_json)
201 {
202 MariaDB_DATA_TYPE_PLUGIN,
203 &plugin_descriptor_type_mysql_json,
204 "MYSQL_JSON",
205 "Anel Husaković, Vicențiu Ciorbaru",
206 "Data type MYSQL_JSON",
207 PLUGIN_LICENSE_GPL,
208 0,
209 0,
210 0x0001,
211 NULL,
212 NULL,
213 "0.1",
214 MariaDB_PLUGIN_MATURITY_BETA
215 }
216 maria_declare_plugin_end;
217