Coverage Report

Created: 2025-08-03 06:43

/src/tarantool/src/box/xrow_update_map.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2010-2019, Tarantool AUTHORS, please see AUTHORS file.
3
 *
4
 * Redistribution and use in source and binary forms, with or
5
 * without modification, are permitted provided that the following
6
 * conditions are met:
7
 *
8
 * 1. Redistributions of source code must retain the above
9
 *    copyright notice, this list of conditions and the
10
 *    following disclaimer.
11
 *
12
 * 2. Redistributions in binary form must reproduce the above
13
 *    copyright notice, this list of conditions and the following
14
 *    disclaimer in the documentation and/or other materials
15
 *    provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21
 * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
22
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
28
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
 * SUCH DAMAGE.
30
 */
31
#include "xrow_update_field.h"
32
#include "fiber.h"
33
#include "small/region.h"
34
#include "tuple_format.h"
35
36
/**
37
 * Descriptor of one updated key-value pair. Besides updated data
38
 * it contains a tail with unchanged pairs, just so as not to
39
 * create a separate object for them, and to be similar with array
40
 * update items.
41
 */
42
struct xrow_update_map_item {
43
  /**
44
   * Updated key. Can be NULL. In such a case this item
45
   * contains only an unchanged tail. A key can be nullified
46
   * if it was removed from the map, or when a map is just
47
   * created and has no any update yet.
48
   */
49
  const char *key;
50
  /** Length of @a key. */
51
  uint32_t key_len;
52
  /** Updated value. */
53
  struct xrow_update_field field;
54
  /** Link in the list of updated map keys. */
55
  struct stailq_entry in_items;
56
  /** Pointer to unchanged tail data. */
57
  const char *tail_data;
58
  /** Size in bytes of unchanged tail data. */
59
  uint32_t tail_size;
60
};
61
62
static inline struct xrow_update_map_item *
63
xrow_update_map_item_alloc(void)
64
0
{
65
0
  return xregion_alloc_object(&fiber()->gc, struct xrow_update_map_item);
66
0
}
67
68
static void
69
xrow_update_map_create_item(struct xrow_update_field *field,
70
          struct xrow_update_map_item *item,
71
          enum xrow_update_type type, const char *key,
72
          uint32_t key_len, const char *data,
73
          uint32_t data_size, uint32_t tail_size)
74
0
{
75
0
  assert(field->type == XUPDATE_MAP);
76
0
  item->key = key;
77
0
  item->key_len = key_len;
78
0
  item->field.type = type;
79
0
  item->field.data = data;
80
0
  item->field.size = data_size;
81
0
  item->tail_data = data + data_size;
82
0
  item->tail_size = tail_size;
83
  /*
84
   * Each time a new item it created it is stored in the
85
   * head of update map item list. It helps in case the
86
   * tuple is regularly updated, because on all next updates
87
   * this key will be found from the very beginning of the
88
   * map.
89
   */
90
0
  stailq_add_entry(&field->map.items, item, in_items);
91
0
}
92
93
static inline struct xrow_update_map_item *
94
xrow_update_map_new_item(struct xrow_update_field *field,
95
       enum xrow_update_type type, const char *key,
96
       uint32_t key_len, const char *data, uint32_t data_size,
97
       uint32_t tail_size)
98
0
{
99
0
  struct xrow_update_map_item *item = xrow_update_map_item_alloc();
100
0
  xrow_update_map_create_item(field, item, type, key, key_len,
101
0
            data, data_size, tail_size);
102
0
  return item;
103
0
}
104
105
/**
106
 * Find an update item to which @a op should be applied. The
107
 * target field may do not exist, but at least its parent should.
108
 */
109
static int
110
xrow_update_map_extract_opt_item(struct xrow_update_field *field,
111
         struct xrow_update_op *op,
112
         struct xrow_update_map_item **res)
