3333#include "utils/memutils.h"
3434#include "utils/typcache.h"
3535
36+ /* Operations available for setPath */
37+ #define JB_PATH_NOOP 0x0000
38+ #define JB_PATH_CREATE 0x0001
39+ #define JB_PATH_DELETE 0x0002
40+ #define JB_PATH_INSERT_BEFORE 0x0004
41+ #define JB_PATH_INSERT_AFTER 0x0008
42+ #define JB_PATH_CREATE_OR_INSERT \
43+ (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER | JB_PATH_CREATE)
44+
3645/* semantic action functions for json_object_keys */
3746static void okeys_object_field_start (void * state , char * fname , bool isnull );
3847static void okeys_array_start (void * state );
@@ -130,14 +139,14 @@ static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
130139static JsonbValue * setPath (JsonbIterator * * it , Datum * path_elems ,
131140 bool * path_nulls , int path_len ,
132141 JsonbParseState * * st , int level , Jsonb * newval ,
133- bool create );
142+ int op_type );
134143static void setPathObject (JsonbIterator * * it , Datum * path_elems ,
135144 bool * path_nulls , int path_len , JsonbParseState * * st ,
136145 int level ,
137- Jsonb * newval , uint32 npairs , bool create );
146+ Jsonb * newval , uint32 npairs , int op_type );
138147static void setPathArray (JsonbIterator * * it , Datum * path_elems ,
139148 bool * path_nulls , int path_len , JsonbParseState * * st ,
140- int level , Jsonb * newval , uint32 nelems , bool create );
149+ int level , Jsonb * newval , uint32 nelems , int op_type );
141150static void addJsonbToParseState (JsonbParseState * * jbps , Jsonb * jb );
142151
143152/* state for json_object_keys */
@@ -3544,7 +3553,7 @@ jsonb_set(PG_FUNCTION_ARGS)
35443553 it = JsonbIteratorInit (& in -> root );
35453554
35463555 res = setPath (& it , path_elems , path_nulls , path_len , & st ,
3547- 0 , newval , create );
3556+ 0 , newval , create ? JB_PATH_CREATE : JB_PATH_NOOP );
35483557
35493558 Assert (res != NULL );
35503559
@@ -3588,7 +3597,52 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
35883597
35893598 it = JsonbIteratorInit (& in -> root );
35903599
3591- res = setPath (& it , path_elems , path_nulls , path_len , & st , 0 , NULL , false);
3600+ res = setPath (& it , path_elems , path_nulls , path_len , & st ,
3601+ 0 , NULL , JB_PATH_DELETE );
3602+
3603+ Assert (res != NULL );
3604+
3605+ PG_RETURN_JSONB (JsonbValueToJsonb (res ));
3606+ }
3607+
3608+ /*
3609+ * SQL function jsonb_insert(jsonb, text[], jsonb, boolean)
3610+ *
3611+ */
3612+ Datum
3613+ jsonb_insert (PG_FUNCTION_ARGS )
3614+ {
3615+ Jsonb * in = PG_GETARG_JSONB (0 );
3616+ ArrayType * path = PG_GETARG_ARRAYTYPE_P (1 );
3617+ Jsonb * newval = PG_GETARG_JSONB (2 );
3618+ bool after = PG_GETARG_BOOL (3 );
3619+ JsonbValue * res = NULL ;
3620+ Datum * path_elems ;
3621+ bool * path_nulls ;
3622+ int path_len ;
3623+ JsonbIterator * it ;
3624+ JsonbParseState * st = NULL ;
3625+
3626+ if (ARR_NDIM (path ) > 1 )
3627+ ereport (ERROR ,
3628+ (errcode (ERRCODE_ARRAY_SUBSCRIPT_ERROR ),
3629+ errmsg ("wrong number of array subscripts" )));
3630+
3631+ if (JB_ROOT_IS_SCALAR (in ))
3632+ ereport (ERROR ,
3633+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3634+ errmsg ("cannot set path in scalar" )));
3635+
3636+ deconstruct_array (path , TEXTOID , -1 , false, 'i' ,
3637+ & path_elems , & path_nulls , & path_len );
3638+
3639+ if (path_len == 0 )
3640+ PG_RETURN_JSONB (in );
3641+
3642+ it = JsonbIteratorInit (& in -> root );
3643+
3644+ res = setPath (& it , path_elems , path_nulls , path_len , & st , 0 , newval ,
3645+ after ? JB_PATH_INSERT_AFTER : JB_PATH_INSERT_BEFORE );
35923646
35933647 Assert (res != NULL );
35943648
@@ -3707,18 +3761,23 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
37073761}
37083762
37093763/*
3710- * Do most of the heavy work for jsonb_set
3764+ * Do most of the heavy work for jsonb_set/jsonb_insert
3765+ *
3766+ * If JB_PATH_DELETE bit is set in op_type, the element is to be removed.
37113767 *
3712- * If newval is null, the element is to be removed.
3768+ * If any bit mentioned in JB_PATH_CREATE_OR_INSERT is set in op_type,
3769+ * we create the new value if the key or array index does not exist.
37133770 *
3714- * If create is true, we create the new value if the key or array index
3715- * does not exist. All path elements before the last must already exist
3716- * whether or not create is true, or nothing is done.
3771+ * Bits JB_PATH_INSERT_BEFORE and JB_PATH_INSERT_AFTER in op_type
3772+ * behave as JB_PATH_CREATE if new value is inserted in JsonbObject.
3773+ *
3774+ * All path elements before the last must already exist
3775+ * whatever bits in op_type are set, or nothing is done.
37173776 */
37183777static JsonbValue *
37193778setPath (JsonbIterator * * it , Datum * path_elems ,
37203779 bool * path_nulls , int path_len ,
3721- JsonbParseState * * st , int level , Jsonb * newval , bool create )
3780+ JsonbParseState * * st , int level , Jsonb * newval , int op_type )
37223781{
37233782 JsonbValue v ;
37243783 JsonbIteratorToken r ;
@@ -3739,15 +3798,15 @@ setPath(JsonbIterator **it, Datum *path_elems,
37393798 case WJB_BEGIN_ARRAY :
37403799 (void ) pushJsonbValue (st , r , NULL );
37413800 setPathArray (it , path_elems , path_nulls , path_len , st , level ,
3742- newval , v .val .array .nElems , create );
3801+ newval , v .val .array .nElems , op_type );
37433802 r = JsonbIteratorNext (it , & v , false);
37443803 Assert (r == WJB_END_ARRAY );
37453804 res = pushJsonbValue (st , r , NULL );
37463805 break ;
37473806 case WJB_BEGIN_OBJECT :
37483807 (void ) pushJsonbValue (st , r , NULL );
37493808 setPathObject (it , path_elems , path_nulls , path_len , st , level ,
3750- newval , v .val .object .nPairs , create );
3809+ newval , v .val .object .nPairs , op_type );
37513810 r = JsonbIteratorNext (it , & v , true);
37523811 Assert (r == WJB_END_OBJECT );
37533812 res = pushJsonbValue (st , r , NULL );
@@ -3771,7 +3830,7 @@ setPath(JsonbIterator **it, Datum *path_elems,
37713830static void
37723831setPathObject (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
37733832 int path_len , JsonbParseState * * st , int level ,
3774- Jsonb * newval , uint32 npairs , bool create )
3833+ Jsonb * newval , uint32 npairs , int op_type )
37753834{
37763835 JsonbValue v ;
37773836 int i ;
@@ -3782,7 +3841,8 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
37823841 done = true;
37833842
37843843 /* empty object is a special case for create */
3785- if ((npairs == 0 ) && create && (level == path_len - 1 ))
3844+ if ((npairs == 0 ) && (op_type & JB_PATH_CREATE_OR_INSERT ) &&
3845+ (level == path_len - 1 ))
37863846 {
37873847 JsonbValue newkey ;
37883848
@@ -3807,8 +3867,19 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38073867 {
38083868 if (level == path_len - 1 )
38093869 {
3810- r = JsonbIteratorNext (it , & v , true); /* skip */
3811- if (newval != NULL )
3870+ /*
3871+ * called from jsonb_insert(), it forbids redefining
3872+ * an existsing value
3873+ */
3874+ if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_INSERT_AFTER ))
3875+ ereport (ERROR ,
3876+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3877+ errmsg ("cannot replace existing key" ),
3878+ errhint ("Try using the function jsonb_set "
3879+ "to replace key value." )));
3880+
3881+ r = JsonbIteratorNext (it , & v , true); /* skip value */
3882+ if (!(op_type & JB_PATH_DELETE ))
38123883 {
38133884 (void ) pushJsonbValue (st , WJB_KEY , & k );
38143885 addJsonbToParseState (st , newval );
@@ -3819,12 +3890,13 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38193890 {
38203891 (void ) pushJsonbValue (st , r , & k );
38213892 setPath (it , path_elems , path_nulls , path_len ,
3822- st , level + 1 , newval , create );
3893+ st , level + 1 , newval , op_type );
38233894 }
38243895 }
38253896 else
38263897 {
3827- if (create && !done && level == path_len - 1 && i == npairs - 1 )
3898+ if ((op_type & JB_PATH_CREATE_OR_INSERT ) && !done &&
3899+ level == path_len - 1 && i == npairs - 1 )
38283900 {
38293901 JsonbValue newkey ;
38303902
@@ -3865,7 +3937,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38653937static void
38663938setPathArray (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
38673939 int path_len , JsonbParseState * * st , int level ,
3868- Jsonb * newval , uint32 nelems , bool create )
3940+ Jsonb * newval , uint32 nelems , int op_type )
38693941{
38703942 JsonbValue v ;
38713943 int idx ,
@@ -3909,7 +3981,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
39093981 * what the idx value is
39103982 */
39113983
3912- if ((idx == INT_MIN || nelems == 0 ) && create && (level == path_len - 1 ))
3984+ if ((idx == INT_MIN || nelems == 0 ) && (level == path_len - 1 ) &&
3985+ (op_type & JB_PATH_CREATE_OR_INSERT ))
39133986 {
39143987 Assert (newval != NULL );
39153988 addJsonbToParseState (st , newval );
@@ -3926,14 +3999,26 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
39263999 if (level == path_len - 1 )
39274000 {
39284001 r = JsonbIteratorNext (it , & v , true); /* skip */
3929- if (newval != NULL )
4002+
4003+ if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE ))
4004+ addJsonbToParseState (st , newval );
4005+
4006+ /*
4007+ * We should keep current value only in case of
4008+ * JB_PATH_INSERT_BEFORE or JB_PATH_INSERT_AFTER
4009+ * because otherwise it should be deleted or replaced
4010+ */
4011+ if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_INSERT_BEFORE ))
4012+ (void ) pushJsonbValue (st , r , & v );
4013+
4014+ if (op_type & JB_PATH_INSERT_AFTER )
39304015 addJsonbToParseState (st , newval );
39314016
39324017 done = true;
39334018 }
39344019 else
39354020 (void ) setPath (it , path_elems , path_nulls , path_len ,
3936- st , level + 1 , newval , create );
4021+ st , level + 1 , newval , op_type );
39374022 }
39384023 else
39394024 {
@@ -3958,7 +4043,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
39584043 }
39594044 }
39604045
3961- if (create && !done && level == path_len - 1 && i == nelems - 1 )
4046+ if (op_type & JB_PATH_CREATE_OR_INSERT && !done &&
4047+ level == path_len - 1 && i == nelems - 1 )
39624048 {
39634049 addJsonbToParseState (st , newval );
39644050 }
0 commit comments