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