Add jsonb_insert
authorTeodor Sigaev <teodor@sigaev.ru>
Wed, 6 Apr 2016 16:20:17 +0000 (19:20 +0300)
committerTeodor Sigaev <teodor@sigaev.ru>
Wed, 6 Apr 2016 16:25:00 +0000 (19:25 +0300)
It inserts a new value into an jsonb array at arbitrary position or
a new key to jsonb object.

Author: Dmitry Dolgov
Reviewers: Petr Jelinek, Vitaly Burovoy, Andrew Dunstan

doc/src/sgml/func.sgml
src/backend/catalog/system_views.sql
src/backend/utils/adt/jsonfuncs.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/jsonb.h
src/test/regress/expected/jsonb.out
src/test/regress/sql/jsonb.sql

index f60d5784fddd0f476d533d81b21311c8c3e997c1..2ce89bb030fb399342d280603935278af6f5c5d2 100644 (file)
@@ -10902,6 +10902,9 @@ table2-mapping
   <indexterm>
    <primary>jsonb_set</primary>
   </indexterm>
+  <indexterm>
+   <primary>jsonb_insert</primary>
+  </indexterm>
   <indexterm>
    <primary>jsonb_pretty</primary>
   </indexterm>
@@ -11183,6 +11186,39 @@ table2-mapping
          </para><para><literal>[{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]</literal>
         </para></entry>
        </row>
+      <row>
+       <entry>
+           <para><literal>
+           jsonb_insert(target jsonb, path text[], new_value jsonb, <optional><parameter>insert_after</parameter> <type>boolean</type></optional>)
+           </literal></para>
+       </entry>
+       <entry><para><type>jsonb</type></para></entry>
+       <entry>
+         Returns <replaceable>target</replaceable> with
+         <replaceable>new_value</replaceable> inserted. If
+         <replaceable>target</replaceable> section designated by
+         <replaceable>path</replaceable> is in a JSONB array,
+         <replaceable>new_value</replaceable> will be inserted before target or
+         after if <replaceable>insert_after</replaceable> is true (default is
+         <literal>false</>). If <replaceable>target</replaceable> section
+         designated by <replaceable>path</replaceable> is in JSONB object,
+         <replaceable>new_value</replaceable> will be inserted only if
+         <replaceable>target</replaceable> does not exist. As with the path
+         orientated operators, negative integers that appear in
+         <replaceable>path</replaceable> count from the end of JSON arrays.
+       </entry>
+       <entry>
+           <para><literal>
+               jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"')
+           </literal></para>
+           <para><literal>
+               jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true)
+           </literal></para>
+       </entry>
+       <entry><para><literal>{"a": [0, "new_value", 1, 2]}</literal>
+         </para><para><literal>{"a": [0, 1, "new_value", 2]}</literal>
+        </para></entry>
+       </row>
       <row>
        <entry><para><literal>jsonb_pretty(from_json jsonb)</literal>
          </para></entry>
@@ -11235,10 +11271,11 @@ table2-mapping
   <note>
     <para>
       All the items of the <literal>path</> parameter of <literal>jsonb_set</>
-      must be present in the <literal>target</>, unless
-      <literal>create_missing</> is true, in which case all but the last item
-      must be present. If these conditions are not met the <literal>target</>
-      is returned unchanged.
+      as well as <literal>jsonb_insert</> except the last item must be present
+      in the <literal>target</>. If <literal>create_missing</> is false, all
+      items of the <literal>path</> parameter of <literal>jsonb_set</> must be
+      present. If these conditions are not met the <literal>target</> is
+      returned unchanged.
     </para>
     <para>
       If the last path item is an object key, it will be created if it
index 2bd40a160e0aefed390f137edc83ae9ca76c4252..cef90c59be1a86636284c9c11c9fcaa6d500b697 100644 (file)
@@ -997,3 +997,11 @@ RETURNS text[]
 LANGUAGE INTERNAL
 STRICT IMMUTABLE
 AS 'parse_ident';
