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