]> git.8kb.co.uk Git - postgresql/pg_jsonb_opx/blobdiff - jsonb_utilsx.c
Raise an error if un implemented append function called.
[postgresql/pg_jsonb_opx] / jsonb_utilsx.c
index c000609a86747dbc380aa040fdd3d9e2a4bbf68b..213424386a653430548c939714ecfda470fb28af 100755 (executable)
 #include "jsonb_opx.h"
 
 JsonbValue * 
-pushJsonbBinary(JsonbParseState *pstate, JsonbContainer *jsonb_container)
+pushJsonbBinary(JsonbParseState **pstate, JsonbContainer *jsonb_container)
 {
-    JsonbIterator *jsonb_iterator;
-    JsonbValue jsonb_iterator_value;
-    int32 jsonb_iterator_token;
-    JsonbValue *return_jsonb_value = NULL;
+    JsonbIterator          *jsonb_iterator;
+    JsonbValue              jsonb_iterator_value;
+    int32                   jsonb_iterator_token;
+    JsonbValue             *return_jsonb_value = NULL;
 
     jsonb_iterator = JsonbIteratorInit((void *)jsonb_container);
     while ((jsonb_iterator_token = JsonbIteratorNext(&jsonb_iterator, &jsonb_iterator_value, false)) != WJB_DONE)
     {
-        return_jsonb_value = pushJsonbValue(&pstate, jsonb_iterator_token, &jsonb_iterator_value);
+        return_jsonb_value = pushJsonbValueBlind(pstate, jsonb_iterator_token, &jsonb_iterator_value);
     }
     return return_jsonb_value;
 }
 
