/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 | } |