+
+CREATE OR REPLACE FUNCTION
+  jsonb_insert(jsonb_in jsonb, path text[] , replacement jsonb,
+            insert_after boolean DEFAULT false)
+RETURNS jsonb
+LANGUAGE INTERNAL
+STRICT IMMUTABLE
+AS 'jsonb_insert';
index 97e0e8e0596fe261598c8429e543e8229c362edf..fb149dcd47799dc8f966d8000986964e3abab27c 100644 (file)
 #include "utils/memutils.h"
 #include "utils/typcache.h"
 
+/* Operations available for setPath */
+#define JB_PATH_NOOP                                   0x0000
+#define JB_PATH_CREATE                                 0x0001
+#define JB_PATH_DELETE                                 0x0002
+#define JB_PATH_INSERT_BEFORE                  0x0004
+#define JB_PATH_INSERT_AFTER                   0x0008
+#define JB_PATH_CREATE_OR_INSERT \
+       (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE)
+
 /* semantic action functions for json_object_keys */
 static void okeys_object_field_start(void *state, char *fname, bool isnull);
 static void okeys_array_start(void *state);
@@ -130,14 +139,14 @@ static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
 static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
                bool *path_nulls, int path_len,
                JsonbParseState **st, int level, Jsonb *newval,
-               bool create);
+               int op_type);
 static void setPathObject(JsonbIterator **it, Datum *path_elems,
                          bool *path_nulls, int path_len, JsonbParseState **st,
                          int level,
-                         Jsonb *newval, uint32 npairs, bool create);
+                         Jsonb *newval, uint32 npairs, int op_type);
 static void setPathArray(JsonbIterator **it, Datum *path_elems,
                         bool *path_nulls, int path_len, JsonbParseState **st,
-                        int level, Jsonb *newval, uint32 nelems, bool create);
+                        int level, Jsonb *newval, uint32 nelems, int op_type);
 static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
 
 /* state for json_object_keys */
@@ -3544,7 +3553,7 @@ jsonb_set(PG_FUNCTION_ARGS)
        it = JsonbIteratorInit(&in->root);
 
        res = setPath(&it, path_elems, path_nulls, path_len, &st,
-                                 0, newval, create);
+                                 0, newval, create ? JB_PATH_CREATE : JB_PATH_NOOP);
 
        Assert(res != NULL);
 
@@ -3588,7 +3597,52 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
 
        it = JsonbIteratorInit(&in->root);
 
-       res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false);
+       res = setPath(&it, path_elems, path_nulls, path_len, &st,
+                                 0, NULL, JB_PATH_DELETE);
+
+       Assert(res != NULL);
+
+       PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
+/*
+ * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
+ *
+ */
+Datum
+jsonb_insert(PG_FUNCTION_ARGS)
+{
+       Jsonb      *in = PG_GETARG_JSONB(0);
+       ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
+       Jsonb      *newval = PG_GETARG_JSONB(2);
+       bool            after = PG_GETARG_BOOL(3);
+       JsonbValue *res = NULL;
+       Datum      *path_elems;
+       bool       *path_nulls;
+       int                     path_len;
+       JsonbIterator *it;
+       JsonbParseState *st = NULL;
+
+       if (ARR_NDIM(path) > 1)
+               ereport(ERROR,
+                               (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+                                errmsg("wrong number of array subscripts")));
+
+       if (JB_ROOT_IS_SCALAR(in))
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                errmsg("cannot set path in scalar")));
+
+       deconstruct_array(path, TEXTOID, -1, false, 'i',
+                                         &path_elems, &path_nulls, &path_len);
+
+       if (path_len == 0)
+               PG_RETURN_JSONB(in);
+
+       it = JsonbIteratorInit(&in->root);
+
+       res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, newval,
+                                 after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE);
 
        Assert(res != NULL);
 