+JsonbValue *
+pushJsonbValueBlind(JsonbParseState **pstate, JsonbIteratorToken jsonb_iterator_token, JsonbValue *jsonb_iterator_value)
+{
+    JsonbValue             *return_jsonb_value = NULL;
+
+    if ((jsonb_iterator_token == WJB_KEY) ||
+            (jsonb_iterator_token == WJB_VALUE) ||
+            (jsonb_iterator_token == WJB_ELEM))
+            {
+            return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, jsonb_iterator_value);
+            }
+        else
+            return_jsonb_value = pushJsonbValue(pstate, jsonb_iterator_token, NULL);
+
+    return return_jsonb_value;
+}
+
 Jsonb * 
 jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
 {
     /* pointers to return jsonb value data and state to be converted to jsonb on return */
-    JsonbParseState *state = NULL;
-    JsonbValue *return_jsonb_value = NULL;
+    JsonbParseState        *state = NULL;
+    JsonbValue             *return_jsonb_value = NULL;
 
     /* pointer to iterator for input jsonb */
-    JsonbIterator *jsonb_iterator;
-    JsonbValue jsonb_iterator_value;
-    int32 jsonb_iterator_token;
-    int32 jsonb_last_token = 0;
-
-    JsonbIterator *jsonb_replacement_iterator;
-    JsonbValue jsonb_replacement_iterator_value;
-    int32 jsonb_replacement_iterator_token;
+    JsonbIterator          *jsonb_iterator;
+    JsonbValue              jsonb_iterator_value;
+    int32                   jsonb_iterator_token;
+
+    JsonbIterator          *jsonb_replacement_iterator;
+    JsonbValue              jsonb_replacement_iterator_value;
+    int32                   jsonb_replacement_iterator_token;
     
     /* 
      * array element variables for use during deconstruction 
      * count is the depth we will be looking from the first matching key
      */
-    Datum *datums;
-    bool *nulls;
-    int32 count; 
+    Datum                  *datums;
+    bool                   *nulls;
+    int32                   count; 
 
     /* the current key we are looking for, starting with the first key */
-    text *key_on = NULL;
-    //text *key_on;
-    int32 index_on = 0; 
-    int32 nest_level = 0;
-    int32 array_level = 0;
-    int32 skip_level = 0;
-    int32 push_nest_level = 0;
-    bool push = true;
+    text                   *key_on = NULL;
+    int32                   index_on = 0; 
+    int32                   nest_level = 0;
+    int32                   array_level = 0;
+    int32                   skip_level = 0;
+    int32                   push_nest_level = 0;
+    bool                    push = true;
 
     /* assert input_array is a text array type */
     Assert(ARR_ELEMTYPE(array_path) == TEXTOID);
@@ -104,10 +119,10 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
         {
             case WJB_BEGIN_ARRAY:
                 array_level++;
-            break;
+                break;
             case WJB_BEGIN_OBJECT:
                nest_level++;
-            break;
+                break;
             case WJB_ELEM:
             case WJB_KEY:
                 /*
@@ -118,8 +133,7 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
                 if (skip_level == 0 && ((jsonb_iterator_token == WJB_KEY && nest_level-1 == index_on && array_level == 0) ||
                         (jsonb_iterator_token == WJB_ELEM && nest_level == 0 && array_level == 1)))
                 {
-                    if ((jsonb_iterator_value.type == jbvNull && key_on == NULL) ||
-                        (key_on != NULL && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(key_on)) &&
+                    if ((!nulls[index_on] && (jsonb_iterator_value.val.string.len == VARSIZE_ANY_EXHDR(key_on)) &&
                         (memcmp(jsonb_iterator_value.val.string.val, VARDATA_ANY(key_on), jsonb_iterator_value.val.string.len) == 0)))
                     {   
                         /* if we have not yet reached the last index in the array / key chain move on and check the next */
@@ -128,8 +142,6 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
                             index_on++;
                             if (!nulls[index_on])
                                 key_on = DatumGetTextP(datums[index_on]);
-                            else
-                                key_on = NULL;
                         }
                         /* if we have reached the last index, the we can modify this level */ 
                         else 
@@ -138,36 +150,45 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
                             if (jsonb_b != NULL) {
                                 jsonb_replacement_iterator = JsonbIteratorInit(&jsonb_b->root);
 
-                                while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
+                                /* if the replacement value is a scalar then it will replace the current element or value */
+                                if (JB_ROOT_IS_SCALAR(jsonb_b)) 
                                 {
-                                    if (((jsonb_last_token == jsonb_replacement_iterator_token) && 
-                                            (jsonb_last_token != WJB_VALUE)) || 
-                                        ((jsonb_last_token == WJB_VALUE) && 
-                                            ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || 
-                                            (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)))) 
+                                    jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
+                                    jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false);
+                                    
+                                    if (jsonb_iterator_token == WJB_ELEM) 
                                     {
-                                        push_nest_level++;
+                                        return_jsonb_value = pushJsonbValue(&state, WJB_ELEM, &jsonb_replacement_iterator_value);
                                     }
-
-                                    if ((jsonb_replacement_iterator_token == WJB_KEY) || 
-                                        (jsonb_replacement_iterator_token == WJB_VALUE) || 
-                                        (jsonb_replacement_iterator_token == WJB_ELEM) || 
-                                        (push_nest_level != 1))
+                                    else
                                     {
-                                        return_jsonb_value = pushJsonbValue(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
+                                        return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
+                                        return_jsonb_value = pushJsonbValue(&state, WJB_VALUE, &jsonb_replacement_iterator_value);
                                     }
-
-                                    if (((jsonb_last_token == WJB_BEGIN_ARRAY) || 
-                                        (jsonb_last_token == WJB_VALUE)) && 
-                                        (jsonb_replacement_iterator_token == WJB_END_ARRAY))
-                                    {
-                                        push_nest_level--;
-                                    }
-                                    else if (((jsonb_last_token == WJB_BEGIN_OBJECT) || 
-                                        (jsonb_last_token == WJB_VALUE)) && 
-                                        (jsonb_replacement_iterator_token == WJB_END_OBJECT))
+                                }
+                                /* otherwise assume this is the replacement for the whole element or key-value pair */
+                                else {
+                                    while ((jsonb_replacement_iterator_token = JsonbIteratorNext(&jsonb_replacement_iterator, &jsonb_replacement_iterator_value, false)) != WJB_DONE)
                                     {
-                                        push_nest_level--;
+                                        if (push_nest_level == 0 && jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)
+                                        {
+                                            return_jsonb_value = pushJsonbValue(&state, WJB_KEY, &jsonb_iterator_value);
+                                        }
+
+                                        if ((jsonb_replacement_iterator_token == WJB_BEGIN_OBJECT) || (jsonb_replacement_iterator_token == WJB_BEGIN_ARRAY)) 
+                                            push_nest_level++;
+
+                                        if ((push_nest_level > 1) ||
+                                            (jsonb_iterator_token == WJB_ELEM && jsonb_replacement_iterator_token != WJB_BEGIN_ARRAY && jsonb_replacement_iterator_token != WJB_END_ARRAY) ||
+                                            (jsonb_iterator_token == WJB_KEY && jsonb_replacement_iterator_token != WJB_BEGIN_OBJECT && jsonb_replacement_iterator_token != WJB_END_OBJECT)) 
+                                        {
+                                            return_jsonb_value = pushJsonbValueBlind(&state, jsonb_replacement_iterator_token, &jsonb_replacement_iterator_value);
+                                        }
+
+                                        if ((jsonb_replacement_iterator_token == WJB_END_OBJECT || jsonb_replacement_iterator_token == WJB_END_ARRAY)) 
+                                            push_nest_level--;
+
+                                        Assert(push_nest_level >= 0);
                                     }
                                 }
                             }
@@ -178,12 +199,12 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
                         }
                     } 
                 }
+            /* switch end */
         }
 
         if (push && (skip_level == 0 || nest_level < skip_level)) 
         {
-            return_jsonb_value = pushJsonbValue(&state, jsonb_iterator_token, &jsonb_iterator_value);
-            jsonb_last_token = jsonb_iterator_token;
+            return_jsonb_value = pushJsonbValueBlind(&state, jsonb_iterator_token, &jsonb_iterator_value);
         }
 
         switch (jsonb_iterator_token) 
@@ -192,17 +213,27 @@ jsonbModifyPath(Jsonb *jsonb_a, ArrayType *array_path, Jsonb *jsonb_b)
                 nest_level--;
                 if (skip_level == nest_level && array_level == 0)
                     skip_level = 0;
-            break;
+                break;
             case WJB_END_ARRAY:
                 array_level--;
                 if (skip_level == nest_level && array_level == 0)
                     skip_level = 0;
-            break;
+                break;
             case WJB_VALUE:
                 if (skip_level == nest_level)
                     skip_level = 0;
+            /* switch end */
         }
     }
 
+    if (return_jsonb_value->type == jbvArray)
+    {
+       if (return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 0)
+               return_jsonb_value->val.array.rawScalar = false;
+       else if (JB_ROOT_IS_SCALAR(jsonb_a) && !return_jsonb_value->val.array.rawScalar && return_jsonb_value->val.array.nElems == 1)
+               return_jsonb_value->val.array.rawScalar = true;
+    }
+
     return JsonbValueToJsonb(return_jsonb_value);
+    
 }