]> git.8kb.co.uk Git - postgresql/pg_jsonb_opx/blob - jsonb_opx.c
Fix some logic when modifying scalars and simplify simple text deletion.
[postgresql/pg_jsonb_opx] / jsonb_opx.c
1 /* 
2  * jsonb_opx.c 
3  *     Test jsonb delete and concatenate operator functions for 9.4
4  *
5  * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  * Author: Glyn Astill <glyn@8kb.co.uk>
8  *
9  * This is purely experimentation and will contain many errors and bad form
10  * DO NOT USE ON PRODUCTION SYSTEMS.
11  *
12  */
13
14 #include "postgres.h"
15 #include "fmgr.h"
16 #include "utils/array.h"
17 #include "utils/jsonb.h"
18 #include "catalog/pg_type.h"
19 #include "utils/builtins.h"
20 #include "jsonb_opx.h"
21
22 #ifdef PG_MODULE_MAGIC
23         PG_MODULE_MAGIC;
24 #endif 
25
26 Datum jsonb_delete_key(PG_FUNCTION_ARGS);
27
28 PG_FUNCTION_INFO_V1(jsonb_delete_key);
29
30 /*
31  * Operator function to delete key from left operand where a match is found in
32  * the right operand.
33  *
34  * jsonb, text -> jsonb
35  *
36  */
37 Datum
38 jsonb_delete_key(PG_FUNCTION_ARGS)
39 {
40     /* pointers to incoming jsonb and text data */
41     Jsonb *input_jsonb = PG_GETARG_JSONB(0);
42     text  *input_text = PG_GETARG_TEXT_P(1);
43    
44     /* pointers to return jsonb value data and state to be converted to jsonb on return */
45     JsonbParseState *state = NULL;
46     JsonbValue *return_jsonb_value = NULL;
47
48     /* pointer to iterator for input_jsonb value data */
49     JsonbIterator *jsonb_iterator;
50     JsonbValue  jsonb_iterator_value;
51     int32 jsonb_iterator_token;
52
53     /* variables used for skip logic */
54     int32 skip_level = 0;
55     int32 nest_level = 0;
56     int32 array_level = 0;
57     bool push = true;
58
59     /*
60     * If we've been supplied with an existing key iterate round json data and rebuild 
61     * with key/element excluded.
62     *
63     * skip_key, nest_level and array_level are crude counts to check if the the value 
64     * for the key is closed and ensure we don't match on keys within nested objects.  
65     * Because we are recursing into nested elements but blindly just pushing them onto 
66     * the return value we can get away without deeper knowledge of the json?
67     */
68
69     jsonb_iterator = JsonbIteratorInit(&input_jsonb->root);
70
71     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE) 
72     {
73         push = true;
74         switch (jsonb_iterator_token)
75         {
76         case WJB_BEGIN_ARRAY:
77             array_level++;
78             break;
79         case WJB_BEGIN_OBJECT:
80             nest_level++;
81             break;
82         case WJB_ELEM:
83         case WJB_KEY:
84             if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) ||
85                     (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1)))
86             {
87                 if (jsonb_iterator_value.type == jbvString)
88                 {
89                     if ((jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(input_text)) &&
90                         (memcmp(jsonb_iterator_value.val.string.val,
91                             VARDATA_ANY(input_text),
92                             jsonb_iterator_value.val.string.len) == 0))
93                     {
94
95                             if (jsonb_iterator_token == WJB_ELEM)
96                                 push = false;
97                             else
98                                 skip_level = nest_level;
99                     }
100                 }
101             }
102         }
103
104         if (push && (skip_level == 0 || nest_level < skip_level))
105         {
106             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
107         }
108
109         switch (jsonb_iterator_token)
110         {
111         case WJB_END_OBJECT:
112             nest_level--;
113             if (skip_level == nest_level && array_level == 0)
114                 skip_level = 0;
115             break;
116         case WJB_END_ARRAY:
117             array_level--;
118             if (skip_level == nest_level && array_level == 0)
119                 skip_level = 0;
120             break;
121         case WJB_VALUE:
122             if (skip_level == nest_level)
123                 skip_level = 0;
124         }
125     }
126
127     if (JB_ROOT_IS_SCALAR(input_jsonb) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
128         return_jsonb_value->val.array.rawScalar = true;
129
130     PG_FREE_IF_COPY(input_jsonb, 0);
131     PG_FREE_IF_COPY(input_text, 1);
132
133     PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value));
134 }
135
136 Datum jsonb_delete_keys(PG_FUNCTION_ARGS);
137
138 PG_FUNCTION_INFO_V1(jsonb_delete_keys);
139
140 /*
141  * Operator function to delete keys from left operand where a match is found in
142  * the right operand.
143  *
144  * jsonb, text[] -> jsonb
145  *
146  */
147 Datum 
148 jsonb_delete_keys(PG_FUNCTION_ARGS)
149 {
150     /* general loops */
151     int i;
152
153     /* pointers to incoming jsonb and text[] data */
154     Jsonb *input_jsonb = PG_GETARG_JSONB(0);
155     ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1);
156     
157     /* pointers to return jsonb value data and state to be converted to jsonb on return */
158     JsonbParseState *state = NULL;
159     JsonbValue *return_jsonb_value = NULL;
160
161     /* pointer to iterator for input_jsonb value data */
162     JsonbIterator *jsonb_iterator;
163     JsonbValue  jsonb_iterator_value;
164     int32 jsonb_iterator_token;
165
166     /* variables used for skip logic */
167     int32 skip_level = 0;
168     int32 nest_level = 0;
169     int32 array_level = 0;
170     bool push = true;
171
172     /* array element variables for use during deconstruction */
173     Datum *datums;
174     bool *nulls;
175     int32 count;
176
177     /* individual array values values from incoming text[] */
178     text *array_element_text;
179
180     /* assert input_array is a text array type */
181     Assert(ARR_ELEMTYPE(input_array) == TEXTOID);
182
183     /* check input_array is one-dimensional */
184     if (ARR_NDIM(input_array) > 1)
185         ereport(ERROR, 
186             (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
187                 errmsg("1 dimensional text array expected")));
188
189     /* deconstruct array elements */
190     deconstruct_array(input_array, TEXTOID, -1, false, 'i', 
191                        &datums, &nulls, &count);
192
193     /* if the array is empty there's no work to do so return the input value */ 
194     if (count == 0) 
195         PG_RETURN_JSONB(input_jsonb);
196
197     /*
198     * If we've been supplied with existing keys iterate round json data and rebuild 
199     * with keys/elements excluded.
200     *
201     * skip_level, nest_level and array_level are crude counts to check if the the value 
202     * for the key is closed and ensure we don't match on keys within nested objects.  
203     * Because we are recursing into nested elements but blindly just pushing them onto 
204     * the return value we can get away without deeper knowledge of the json?
205     */
206     jsonb_iterator = JsonbIteratorInit(&input_jsonb->root);
207
208     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE) {
209
210         push = true; 
211         switch (jsonb_iterator_token)
212         {
213         case WJB_BEGIN_ARRAY:
214             array_level++;
215             break;
216         case WJB_BEGIN_OBJECT:
217             nest_level++;
218             break;
219         case WJB_ELEM:
220         case WJB_KEY:
221             if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level == 1 && array_level == 0) ||
222                     (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1)))
223             {
224                 if (jsonb_iterator_value.type == jbvString || jsonb_iterator_value.type == jbvNull)
225                 {
226                     for (i=0; i<count; i++)
227                     {
228                         if (!nulls[i])
229                             array_element_text = DatumGetTextP(datums[i]);
230                         else
231                             array_element_text = NULL;
232
233                         if (((array_element_text != NULL) && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(array_element_text)) &&
234                             (memcmp(jsonb_iterator_value.val.string.val,
235                                 VARDATA_ANY(array_element_text),
236                                 jsonb_iterator_value.val.string.len) == 0)) || ((array_element_text == NULL) && (jsonb_iterator_value.type == jbvNull)))
237                         {
238                             if (jsonb_iterator_token == WJB_ELEM)
239                                 push = false;
240                             else
241                                 skip_level = nest_level;
242                             break;
243                         }
244                     }
245                 }
246             }
247         }
248
249         if (push && (skip_level == 0 || nest_level < skip_level))
250         {
251             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
252         }
253
254         switch (jsonb_iterator_token)
255         {
256         case WJB_END_OBJECT:
257             nest_level--;
258             if (skip_level == nest_level && array_level == 0)
259                 skip_level = 0;
260             break;
261         case WJB_END_ARRAY:
262             array_level--;
263             if (skip_level == nest_level && array_level == 0)
264                 skip_level = 0;
265             break;
266         case WJB_VALUE:
267             if (skip_level == nest_level)
268                 skip_level = 0;
269         } 
270     }
271
272     if (JB_ROOT_IS_SCALAR(input_jsonb) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
273         return_jsonb_value->val.array.rawScalar = true;
274
275     PG_FREE_IF_COPY(input_jsonb, 0); 
276     PG_FREE_IF_COPY(input_array, 1); 
277
278     PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value));
279 }
280
281 Datum jsonb_delete_jsonb(PG_FUNCTION_ARGS);
282
283 PG_FUNCTION_INFO_V1(jsonb_delete_jsonb);
284
285 /*
286  * Operator function to delete keys and values from left operand where a match 
287  * is found in the right operand.
288  *
289  * jsonb, jsonb -> jsonb
290  *
291  */
292 Datum 
293 jsonb_delete_jsonb(PG_FUNCTION_ARGS)
294 {
295     /* pointers to incoming jsonb and text[] data */
296     Jsonb *input_jsonb_a = PG_GETARG_JSONB(0);
297     Jsonb *input_jsonb_b = PG_GETARG_JSONB(1);
298     
299     /* pointers to return jsonb value data and state to be converted to jsonb on return */
300     JsonbValue *return_jsonb_value = NULL;
301     JsonbParseState *state = NULL;
302
303     /* pointer to iterator for input_jsonb_a and temporary value data */
304     JsonbIterator *jsonb_iterator;
305     JsonbValue  jsonb_iterator_value;
306     JsonbValue  jsonb_iterator_key;
307     int32 jsonb_iterator_token;
308     bool skip_nested = false;
309     
310     bool push = true;
311
312     /* pointer to lookup on input_jsonb_b */
313     JsonbValue *jsonb_lookup_value = NULL;
314     
315     /*
316      * check if either right jsonb is empty and return left if so
317      */
318     if (JB_ROOT_COUNT(input_jsonb_b) == 0)
319         PG_RETURN_JSONB(input_jsonb_a);
320
321     jsonb_iterator = JsonbIteratorInit(&input_jsonb_a->root);
322
323     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, skip_nested)) != WJB_DONE)
324     {
325         skip_nested = true;
326         push = true;
327
328         switch (jsonb_iterator_token)
329         {
330         case WJB_BEGIN_ARRAY:
331         case WJB_BEGIN_OBJECT:
332         case WJB_END_ARRAY:
333         case WJB_END_OBJECT:
334             return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, NULL);
335         break;
336         case WJB_ELEM:
337             /*
338              * findJsonbValueFromContainer only supports jsonb arrays containting scalar values? 
339              * If container is something like '[[1]]' or '[{"a":1}]' will error with "invalid jsonb scalar type"
340              */
341             jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root, JB_FOBJECT | JB_FARRAY, &jsonb_iterator_value);
342             if (jsonb_lookup_value == NULL) 
343             {
344                 if (jsonb_iterator_value.type == jbvBinary) 
345                 {
346                     return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data);
347                 }
348                 else 
349                 {
350                     return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_iterator_value);
351                 }
352             }
353             break;
354         case WJB_KEY :
355             jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root, JB_FOBJECT | JB_FARRAY, &jsonb_iterator_value);
356
357             jsonb_iterator_key = jsonb_iterator_value;
358             jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, skip_nested);
359             if (jsonb_iterator_token != WJB_VALUE)
360                 elog(ERROR, "invalid JsonbIteratorNext (expected WJB_VALUE) rc: %d", jsonb_iterator_token);
361
362             if (jsonb_lookup_value != NULL)
363             {
364
365                 if (jsonb_lookup_value->type == jsonb_iterator_value.type) 
366                 {
367                     switch (jsonb_lookup_value->type) 
368                     {
369                         case jbvNull:
370                                 push = false;
371                         break;
372                         case jbvNumeric:
373                             if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
374                                 PointerGetDatum(jsonb_lookup_value->val.numeric), 
375                                 PointerGetDatum(jsonb_iterator_value.val.numeric))))
376                                 push = false;
377                         break;
378                         case jbvString:
379                             if ((jsonb_lookup_value->val.string.len == jsonb_iterator_value.val.string.len) &&
380                                 (memcmp(jsonb_lookup_value->val.string.val, 
381                                     jsonb_iterator_value.val.string.val, 
382                                     jsonb_lookup_value->val.string.len) == 0))
383                                 push = false;
384                         break;
385                         case jbvBinary:
386                             if ((jsonb_lookup_value->val.binary.len == jsonb_iterator_value.val.binary.len) &&
387                                 (memcmp(jsonb_lookup_value->val.binary.data, 
388                                     jsonb_iterator_value.val.binary.data, 
389                                     jsonb_lookup_value->val.binary.len) == 0))
390                                 push = false;
391                         break;
392                         case jbvBool:
393                             if (jsonb_lookup_value->val.boolean == jsonb_iterator_value.val.boolean)
394                                 push = false;
395                         break;
396                         case jbvArray:
397                         /* should not be possible? */
398                         case jbvObject:
399                         /* should not be possible? */
400                         default:
401                         ereport(ERROR, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("unexpected lookup type %i", jsonb_iterator_token)));
402                     }
403                 }
404             }
405
406             if (push) 
407             {                
408                 return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_key);
409
410                 /* if our value is nested binary data, iterate separately pushing each val */
411                 if (jsonb_iterator_value.type == jbvBinary) 
412                 {
413                     return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data);
414                 }
415                 else 
416                 {
417                     return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, &jsonb_iterator_value);
418                 }
419             }
420             break;
421         case WJB_VALUE:
422             /* should not be possible */
423         default:
424             elog(ERROR, "invalid JsonbIteratorNext rc: %d", jsonb_iterator_token);
425         }
426
427     }
428
429     if (JB_ROOT_IS_SCALAR(input_jsonb_a) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
430         return_jsonb_value->val.array.rawScalar = true;
431
432     PG_FREE_IF_COPY(input_jsonb_a, 0); 
433     PG_FREE_IF_COPY(input_jsonb_b, 1); 
434
435     PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value));
436 }
437
438 Datum jsonb_delete_path(PG_FUNCTION_ARGS);
439
440 PG_FUNCTION_INFO_V1(jsonb_delete_path);
441
442 /*
443  * Test
444  * jsonb, text[] -> jsonb
445  *
446  */
447 Datum 
448 jsonb_delete_path(PG_FUNCTION_ARGS)
449 {
450     /* pointers to incoming jsonb and text[] data */
451     Jsonb *input_jsonb_a = PG_GETARG_JSONB(0);
452     ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1);
453
454     /* pointer to return jsonb data */
455     Jsonb *return_jsonb = NULL;
456
457     return_jsonb = jsonbModifyPath(input_jsonb_a, input_array, NULL);
458
459     PG_FREE_IF_COPY(input_jsonb_a, 0); 
460     PG_FREE_IF_COPY(input_array, 1); 
461
462     PG_RETURN_JSONB(return_jsonb);
463 }
464
465 Datum jsonb_concat_jsonb(PG_FUNCTION_ARGS);
466
467 PG_FUNCTION_INFO_V1(jsonb_concat_jsonb);
468
469 /*
470  * Operator function to concatenate json from left operand where a match 
471  * is found in the right operand.
472  *
473  * jsonb, jsonb -> jsonb
474  *
475  */
476 Datum 
477 jsonb_concat_jsonb(PG_FUNCTION_ARGS)
478 {
479     /* incoming jsonb data */
480     Jsonb *input_jsonb_a = PG_GETARG_JSONB(0);
481     Jsonb *input_jsonb_b = PG_GETARG_JSONB(1);
482     
483     /* return jsonb value data to be converted to jsonb on return */
484     JsonbParseState *state = NULL;
485     JsonbValue *return_jsonb_value = NULL;
486
487     /* iterator for input_jsonb_b */
488     JsonbIterator *jsonb_iterator;
489     JsonbValue jsonb_iterator_value;
490     int32 jsonb_iterator_token;
491     int32 jsonb_root_open;
492     int32 jsonb_root_close;
493
494     int32 nest_level = 0;
495     bool first = true; 
496
497     /*
498      * check if either supplied jsonb is empty and return the other if so
499      */
500     if (JB_ROOT_COUNT(input_jsonb_a) == 0)
501         PG_RETURN_JSONB(input_jsonb_b);
502     else if (JB_ROOT_COUNT(input_jsonb_b) == 0)
503         PG_RETURN_JSONB(input_jsonb_a);
504
505     /* 
506      * rather than restrict concatenation to objects, allow any jsonb root
507      * but if one is an array use an array as the root container else
508      * default to object
509      */
510     if (JB_ROOT_IS_ARRAY(input_jsonb_a) || JB_ROOT_IS_ARRAY(input_jsonb_b))
511     {
512         jsonb_root_open = WJB_BEGIN_ARRAY;
513         jsonb_root_close = WJB_END_ARRAY; 
514     } else
515     {
516         jsonb_root_open = WJB_BEGIN_OBJECT;
517         jsonb_root_close = WJB_END_OBJECT; 
518     }
519
520     /*
521      * The following is essentially a cut 'n shut job; discarding the closing root 
522      * object token from the first jsonb value and the opening one from the second.
523      * Values from each are just blindly pushed onto the return value leaving
524      * deduplication down to lower level jsonb logic.
525      */
526
527     return_jsonb_value = pushJsonbValue(&state, jsonb_root_open, NULL);
528
529     jsonb_iterator = JsonbIteratorInit(&input_jsonb_a->root);
530
531     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE) 
532     {
533         if (jsonb_iterator_token == jsonb_root_open && (first || nest_level > 0)) 
534         {
535             nest_level++;
536             if (nest_level == 1)
537             { 
538                 first = false;
539                 continue;
540             }
541         }
542         else if (jsonb_iterator_token == jsonb_root_close && nest_level > 0) 
543         {
544             nest_level--;
545             if (nest_level == 0) 
546                 continue;
547         }
548
549         first = false;
550         return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
551     }
552
553     nest_level = 0;
554     first = true;
555     jsonb_iterator = JsonbIteratorInit(&input_jsonb_b->root);
556
557     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
558     {
559         if (jsonb_iterator_token == jsonb_root_open && (first || nest_level > 0)) 
560         {
561             nest_level++;
562             if (nest_level == 1) 
563             {
564                 first = false;
565                 continue;
566             }
567         }
568         else if (jsonb_iterator_token == jsonb_root_close && nest_level > 0) 
569         {
570             nest_level--;
571             if (nest_level == 0) 
572                 continue;
573         }
574
575         first = false;
576         return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
577     }
578
579     return_jsonb_value = pushJsonbValue(&state, jsonb_root_close, NULL);
580
581     PG_FREE_IF_COPY(input_jsonb_a, 0); 
582     PG_FREE_IF_COPY(input_jsonb_b, 1); 
583
584     PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value));
585 }
586
587 Datum jsonb_replace_jsonb(PG_FUNCTION_ARGS);
588
589 PG_FUNCTION_INFO_V1(jsonb_replace_jsonb);
590
591 /*
592  * Operator function to replace json in left operand where keys match 
593  * in the right operand.
594  *
595  * jsonb, jsonb -> jsonb
596  *
597  */
598 Datum 
599 jsonb_replace_jsonb(PG_FUNCTION_ARGS)
600 {
601     /* incoming jsonb data */
602     Jsonb *input_jsonb_a = PG_GETARG_JSONB(0);
603     Jsonb *input_jsonb_b = PG_GETARG_JSONB(1);
604     
605     /* return jsonb value data to be converted to jsonb on return */
606     JsonbParseState *state = NULL;
607     JsonbValue *return_jsonb_value = NULL;
608
609     /* lookup jsonb value data */
610     JsonbValue  jsonb_lookup_key;
611     JsonbValue *jsonb_lookup_value = NULL;
612     uint32 jsonb_lookup_flags;
613
614     /* iterator for input_jsonb_b */
615     JsonbIterator *jsonb_iterator;
616     JsonbValue jsonb_iterator_value;
617     int32 jsonb_iterator_token;
618
619     /*
620      * check if  supplied replacement jsonb is empty and return unchanged if so
621      */
622     if (JB_ROOT_COUNT(input_jsonb_b) == 0)
623         PG_RETURN_JSONB(input_jsonb_a);
624
625     if (JB_ROOT_IS_OBJECT(input_jsonb_a))
626         jsonb_lookup_flags = JB_FOBJECT;
627     else
628         jsonb_lookup_flags = JB_FOBJECT | JB_FARRAY;
629
630     jsonb_iterator = JsonbIteratorInit(&input_jsonb_a->root);
631     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, true)) != WJB_DONE) 
632     {
633         if ((jsonb_iterator_token == WJB_ELEM ) && (jsonb_iterator_value.type == jbvBinary))
634         {
635             return_jsonb_value = pushJsonbBinary(&state, jsonb_iterator_value.val.binary.data);
636         }
637         else
638         {
639             return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
640         }
641
642         Assert(jsonb_iterator_token != WJB_VALUE);
643
644         if ( jsonb_iterator_token == WJB_KEY )
645         {
646             jsonb_lookup_key.type = jbvString;
647             jsonb_lookup_key.val.string.val = jsonb_iterator_value.val.string.val;
648             jsonb_lookup_key.val.string.len = jsonb_iterator_value.val.string.len;
649
650             jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, true);
651             Assert(jsonb_iterator_token == WJB_VALUE);
652
653             jsonb_lookup_value = findJsonbValueFromContainer(&input_jsonb_b->root,
654                 jsonb_lookup_flags, &jsonb_lookup_key);
655
656             /* if there's nothing to replace push the original value */
657             if (jsonb_lookup_value == NULL) 
658             {
659                 jsonb_lookup_value = &jsonb_iterator_value;
660             }
661
662             /* if our value is nested binary data, iterate separately pushing each val */
663             if (jsonb_lookup_value->type == jbvBinary)
664             {
665                 return_jsonb_value = pushJsonbBinary(&state, jsonb_lookup_value->val.binary.data);
666             }
667             else 
668             {
669                 return_jsonb_value = pushJsonbValue(&state, WJB_VALUE, jsonb_lookup_value);
670             }
671         }
672     }
673
674     if (JB_ROOT_IS_SCALAR(input_jsonb_a) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
675         return_jsonb_value->val.array.rawScalar = true;
676
677     PG_FREE_IF_COPY(input_jsonb_a, 0); 
678     PG_FREE_IF_COPY(input_jsonb_b, 1); 
679
680     PG_RETURN_JSONB(JsonbValueToJsonb(return_jsonb_value));
681 }
682
683 Datum jsonb_replace_path(PG_FUNCTION_ARGS);
684
685 PG_FUNCTION_INFO_V1(jsonb_replace_path);
686
687 /*
688  * Test
689  * jsonb, text[], jsonb -> jsonb
690  *
691  */
692 Datum 
693 jsonb_replace_path(PG_FUNCTION_ARGS)
694 {
695     /* pointers to incoming jsonb and text[] data */
696     Jsonb *input_jsonb_a = PG_GETARG_JSONB(0);
697     ArrayType *input_array = PG_GETARG_ARRAYTYPE_P(1);
698     Jsonb *input_jsonb_b = PG_GETARG_JSONB(2);
699
700     /* pointer to return jsonb data */
701     Jsonb *return_jsonb = NULL;
702
703     return_jsonb = jsonbModifyPath(input_jsonb_a, input_array, input_jsonb_b);
704
705     PG_FREE_IF_COPY(input_jsonb_a, 0); 
706     PG_FREE_IF_COPY(input_array, 1); 
707     PG_FREE_IF_COPY(input_jsonb_b, 2); 
708
709     PG_RETURN_JSONB(return_jsonb);
710 }