113
0
{
114
0
  assert(field->type == XUPDATE_MAP);
115
0
  if (op->is_token_consumed) {
116
0
    if (xrow_update_op_next_token(op) != 0)
117
0
      return -1;
118
0
    if (op->token_type != JSON_TOKEN_STR) {
119
0
      return xrow_update_err(op, "can't update a map by not "\
120
0
                 "a string key");
121
0
    }
122
0
  }
123
0
  struct stailq *items = &field->map.items;
124
0
  struct xrow_update_map_item *i, *new_item;
125
  /*
126
   * Firstly, try to find the key among already updated
127
   * ones. Perhaps, the needed node is just an intermediate
128
   * key of a bigger JSON path, and there are many updates
129
   * passing this key, so it should be here for all except
130
   * first updates.
131
   */
132
0
  stailq_foreach_entry(i, items, in_items) {
133
0
    if (i->key != NULL && i->key_len == op->key_len &&
134
0
        memcmp(i->key, op->key, i->key_len) == 0) {
135
0
      *res = i;
136
0
      return 0;
137
0
    }
138
0
  }
139
  /*
140
   * Slow path - the key is updated first time, need to
141
   * decode tails.
142
   */
143
0
  uint32_t key_len, i_tail_size;
144
0
  const char *pos, *key, *end, *tmp, *begin;
145
0
  stailq_foreach_entry(i, items, in_items) {
146
0
    begin = i->tail_data;
147
0
    pos = begin;
148
0
    end = begin + i->tail_size;
149
0
    i_tail_size = 0;
150
0
    while(pos < end) {
151
0
      if (mp_typeof(*pos) != MP_STR) {
152
0
        mp_next(&pos);
153
0
        mp_next(&pos);
154
0
        continue;
155
0
      }
156
0
      i_tail_size = pos - begin;
157
0
      key = mp_decode_str(&pos, &key_len);
158
0
      if (key_len == op->key_len &&
159
0
          memcmp(key, op->key, key_len) == 0)
160
0
        goto key_is_found;
161
0
      mp_next(&pos);
162
0
    }
163
0
  }
164
0
  *res = NULL;
165
0
  return 0;
166
167
0
key_is_found:
168
0
  tmp = pos;
169
0
  mp_next(&tmp);
170
0
  if (i_tail_size == 0 && i->key == NULL) {
171
    /*
172
     * Looks like the needed key was found from the
173
     * beginning of tail of an item without a key.
174
     * This is actually good, because this item can
175
     * be transformed instead of a new item creation.
176
     */
177
0
    i->key = op->key;
178
0
    i->key_len = op->key_len;
179
0
    i->field.data = pos;
180
0
    i->field.size = tmp - pos;
181
0
    i->tail_data = tmp;
182
0
    i->tail_size = end - tmp;
183
0
    new_item = i;
184
0
  } else {
185
0
    new_item = xrow_update_map_new_item(field, XUPDATE_NOP, op->key,
186
0
                op->key_len, pos, tmp - pos,
187
0
                end - tmp);
188
0
    i->tail_size = i_tail_size;
189
0
  }
190
0
  *res = new_item;
191
0
  return 0;
192
0
}
193
194
/**
195
 * The same as optional extractor, but the field to update should
196
 * exist. It is the case of all scalar operations (except '=' - it
197
 * can work as insert).
198
 */
199
static inline struct xrow_update_map_item *
200
xrow_update_map_extract_item(struct xrow_update_field *field,
201
           struct xrow_update_op *op)
202
0
{
203
0
  assert(field->type == XUPDATE_MAP);
204
0
  struct xrow_update_map_item *res;
205
0
  if (xrow_update_map_extract_opt_item(field, op, &res) != 0)
206
0
    return NULL;
207
0
  if (res == NULL)
208
0
    xrow_update_err_no_such_field(op);
209
0
  return res;
210
0
}
211
212
int
213
xrow_update_op_do_map_insert(struct xrow_update_op *op,
214
           struct xrow_update_field *field)
215
0
{
216
0
  assert(field->type == XUPDATE_MAP);
217
0
  struct xrow_update_map_item *item;
218
0
  if (xrow_update_map_extract_opt_item(field, op, &item) != 0)
219
0
    return -1;
220
0
  if (!xrow_update_op_is_term(op)) {
221
0
    if (item == NULL)
222
0
      return xrow_update_err_no_such_field(op);
223
0
    op->is_token_consumed = true;
224
0
    return xrow_update_op_do_field_insert(op, &item->field);
225
0
  }
226
0
  if (item != NULL)
227
0
    return xrow_update_err_duplicate(op);
228
0
  ++field->map.size;
229
0
  item = xrow_update_map_new_item(field, XUPDATE_NOP, op->key,
230
0
          op->key_len, op->arg.set.value,
231
0
          op->arg.set.length, 0);
232
0
  return 0;
233
0
}
234
235
int
236
xrow_update_op_do_map_set(struct xrow_update_op *op,
237
        struct xrow_update_field *field)
