File: create_json_unpacker.inc

package info (click to toggle)
mysql-8.0 8.0.43-3
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,273,924 kB
  • sloc: cpp: 4,684,605; ansic: 412,450; pascal: 108,398; java: 83,641; perl: 30,221; cs: 27,067; sql: 26,594; sh: 24,181; python: 21,816; yacc: 17,169; php: 11,522; xml: 7,388; javascript: 7,076; makefile: 2,194; lex: 1,075; awk: 670; asm: 520; objc: 183; ruby: 97; lisp: 86
file content (302 lines) | stat: -rw-r--r-- 11,397 bytes parent folder | download
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# ==== Purpose ====
#
# Create a named 'unpacker', which is capable of unpacking JSON
# objects into a set of mysqltest variables.  For instance, it might
# unpack { "x": 1, "y": 2 } into the variable assignments $x=1, $y=2.
# A single unpacker can be used with multiple JSON objects.
#
# The unpacker consists of a .inc file, which unpacks a fixed set of
# named keys given when creating the unpacker, from an object
# given when invoking the unpacker.
#
# This script exposes the filename of the .inc file as an mtr variable.
#
# ==== Usage ====
#
# --let $json_label = NAME
# --let $json_keys = KEY1[, KEY2[, ...]]
# [--let $json_required = 0 | 1 | LIST]
# [--let $json_defaults = JSON_OBJECT]
# [--let $json_output_json_quoted = 0 | 1 | LIST]
# [--let $json_output_single_quoted = 0 | 1 | LIST]
# [--let $json_verbose = 0 | 1 | LIST]
# --source include/create_json_unpacker.inc
#
# --let $json_object = { "KEY1": VAL1[, "KEY2": VAL2 [, ...]] }
# --source $json_NAME_unpack
# # Now there are mysqltest variables named $KEY1, $KEY2, ...
# # holding the values VAL1, VAL2, ...
#
# At the end of the test, source include/destroy_json_functions.inc
# to remove all .inc files created by this script and the related
# include/create_json_* scripts.
#
# Parameters:
#
#   $json_label
#     Identifier used to distinguish this unpacker from other unpackers.
#
#   $json_keys
#     Comma-separated list of keys to unpack.
#     Since these are going to be used as names of mtr variables, they
#     must all be 7-bit ascii, alphanumeric strings.
#
#   $json_required
#     By default, the unpacker will accept that keys are missing, and
#     return a default value (see $json_defaults).
#     If all the keys are required, set this to 1.
#     If some of the keys are required, set this to a comma-separated
#     list containing those keys which are required.
#     When a required key is missing, the test will fail.
#
#   $json_defaults
#     By default, the unpacker will return an empty string if a key
#     is missing. To use another default, set $json_defaults to a
#     JSON object where keys from $json_keys are mapped to their
#     default values.
#
#   $json_output_json_quoted
#   $json_output_single_quoted
#     These options control the output format.  If
#     $json_output_json_quoted is used, JSON strings in the output
#     retain their double quotes; otherwise the double quotes are
#     stripped using JSON_UNQUOTE.  If $json_output_single_quoted is
#     used, any backslash and single quote characters are escaped so
#     that the result can be used in a single-quoted string.
#     Examples:
#     json_quoted single_quoted   "blowin' in the wind"
#          0            0         blowin' in the wind
#          0            1         blowin\' in the wind
#          1            0         "blowin' in the wind"
#          1            1         "blowin\' in the wind"
#     To apply the same quoting rules to all unpacked values, set the
#     options to 0 or 1. To specify individual quoting for each key,
#     set the options to comma-separated lists of keys: the quoting
#     rule will be enabled for the values of the listed keys and
#     disabled for the values of keys that are not in the lists.
#
#   $json_verbose
#     By default, this script prints nothing. If this option is set to
#     1, the script prints all key-value pairs to the result log. If
#     this option is set to a comma-separated list of keys, the script
#     prints the key-value pair for the keys in the list.
#
# Output:
#
#   This script creates a .inc file in $MYSQLTEST_VARDIR/tmp which is
#   capable of unpacking values from a JSON document into mtr
#   variables.  If $json_label is NAME, this script sets the variable
#   $json_NAME_unpack to the filename of the .inc file.  The script
#   should be used as follows:
#
#     --let $json_object = JSON_OBJECT
#     [--let $json_allow_extra_keys = 1]
#     --source $json_NAME_unpack
#
#       Parameters:
#
#         $json_object
#
#           The object from which values will be unpacked.
#
#         $json_allow_extra_keys
#
#           By default, this script checks if $json_object contains
#           any keys that were not specified in $json_keys when
#           create_json_iterator was invoked. To suppress that check,
#           set $json_allow_extra_keys=1.
#
# ==== Examples ===
#
# See mysql-test/t/mysqltest_json.test.

