summaryrefslogtreecommitdiff
path: root/plugin/type_mysql_json/type.cc
blob: b897f64a3b2b668a440c2743e9fdfd6903579ef3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
   Copyright (c) 2020 MariaDB Foundation

   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
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335  USA */

#include <my_global.h>
#include <mysql/plugin_data_type.h>
#include <sql_type.h>
#include <field.h>
#include <mysqld_error.h>
#include "mysql_json.h"

const LEX_CSTRING empty_clex_str= {"", 0};

class Type_handler_mysql_json: public Type_handler_blob
{
public:
  Field *make_conversion_table_field(MEM_ROOT *, TABLE *, uint, const Field *)
    const override;
  const Type_collection *type_collection() const override;
  Field *make_table_field_from_def(TABLE_SHARE *, MEM_ROOT *,
                                   const LEX_CSTRING *, const Record_addr &,
                                   const Bit_addr &,
                                   const Column_definition_attributes *,
                                   uint32) const override;
  Field *make_table_field(MEM_ROOT *, const LEX_CSTRING *,
                          const Record_addr &, const Type_all_attributes &,
                          TABLE_SHARE *) const override;
  void Column_definition_reuse_fix_attributes(THD *thd,
                                              Column_definition *def,
                                              const Field *field) const override;
  const Type_handler *type_handler_base() const override
  {
    /*
      Override this method in the same way with what Type_handler_blob_json
      does, to tell the server that MySQL JSON inherits aggregation behaviour
      from the LONGBLOB data type.
      This makes MariaDB JSON column and a MySQL JSON column compatible for
      assignment, so "ALTER TABLE table_with_mysql_json FORCE" can run without
      raising ER_ILLEGAL_PARAMETER_DATA_TYPES2_FOR_OPERATION.
    */
    return &type_handler_long_blob;
  }
};

Type_handler_mysql_json type_handler_mysql_json;


class Field_mysql_json: public Field_blob
{
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)
    : Field_blob(ptr_arg, null_ptr_arg, null_bit_arg, unireg_check_arg,
                 field_name_arg, share, blob_pack_length,
                 &my_charset_utf8mb4_bin)
  {}

  String *val_str(String *val_buffer, String *val_str);
  const Type_handler *type_handler() const { return &type_handler_mysql_json; }
  bool parse_mysql(String *dest, const char *data, size_t length) const;
  bool send(Protocol *protocol) { return Field::send(protocol); }
  void sql_type(String &s) const
  { s.set_ascii(STRING_WITH_LEN("json /* MySQL 5.7 */")); }
  /* this will make ALTER TABLE to consider it different from built-in field */
  Compression_method *compression_method() const { return (Compression_method*)1; }
};

Field *Type_handler_mysql_json::make_conversion_table_field(MEM_ROOT *root,
                    TABLE *table, uint metadata, const Field *target) const
{
  uint pack_length= metadata & 0x00ff;
  if (pack_length < 1 || pack_length > 4)
    return NULL; // Broken binary log?
  return new (root)
         Field_mysql_json(NULL, (uchar *) "", 1, Field::NONE, &empty_clex_str,
                          table->s, pack_length, target->charset());
}

Field *Type_handler_mysql_json::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) const
{
  return new (root) Field_mysql_json(addr.ptr(), addr.null_ptr(),
                 addr.null_bit(), attr->unireg_check, name, share,
                 attr->pack_flag_to_pack_length(), attr->charset);
}

void Type_handler_mysql_json::
       Column_definition_reuse_fix_attributes(THD *thd,
                                              Column_definition *def,
                                              const Field *field) const
{
  Type_handler_blob::Column_definition_reuse_fix_attributes(thd, def, field);
  def->decimals= 0;
}



Field *Type_handler_mysql_json::make_table_field(MEM_ROOT *root,
                 const LEX_CSTRING *name, const Record_addr &addr,
                 const Type_all_attributes &attr, TABLE_SHARE *share) const
{
  return new (root) Field_mysql_json(addr.ptr(), addr.null_ptr(),
                 addr.null_bit(), Field::NONE, name, share, 2, attr.collation);
}


String *Field_mysql_json::val_str(String *val_buffer, String *val_ptr)
{
  String *raw_value= Field_blob::val_str(val_buffer, val_ptr);
  String data;

  data.copy(*raw_value);

  val_ptr->length(0);
  if (parse_mysql(val_ptr, data.ptr(), data.length()))
  {
    val_ptr->length(0);
    my_printf_error(ER_UNKNOWN_ERROR,
        "Error parsing MySQL JSON format, please dump this table from MySQL "
        "and then restore it to be able to use it in MariaDB.", MYF(0));
  }
  return val_ptr;
}

bool Field_mysql_json::parse_mysql(String *dest,
                                   const char *data, size_t length) const
{
  if (!data)
    return false;

  /* Each JSON blob must start with a type specifier. */
  if (length < 2)
    return true;

  if (parse_mysql_json_value(dest, static_cast<JSONB_TYPES>(data[0]),
                             reinterpret_cast<const uchar*>(data) + 1,
                             length - 1, 0))
    return true;

  return false;
}

class Type_collection_mysql_json: public Type_collection
{
public:
  const Type_handler *aggregate_for_result(const Type_handler *a,
                                           const Type_handler *b)
                                           const override
  {
    if (a == b)
      return a;
    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
  {
    return aggregate_for_result(a, b);
  }

  const Type_handler *aggregate_for_num_op(const Type_handler *a,
                                           const Type_handler *b)
                                           const override
  {
    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
{
  static Type_collection_mysql_json type_collection_mysql_json;
  return &type_collection_mysql_json;
}

static struct st_mariadb_data_type plugin_descriptor_type_mysql_json=
{
  MariaDB_DATA_TYPE_INTERFACE_VERSION,
  &type_handler_mysql_json
};

maria_declare_plugin(type_mysql_json)
{
  MariaDB_DATA_TYPE_PLUGIN,
  &plugin_descriptor_type_mysql_json,
  "MYSQL_JSON",
  "Anel Husaković, Vicențiu Ciorbaru",
  "Data type MYSQL_JSON",
  PLUGIN_LICENSE_GPL,
  0,
  0,
  0x0001,
  NULL,
  NULL,
  "0.1",
  MariaDB_PLUGIN_MATURITY_STABLE
}
maria_declare_plugin_end;