238
0
{
239
0
  assert(field->type == XUPDATE_MAP);
240
0
  struct xrow_update_map_item *item;
241
0
  if (xrow_update_map_extract_opt_item(field, op, &item) != 0)
242
0
    return -1;
243
0
  if (!xrow_update_op_is_term(op)) {
244
0
    if (item == NULL)
245
0
      return xrow_update_err_no_such_field(op);
246
0
    op->is_token_consumed = true;
247
0
    return xrow_update_op_do_field_set(op, &item->field);
248
0
  }
249
0
  if (item != NULL) {
250
0
    item->field.type = XUPDATE_NOP;
251
0
    item->field.data = op->arg.set.value;
252
0
    item->field.size = op->arg.set.length;
253
0
    return 0;
254
0
  }
255
0
  ++field->map.size;
256
0
  item = xrow_update_map_new_item(field, XUPDATE_NOP, op->key,
257
0
          op->key_len, op->arg.set.value,
258
0
          op->arg.set.length, 0);
259
0
  return 0;
260
0
}
261
262
int
263
xrow_update_op_do_map_delete(struct xrow_update_op *op,
264
           struct xrow_update_field *field)
265
0
{
266
0
  assert(field->type == XUPDATE_MAP);
267
0
  struct xrow_update_map_item *item;
268
0
  if (xrow_update_map_extract_opt_item(field, op, &item) != 0)
269
0
    return -1;
270
0
  if (!xrow_update_op_is_term(op)) {
271
0
    if (item == NULL)
272
0
      return xrow_update_err_no_such_field(op);
273
0
    op->is_token_consumed = true;
274
0
    return xrow_update_op_do_field_delete(op, &item->field);
275
0
  }
276
0
  if (op->arg.del.count != 1)
277
0
    return xrow_update_err_delete1(op);
278
0
  if (item == NULL)
279
0
    return 0;
280
  /*
281
   * Note, even if tail size becomes 0, this item is not
282
   * deleted. This is because items are linked via stailq,
283
   * elements of which can't be deleted as simple as that.
284
   * But it is not a big deal, because '#' is a really rare
285
   * operation.
286
   * Why just a next key from the tail can't be decoded?
287
   * Why key should be NULL here? This is because the JSON
288
   * updates allow to update a map containing non-string
289
   * keys. If the next key is not a string, it can't be
290
   * saved as key of the item. Therefore, it is better not
291
   * to touch unchanged tails unless a new update operation
292
   * needs it.
293
   */
294
0
  item->key = NULL;
295
0
  item->key_len = 0;
296
0
  item->field.size = 0;
297
0
  item->field.type = XUPDATE_NOP;
298
0
  --field->map.size;
299
0
  return 0;
300
0
}
301
302
#define DO_SCALAR_OP_GENERIC(op_type)           \
303
int                   \
304
xrow_update_op_do_map_##op_type(struct xrow_update_op *op,      \
305
0
        struct xrow_update_field *field)    \