# Get number of array elements and number of keys.
--let CJU_LABEL = $json_label
--let CJU_KEYS = $json_keys
--let CJU_DEFAULTS = $json_defaults
--let CJU_REQUIRED = $json_required
--let CJU_JSON_QUOTED = $json_output_json_quoted
--let CJU_SINGLE_QUOTED = $json_output_single_quoted
--let CJU_VERBOSE = $json_verbose
--let CJU_UUID = `SELECT UUID()`

perl END_OF_PERL;
  my $label = $ENV{'CJU_LABEL'};
  my $key_text = $ENV{'CJU_KEYS'};
  my $defaults_text = $ENV{'CJU_DEFAULTS'};
  my $required_text = $ENV{'CJU_REQUIRED'};
  my $json_quoted_text = $ENV{'CJU_JSON_QUOTED'};
  my $single_quoted_text = $ENV{'CJU_SINGLE_QUOTED'};
  my $verbose_text = $ENV{'CJU_VERBOSE'};
  my $uuid = $ENV{'CJU_UUID'};
  my $file_infix = "/tmp/json_$label";
  my $file_prefix = $ENV{'MYSQLTEST_VARDIR'} . $file_infix;
  my $file_prefix_generator = '$MYSQLTEST_VARDIR' . $file_infix;
  my $init_file = "${file_prefix}_init_unpacker.inc";
  my $unpack_file = "${file_prefix}_unpack.inc";
  my $var_prefix = 'json_' . $label;
  my $unpack_file_var = $var_prefix . '_unpack';
  my $defaults_var = $var_prefix . '_defaults';
  my $added_var = $var_prefix . '_added_unpacker_to_json_function_files';

  if ($label !~ /^[a-zA-Z0-9_]+$/) {
    die "!!!ERROR IN TEST: Set \$json_label an alphanumeric 7-bit ASCII identifier (it was '$label')";
  }
  if (!$key_text) {
    die "!!!ERROR IN TEST: Set \$json_keys.";
  }

  my @key_list = split / *, */, $key_text;
  my %key_hash = ();
  my $key_check_sql = '';
  for my $key (@key_list) {
    $key_hash{$key} = 1;
    $key_check_sql .= ", '\$.\"$key\"'";
  }

  my %required_hash = ();
  if ($required_text eq '1') { $required_text = $key_text; }
  elsif ($required_text eq '0') { $required_text = ''; }
  for my $key (split / *, */, $required_text) {
    if (!defined($key_hash{$key})) {
      die "!!!ERROR IN TEST: \$json_required contains key <$key> which is not in \$json_keys";
    }
    $required_hash{$key} = 1;
  }

  my %json_quoted_hash = ();
  if ($json_quoted_text eq '1') { $json_quoted_text = $key_text; }
  elsif ($json_quoted_text eq '0') { $json_quoted_text = ''; }
  for my $key (split(/ *, */, $json_quoted_text)) {
    if (!defined($key_hash{$key})) {
      die "!!!ERROR IN TEST: \$json_output_json_quoted contains key <$key> which is not in \$json_keys";
    }
    $json_quoted_hash{$key} = 1;
  }

  my %single_quoted_hash = ();
  if ($single_quoted_text eq '1') { $single_quoted_text = $key_text; }
  if ($single_quoted_text eq '0') { $single_quoted_text = ''; }
  for my $key (split(/ *, */, $single_quoted_text)) {
    if (!defined($key_hash{$key})) {
      die "!!!ERROR IN TEST: \$json_output_single_quoted contains key <$key> which is not in \$json_keys";
    }
    $single_quoted_hash{$key} = 1;
  }

  my %verbose_hash = ();
  if ($verbose_text eq '1') { $verbose_text = $key_text; }
  if ($verbose_text eq '0') { $verbose_text = ''; }
  for my $key (split(/ *, */, $verbose_text)) {
    if (!defined($key_hash{$key})) {
      die "!!!ERROR IN TEST: \$json_verbose contains key <$key> which is not in \$json_keys";
    }
    $verbose_hash{$key} = 1;
  }

  # INIT_FILE will be sourced at the end of create_json_unpacker.inc
  open(INIT_FILE, "> $init_file")
    or die "Error $? opening $init_file: $!";
  print INIT_FILE (###############################################
    "
    --let \$$unpack_file_var = ${file_prefix_generator}_unpack.inc
    --let \$$defaults_var = escape('\\,\$json_defaults)
    if (\$rpl_debug) {
      --echo # $unpack_file_var: $defaults_var=<\$$defaults_var>
      # This gives a useful error message for malformed JSON.
      if (\$$defaults_var) {
        eval SELECT JSON_TYPE('\$$defaults_var') AS JSON_TYPE;
      }
    }
    if (!\$$added_var) {
      --let \$$added_var = 1
      --let \$json_function_files = \$json_function_files ${file_prefix_generator}_init_unpacker.inc \$$unpack_file_var
    }

  "###############################################################
  ) or die "Error $? writing to $init_file: $!";

  close(INIT_FILE)
    or die "Error $? closing $init_file: $!";

  open(UNPACK_FILE, "> $unpack_file")
    or die "Error $? opening $unpack_file: $!";
  print UNPACK_FILE (############################################
    "
    # Check JSON syntax and get a reasonable error message.
    --let \$json = \$json_object
    --source include/json_check.inc

    --let \$json_object_escaped = escape('\\,\$json_object)
    if (\$rpl_debug) {
      --echo # $unpack_file_var: json_object=<\$json_object>
      --echo # $unpack_file_var: json_object_escaped=<\$json_object_escaped>
    }

    # Check that no keys other than the expected ones exist in the object.
    if (!\$json_allow_extra_keys) {
      if (`SELECT JSON_REMOVE('\$json_object_escaped'$key_check_sql) != CAST('{}' AS JSON)`) {
        --enable_result_log
        --echo json_object=<\$json_object>
        --echo json_keys=<\$json_keys>
        eval SELECT JSON_REMOVE('\$json_object_escaped'$key_check_sql) AS unknown_keys;
        --die !!!ERROR IN TEST: Unknown keys in json_object
      }
    }
    "###########################################################
  ) or die "Error $? writing to $unpack_file: $!";
  if (%verbose_hash) {
    print UNPACK_FILE (
      "--echo $label:\n"
    );
  }
  for my $key (split(/ *, */, $key_text)) {
    if ($key !~ /^[a-zA-Z0-9_]+$/) {
      die "!!!ERROR IN TEST: \$json_keys should contain only alphanumeric 7-bit ASCII identifiers";
    }
    my $o = "'\$json_object_escaped'";
    my $k = "'\$.\"$key\"'";
    my $func = 'JSON_EXTRACT';
    my $end = '';
    if (!defined($json_quoted_hash{$key})) {
      $func = "JSON_UNQUOTE($func";
      $end = ')';
    }
    if (defined($required_hash{$key})) {
      print UNPACK_FILE (#######################################
        "
        --let \$$key = `SELECT IF(JSON_CONTAINS_PATH($o, 'all', $k), $func($o, $k)$end, '$uuid')`
        if (\$$key == '$uuid') {
          --echo Missing value for \"$key\" in <\$json_object>
          --die Missing value for $key in json_object
        }
        "#######################################################
      ) or die "Error $? writing to $unpack_file: $!";
    } elsif ($defaults_text) {
      print UNPACK_FILE (
        "--let \$$key = `SELECT $func(IF(JSON_CONTAINS_PATH($o, 'all', $k), $o, '\$$defaults_var'), $k)$end`\n"
      ) or die "Error $? writing to $unpack_file: $!";
    } else {
      print UNPACK_FILE (
        "--let \$$key = `SELECT $func($o, $k)$end`\n"
      );
    }
    if (defined($single_quoted_hash{$key})) {
      print UNPACK_FILE (
        "
        --let \$$key = escape('\\,\$$key)
        "
      );
    }
    if (defined($verbose_hash{$key})) {
      print UNPACK_FILE (
        "--echo - $key=\$$key\n"
      ) or die "Error $? writing to $unpack_file: $!";
    }
  }
  close(UNPACK_FILE)
    or die "Error $? closing $unpack_file: $!";
END_OF_PERL

--let $cju_init = _init_unpacker
--source $MYSQLTEST_VARDIR/tmp/json_$json_label$cju_init.inc