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