306
0
{                   \
307
0
  assert(field->type == XUPDATE_MAP);         \
308
0
  struct xrow_update_map_item *item =         \
309
0
    xrow_update_map_extract_item(field, op);      \
310
0
  if (item == NULL)             \
311
0
    return -1;             \
312
0
  if (!xrow_update_op_is_term(op)) {         \
313
0
    op->is_token_consumed = true;         \
314
0
    return xrow_update_op_do_field_##op_type(op, &item->field); \
315
0
  }                  \
316
0
  struct xrow_update_scalar *scalar = &item->field.scalar;    \
317
0
  /* Just stub for non scalar field. op_do_ * will fail on it. */   \
318
0
  struct xrow_update_scalar na = { .type = XUPDATE_TYPE_NONE };   \
319
0
  if (item->field.type == XUPDATE_NOP) {         \
320
0
    const char *data = item->field.data;        \
321
0
    xrow_update_mp_read_scalar(&data, &item->field.scalar);   \
322
0
  } else if (item->field.type != XUPDATE_SCALAR) {     \
323
0
    scalar = &na;             \
324
0
  }                  \
325
0
  if (xrow_update_op_do_##op_type(op, scalar) != 0)     \
326
0
    return -1;             \
327
0
  item->field.type = XUPDATE_SCALAR;          \
328
0
  return 0;               \
329
0
}
Unexecuted instantiation: xrow_update_op_do_map_arith
Unexecuted instantiation: xrow_update_op_do_map_bit
Unexecuted instantiation: xrow_update_op_do_map_splice
330
331
DO_SCALAR_OP_GENERIC(arith)
332
333
DO_SCALAR_OP_GENERIC(bit)
334
335
DO_SCALAR_OP_GENERIC(splice)
336
337
void
338
xrow_update_map_create(struct xrow_update_field *field, const char *header,
339
           const char *data, const char *data_end, int field_count)
340
0
{
341
0
  field->type = XUPDATE_MAP;
342
0
  field->data = header;
343
0
  field->size = data_end - header;
344
0
  field->map.size = field_count;
345
0
  stailq_create(&field->map.items);
346
0
  if (field_count == 0)
347
0
    return;
348
0
  xrow_update_map_new_item(field, XUPDATE_NOP, NULL, 0, data, 0,
349
0
         data_end - data);
350
0
}
351
352
void
353
xrow_update_map_create_with_child(struct xrow_update_field *field,
354
          const char *header,
355
          const struct xrow_update_field *child,
356
          const char *key, uint32_t key_len)
357
0
{
358
0
  field->type = XUPDATE_MAP;
359
0
  field->data = header;
360
0
  stailq_create(&field->map.items);
361
362
0
  const char *pos = header;
363
0
  uint32_t i, ikey_len, field_count = mp_decode_map(&pos);
364
0
  const char *begin = pos;
365
0
  struct xrow_update_map_item *item = NULL;
366
0
  for (i = 0; i < field_count; ++i) {
367
0
    if (mp_typeof(*pos) != MP_STR) {
368
0
      mp_next(&pos);
369
0
      mp_next(&pos);
370
0
      continue;
371
0
    }
372
0
    const char *before_key = pos;
373
0
    const char *ikey = mp_decode_str(&pos, &ikey_len);
374
0
    if (ikey_len == key_len && memcmp(ikey, key, key_len) == 0) {
375
0
      item = xrow_update_map_new_item(field, XUPDATE_NOP,
376
0
              NULL, 0, begin, 0,
377
0
              before_key - begin);
378
0
      ++i;
379
0
      break;
380
0
    }
381
0
    mp_next(&pos);
382
0
  }
383
  /*
384
   * When a map is initialized with an existing child, it
385
   * means, that it was already found earlier. I.e. it is
386
   * impossible to miss it.
387
   */
388
0
  assert(item != NULL);
389
0
  const char *data = pos;
390
0
  mp_next(&pos);
391
0
  uint32_t data_size = pos - data;
392
0
  for (; i < field_count; ++i) {
393
0
    mp_next(&pos);
394
0
    mp_next(&pos);
395
0
  }
396
0
  item = xrow_update_map_item_alloc();
397
0
  item->field = *child;
398
0
  xrow_update_map_create_item(field, item, child->type, key, key_len,
399
0
            data, data_size, pos - data - data_size);
400
0
  field->map.size = field_count;
401
0
  field->size = pos - header;
402
0
}
403
404
uint32_t
405
xrow_update_map_sizeof(struct xrow_update_field *field)
406
0
{
407
0
  assert(field->type == XUPDATE_MAP);
408
0
  uint32_t res = mp_sizeof_map(field->map.size);
409
0
  struct xrow_update_map_item *i;
410
0
  stailq_foreach_entry(i, &field->map.items, in_items) {
411
0
    res += i->tail_size;
412
0
    if (i->key != NULL) {
413
0
      res += mp_sizeof_str(i->key_len) +
414
0
             xrow_update_field_sizeof(&i->field);
415
0
    }
416
0
  }
417
0
  return res;
418
0
}
419
420
uint32_t
421
xrow_update_map_store(struct xrow_update_field *field,
422
          struct json_tree *format_tree,
423
          struct json_token *this_node, char *out, char *out_end)
424
0
{
425
0
  assert(field->type == XUPDATE_MAP);
426
0
  char *out_begin = out;
427
0
  out = mp_encode_map(out, field->map.size);
428
0
  struct xrow_update_map_item *i;
429
  /*
430
   * This is the trick about saving updated keys before
431
   * others. The first cycle doesn't save unchanged tails.
432
   */
433
0
  if (this_node == NULL) {
434
0
    stailq_foreach_entry(i, &field->map.items, in_items) {
435
0
      if (i->key != NULL) {
436
0
        out = mp_encode_str(out, i->key, i->key_len);
437
0
        out += xrow_update_field_store(&i->field, NULL,
438
0
                     NULL, out,
439
0
                     out_end);
440
0
      }
441
0
    }
442
0
  } else {
443
0
    struct json_token token;
444
0
    token.type = JSON_TOKEN_STR;
445
0
    struct json_token *next_node;
446
0
    stailq_foreach_entry(i, &field->map.items, in_items) {
447
0
      if (i->key != NULL) {
448
0
        token.str = i->key;
449
0
        token.len = i->key_len;
450
0
        next_node = json_tree_lookup(format_tree,
451
0
                   this_node,
452
0
                   &token);
453
0
        out = mp_encode_str(out, i->key, i->key_len);
454
0
        out += xrow_update_field_store(&i->field,
455
0
                     format_tree,
456
0
                     next_node, out,
457
0
                     out_end);
458
0
      }
459
0
    }
460
0
  }
461
0
  stailq_foreach_entry(i, &field->map.items, in_items) {
462
0
    memcpy(out, i->tail_data, i->tail_size);
463
0
    out += i->tail_size;
464
0
  }
465
0
  assert(out <= out_end);
466
0
  return out - out_begin;
467
0
}