@@ -3707,18 +3761,23 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
 }
 
 /*
- * Do most of the heavy work for jsonb_set
+ * Do most of the heavy work for jsonb_set/jsonb_insert
+ *
+ * If JB_PATH_DELETE bit is set in op_type, the element is to be removed.
  *
- * If newval is null, the element is to be removed.
+ * If any bit mentioned in JB_PATH_CREATE_OR_INSERT is set in op_type,
+ * we create the new value if the key or array index does not exist.
  *
- * If create is true, we create the new value if the key or array index
- * does not exist. All path elements before the last must already exist
- * whether or not create is true, or nothing is done.
+ * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type
+ * behave as JB_PATH_CREATE if new value is inserted in JsonbObject.
+ *
+ * All path elements before the last must already exist
+ * whatever bits in op_type are set, or nothing is done.
  */
 static JsonbValue *
 setPath(JsonbIterator **it, Datum *path_elems,
                bool *path_nulls, int path_len,
-               JsonbParseState **st, int level, Jsonb *newval, bool create)
+               JsonbParseState **st, int level, Jsonb *newval, int op_type)
 {
        JsonbValue      v;
        JsonbIteratorToken r;
@@ -3739,7 +3798,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
                case WJB_BEGIN_ARRAY:
                        (void) pushJsonbValue(st, r, NULL);
                        setPathArray(it, path_elems, path_nulls, path_len, st, level,
-                                                newval, v.val.array.nElems, create);
+                                                newval, v.val.array.nElems, op_type);
                        r = JsonbIteratorNext(it, &v, false);
                        Assert(r == WJB_END_ARRAY);
                        res = pushJsonbValue(st, r, NULL);
@@ -3747,7 +3806,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
                case WJB_BEGIN_OBJECT:
                        (void) pushJsonbValue(st, r, NULL);
                        setPathObject(it, path_elems, path_nulls, path_len, st, level,
-                                                 newval, v.val.object.nPairs, create);
+                                                 newval, v.val.object.nPairs, op_type);
                        r = JsonbIteratorNext(it, &v, true);
                        Assert(r == WJB_END_OBJECT);
                        res = pushJsonbValue(st, r, NULL);
@@ -3771,7 +3830,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
 static void
 setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                          int path_len, JsonbParseState **st, int level,
-                         Jsonb *newval, uint32 npairs, bool create)
+                         Jsonb *newval, uint32 npairs, int op_type)
 {
        JsonbValue      v;
        int                     i;
@@ -3782,7 +3841,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                done = true;
 
        /* empty object is a special case for create */
-       if ((npairs == 0) && create && (level == path_len - 1))
+       if ((npairs == 0) && (op_type & JB_PATH_CREATE_OR_INSERT) &&
+               (level == path_len - 1))
        {
                JsonbValue      newkey;
 
@@ -3807,8 +3867,19 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                {
                        if (level == path_len - 1)
                        {
-                               r = JsonbIteratorNext(it, &v, true);    /* skip */
-                               if (newval != NULL)
+                               /*
+                                * called from jsonb_insert(), it forbids redefining
+                                * an existsing value
+                                */
+                               if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER))
+                                       ereport(ERROR,
+                                                       (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+                                                        errmsg("cannot replace existing key"),
+                                                        errhint("Try using the function jsonb_set "
+                                                                        "to replace key value.")));
+
+                               r = JsonbIteratorNext(it, &v, true); /* skip value */
+                               if (!(op_type & JB_PATH_DELETE))
                                {
                                        (void) pushJsonbValue(st, WJB_KEY, &k);
                                        addJsonbToParseState(st, newval);
@@ -3819,12 +3890,13 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                        {
                                (void) pushJsonbValue(st, r, &k);
                                setPath(it, path_elems, path_nulls, path_len,
-                                               st, level + 1, newval, create);
+                                               st, level + 1, newval, op_type);
                        }
                }
                else
                {
-                       if (create && !done && level == path_len - 1 && i == npairs - 1)
+                       if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
+                               level == path_len - 1 && i == npairs - 1)
                        {
                                JsonbValue      newkey;
 
@@ -3865,7 +3937,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
 static void
 setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                         int path_len, JsonbParseState **st, int level,
-                        Jsonb *newval, uint32 nelems, bool create)
+                        Jsonb *newval, uint32 nelems, int op_type)
 {
        JsonbValue      v;
        int                     idx,
@@ -3909,7 +3981,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
         * what the idx value is
         */
 
-       if ((idx == INT_MIN || nelems == 0) && create && (level == path_len - 1))
+       if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) &&
+               (op_type & JB_PATH_CREATE_OR_INSERT))
        {
                Assert(newval != NULL);
                addJsonbToParseState(st, newval);
@@ -3926,14 +3999,26 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                        if (level == path_len - 1)
                        {
                                r = JsonbIteratorNext(it, &v, true);    /* skip */
-                               if (newval != NULL)
+
+                               if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
+                                       addJsonbToParseState(st, newval);
+
+                               /*
+                                * We should keep current value only in case of
+                                * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER
+                                * because otherwise it should be deleted or replaced
+                                */
+                               if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE))
+                                       (void) pushJsonbValue(st, r, &v);
+
+                               if (op_type & JB_PATH_INSERT_AFTER)
                                        addJsonbToParseState(st, newval);
 
                                done = true;
                        }
                        else
                                (void) setPath(it, path_elems, path_nulls, path_len,
-                                                          st, level + 1, newval, create);
+                                                          st, level + 1, newval, op_type);
                }
                else
                {
@@ -3958,7 +4043,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
                                }
                        }
 
-                       if (create && !done && level == path_len - 1 && i == nelems - 1)
+                       if (op_type & JB_PATH_CREATE_OR_INSERT && !done &&
+                               level == path_len - 1 && i == nelems - 1)
                        {
                                addJsonbToParseState(st, newval);
                        }
index 440bfba7f9129a041fe1eafd1e684fcdfd7b69b5..7ba17364fdd5f3e5db06d09932c973bd46f85d70 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201604053
+#define CATALOG_VERSION_NO     201604061
 
 #endif
index 6eab6c7c33419467f01c80e3b5be2be6fc5d02db..36635f81305008a49dfefd2b7c5664dd9c54b4a0 100644 (file)
@@ -4869,6 +4869,8 @@ DATA(insert OID = 3305 (  jsonb_set    PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4
 DESCR("Set part of a jsonb");
 DATA(insert OID = 3306 (  jsonb_pretty    PGNSP PGUID 12 1 0 0 0 f f f f t f i s 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ ));
 DESCR("Indented text from jsonb");
+DATA(insert OID = 3579 (  jsonb_insert    PGNSP PGUID 12 1 0 0 0 f f f f t f i s 4 0 3802 "3802 1009 3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_insert _null_ _null_ _null_ ));
+DESCR("Insert value into a jsonb");
 /* txid */
 DATA(insert OID = 2939 (  txid_snapshot_in                     PGNSP PGUID 12 1  0 0 0 f f f f t f i s 1 0 2970 "2275" _null_ _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ ));
 DESCR("I/O");
index 5f49d8dfea8662b74c21f074e1eae81dafc2c693..5d8e4a9f88c77969f95ab62aae9b9c66f63fd1ec 100644 (file)
@@ -408,6 +408,9 @@ extern Datum jsonb_delete_path(PG_FUNCTION_ARGS);
 /* replacement */
 extern Datum jsonb_set(PG_FUNCTION_ARGS);
 
+/* insert after or before (for arrays) */
+extern Datum jsonb_insert(PG_FUNCTION_ARGS);
+
 /* Support functions */
 extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
 extern uint32 getJsonbLength(const JsonbContainer *jc, int index);
index 497b0d93ef0ed2fe70ca919e89957f5433a2a907..a6d25defb0d82e3e151675e7a6ccaa72b630a210 100644 (file)
@@ -3312,3 +3312,132 @@ select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'
 ERROR:  path element at position 3 is not an integer: "non_integer"
 select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"');
 ERROR:  path element at position 3 is null
+-- jsonb_insert
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": [0, "new_value", 1, 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true);
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, "new_value", 2]}
+(1 row)
+
+select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"');
+                        jsonb_insert                        
+------------------------------------------------------------
+ {"a": {"b": {"c": [0, 1, "new_value", "test1", "test2"]}}}
+(1 row)
+
+select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', true);
+                        jsonb_insert                        
+------------------------------------------------------------
+ {"a": {"b": {"c": [0, 1, "test1", "new_value", "test2"]}}}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}');
+           jsonb_insert           
+----------------------------------
+ {"a": [0, {"b": "value"}, 1, 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]');
+              jsonb_insert              
+----------------------------------------
+ {"a": [0, ["value1", "value2"], 1, 2]}
+(1 row)
+
+-- edge cases
+select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": ["new_value", 0, 1, 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"', true);
+         jsonb_insert          
+-------------------------------
+ {"a": [0, "new_value", 1, 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, "new_value", 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"', true);
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, 2, "new_value"]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, "new_value", 2]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"', true);
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, 2, "new_value"]}
+(1 row)
+
+select jsonb_insert('[]', '{1}', '"new_value"');
+ jsonb_insert  
+---------------
+ ["new_value"]
+(1 row)
+
+select jsonb_insert('[]', '{1}', '"new_value"', true);
+ jsonb_insert  
+---------------
+ ["new_value"]
+(1 row)
+
+select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"');
+     jsonb_insert     
+----------------------
+ {"a": ["new_value"]}
+(1 row)
+
+select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"', true);
+     jsonb_insert     
+----------------------
+ {"a": ["new_value"]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, 10}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": [0, 1, 2, "new_value"]}
+(1 row)
+
+select jsonb_insert('{"a": [0,1,2]}', '{a, -10}', '"new_value"');
+         jsonb_insert          
+-------------------------------
+ {"a": ["new_value", 0, 1, 2]}
+(1 row)
+
+-- jsonb_insert should be able to insert new value for objects, but not to replace
+select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"');
+              jsonb_insert               
+-----------------------------------------
+ {"a": {"b": "value", "c": "new_value"}}
+(1 row)
+
+select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
+              jsonb_insert               
+-----------------------------------------
+ {"a": {"b": "value", "c": "new_value"}}
+(1 row)
+
+select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
+ERROR:  cannot replace existing key
+HINT:  Try using the function jsonb_set to replace key value.
+select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+ERROR:  cannot replace existing key
+HINT:  Try using the function jsonb_set to replace key value.
index c6716841344c3744e1ef0908355305ed36b993c8..b84bd70a2992904f1520d897e5d7ae2ab8030d75 100644 (file)
@@ -837,3 +837,33 @@ select jsonb_set('[]','{-99}','{"foo":123}');
 select jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"');
 select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"');
 select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"');
+
+
+-- jsonb_insert
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"');
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"', true);
+select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"');
+select jsonb_insert('{"a": {"b": {"c": [0, 1, "test1", "test2"]}}}', '{a, b, c, 2}', '"new_value"', true);
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '{"b": "value"}');
+select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '["value1", "value2"]');
+
+-- edge cases
+select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"');
+select jsonb_insert('{"a": [0,1,2]}', '{a, 0}', '"new_value"', true);
+select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"');
+select jsonb_insert('{"a": [0,1,2]}', '{a, 2}', '"new_value"', true);
+select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"');
+select jsonb_insert('{"a": [0,1,2]}', '{a, -1}', '"new_value"', true);
+select jsonb_insert('[]', '{1}', '"new_value"');
+select jsonb_insert('[]', '{1}', '"new_value"', true);
+select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"');
+select jsonb_insert('{"a": []}', '{a, 1}', '"new_value"', true);
+select jsonb_insert('{"a": [0,1,2]}', '{a, 10}', '"new_value"');
+select jsonb_insert('{"a": [0,1,2]}', '{a, -10}', '"new_value"');
+
+-- jsonb_insert should be able to insert new value for objects, but not to replace
+select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"');
+select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
+
+select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
+select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);