@@ -124,18 +124,20 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
124124 char * key ,
125125 uint32 keylen );
126126
127- /* functions supporting jsonb_delete, jsonb_replace and jsonb_concat */
127+ /* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
128128static JsonbValue * IteratorConcat (JsonbIterator * * it1 , JsonbIterator * * it2 ,
129129 JsonbParseState * * state );
130- static JsonbValue * replacePath (JsonbIterator * * it , Datum * path_elems ,
131- bool * path_nulls , int path_len ,
132- JsonbParseState * * st , int level , Jsonb * newval );
133- static void replacePathObject (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
134- int path_len , JsonbParseState * * st , int level ,
135- Jsonb * newval , uint32 nelems );
136- static void replacePathArray (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
137- int path_len , JsonbParseState * * st , int level ,
138- Jsonb * newval , uint32 npairs );
130+ static JsonbValue * setPath (JsonbIterator * * it , Datum * path_elems ,
131+ bool * path_nulls , int path_len ,
132+ JsonbParseState * * st , int level , Jsonb * newval ,
133+ bool create );
134+ static void setPathObject (JsonbIterator * * it , Datum * path_elems ,
135+ bool * path_nulls , int path_len , JsonbParseState * * st ,
136+ int level ,
137+ Jsonb * newval , uint32 npairs , bool create );
138+ static void setPathArray (JsonbIterator * * it , Datum * path_elems ,
139+ bool * path_nulls , int path_len , JsonbParseState * * st ,
140+ int level , Jsonb * newval , uint32 nelems , bool create );
139141static void addJsonbToParseState (JsonbParseState * * jbps , Jsonb * jb );
140142
141143/* state for json_object_keys */
@@ -3443,14 +3445,16 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
34433445}
34443446
34453447/*
3446- * SQL function jsonb_replace(jsonb, text[], jsonb)
3448+ * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
3449+ *
34473450 */
34483451Datum
3449- jsonb_replace (PG_FUNCTION_ARGS )
3452+ jsonb_set (PG_FUNCTION_ARGS )
34503453{
34513454 Jsonb * in = PG_GETARG_JSONB (0 );
34523455 ArrayType * path = PG_GETARG_ARRAYTYPE_P (1 );
34533456 Jsonb * newval = PG_GETARG_JSONB (2 );
3457+ bool create = PG_GETARG_BOOL (3 );
34543458 JsonbValue * res = NULL ;
34553459 Datum * path_elems ;
34563460 bool * path_nulls ;
@@ -3466,9 +3470,9 @@ jsonb_replace(PG_FUNCTION_ARGS)
34663470 if (JB_ROOT_IS_SCALAR (in ))
34673471 ereport (ERROR ,
34683472 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3469- errmsg ("cannot replace path in scalar" )));
3473+ errmsg ("cannot set path in scalar" )));
34703474
3471- if (JB_ROOT_COUNT (in ) == 0 )
3475+ if (JB_ROOT_COUNT (in ) == 0 && ! create )
34723476 PG_RETURN_JSONB (in );
34733477
34743478 deconstruct_array (path , TEXTOID , -1 , false, 'i' ,
@@ -3479,7 +3483,8 @@ jsonb_replace(PG_FUNCTION_ARGS)
34793483
34803484 it = JsonbIteratorInit (& in -> root );
34813485
3482- res = replacePath (& it , path_elems , path_nulls , path_len , & st , 0 , newval );
3486+ res = setPath (& it , path_elems , path_nulls , path_len , & st ,
3487+ 0 , newval , create );
34833488
34843489 Assert (res != NULL );
34853490
@@ -3523,7 +3528,7 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
35233528
35243529 it = JsonbIteratorInit (& in -> root );
35253530
3526- res = replacePath (& it , path_elems , path_nulls , path_len , & st , 0 , NULL );
3531+ res = setPath (& it , path_elems , path_nulls , path_len , & st , 0 , NULL , false );
35273532
35283533 Assert (res != NULL );
35293534
@@ -3563,7 +3568,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
35633568 */
35643569 pushJsonbValue (state , r1 , NULL );
35653570 while ((r1 = JsonbIteratorNext (it1 , & v1 , true)) != WJB_END_OBJECT )
3566- pushJsonbValue (state , r1 , & v1 );
3571+ pushJsonbValue (state , r1 , & v1 );
35673572
35683573 /*
35693574 * Append the all tokens from v2 to res, include last WJB_END_OBJECT
@@ -3586,7 +3591,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
35863591 pushJsonbValue (state , r1 , & v1 );
35873592 }
35883593
3589- while ((r2 = JsonbIteratorNext (it2 , & v2 , true)) != WJB_END_ARRAY )
3594+ while ((r2 = JsonbIteratorNext (it2 , & v2 , true)) != WJB_END_ARRAY )
35903595 {
35913596 Assert (r2 == WJB_ELEM );
35923597 pushJsonbValue (state , WJB_ELEM , & v2 );
@@ -3642,12 +3647,18 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
36423647}
36433648
36443649/*
3645- * do most of the heavy work for jsonb_replace
3650+ * Do most of the heavy work for jsonb_set
3651+ *
3652+ * If newval is null, the element is to be removed.
3653+ *
3654+ * If create is true, we create the new value if the key or array index
3655+ * does not exist. All path elemnts before the last must already exist
3656+ * whether or not create is true, or nothing is done.
36463657 */
36473658static JsonbValue *
3648- replacePath (JsonbIterator * * it , Datum * path_elems ,
3649- bool * path_nulls , int path_len ,
3650- JsonbParseState * * st , int level , Jsonb * newval )
3659+ setPath (JsonbIterator * * it , Datum * path_elems ,
3660+ bool * path_nulls , int path_len ,
3661+ JsonbParseState * * st , int level , Jsonb * newval , bool create )
36513662{
36523663 JsonbValue v ;
36533664 JsonbValue * res = NULL ;
@@ -3659,17 +3670,17 @@ replacePath(JsonbIterator **it, Datum *path_elems,
36593670 {
36603671 case WJB_BEGIN_ARRAY :
36613672 (void ) pushJsonbValue (st , r , NULL );
3662- replacePathArray (it , path_elems , path_nulls , path_len , st , level ,
3663- newval , v .val .array .nElems );
3673+ setPathArray (it , path_elems , path_nulls , path_len , st , level ,
3674+ newval , v .val .array .nElems , create );
36643675 r = JsonbIteratorNext (it , & v , false);
36653676 Assert (r == WJB_END_ARRAY );
36663677 res = pushJsonbValue (st , r , NULL );
36673678
36683679 break ;
36693680 case WJB_BEGIN_OBJECT :
36703681 (void ) pushJsonbValue (st , r , NULL );
3671- replacePathObject (it , path_elems , path_nulls , path_len , st , level ,
3672- newval , v .val .object .nPairs );
3682+ setPathObject (it , path_elems , path_nulls , path_len , st , level ,
3683+ newval , v .val .object .nPairs , create );
36733684 r = JsonbIteratorNext (it , & v , true);
36743685 Assert (r == WJB_END_OBJECT );
36753686 res = pushJsonbValue (st , r , NULL );
@@ -3687,12 +3698,12 @@ replacePath(JsonbIterator **it, Datum *path_elems,
36873698}
36883699
36893700/*
3690- * Object walker for replacePath
3701+ * Object walker for setPath
36913702 */
36923703static void
3693- replacePathObject (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
3694- int path_len , JsonbParseState * * st , int level ,
3695- Jsonb * newval , uint32 nelems )
3704+ setPathObject (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
3705+ int path_len , JsonbParseState * * st , int level ,
3706+ Jsonb * newval , uint32 npairs , bool create )
36963707{
36973708 JsonbValue v ;
36983709 int i ;
@@ -3702,7 +3713,19 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
37023713 if (level >= path_len || path_nulls [level ])
37033714 done = true;
37043715
3705- for (i = 0 ; i < nelems ; i ++ )
3716+ /* empty object is a special case for create */
3717+ if ((npairs == 0 ) && create && (level == path_len - 1 ))
3718+ {
3719+ JsonbValue new = k ;
3720+
3721+ new .val .string .len = VARSIZE_ANY_EXHDR (path_elems [level ]);
3722+ new .val .string .val = VARDATA_ANY (path_elems [level ]);
3723+
3724+ (void ) pushJsonbValue (st , WJB_KEY , & new );
3725+ addJsonbToParseState (st , newval );
3726+ }
3727+
3728+ for (i = 0 ; i < npairs ; i ++ )
37063729 {
37073730 int r = JsonbIteratorNext (it , & k , true);
37083731
@@ -3721,16 +3744,28 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
37213744 (void ) pushJsonbValue (st , WJB_KEY , & k );
37223745 addJsonbToParseState (st , newval );
37233746 }
3747+ done = true;
37243748 }
37253749 else
37263750 {
37273751 (void ) pushJsonbValue (st , r , & k );
3728- replacePath (it , path_elems , path_nulls , path_len ,
3729- st , level + 1 , newval );
3752+ setPath (it , path_elems , path_nulls , path_len ,
3753+ st , level + 1 , newval , create );
37303754 }
37313755 }
37323756 else
37333757 {
3758+ if (create && !done && level == path_len - 1 && i == npairs - 1 )
3759+ {
3760+ JsonbValue new = k ;
3761+
3762+ new .val .string .len = VARSIZE_ANY_EXHDR (path_elems [level ]);
3763+ new .val .string .val = VARDATA_ANY (path_elems [level ]);
3764+
3765+ (void ) pushJsonbValue (st , WJB_KEY , & new );
3766+ addJsonbToParseState (st , newval );
3767+ }
3768+
37343769 (void ) pushJsonbValue (st , r , & k );
37353770 r = JsonbIteratorNext (it , & v , false);
37363771 (void ) pushJsonbValue (st , r , r < WJB_BEGIN_ARRAY ? & v : NULL );
@@ -3755,17 +3790,18 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
37553790}
37563791
37573792/*
3758- * Array walker for replacePath
3793+ * Array walker for setPath
37593794 */
37603795static void
3761- replacePathArray (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
3762- int path_len , JsonbParseState * * st , int level ,
3763- Jsonb * newval , uint32 npairs )
3796+ setPathArray (JsonbIterator * * it , Datum * path_elems , bool * path_nulls ,
3797+ int path_len , JsonbParseState * * st , int level ,
3798+ Jsonb * newval , uint32 nelems , bool create )
37643799{
37653800 JsonbValue v ;
37663801 int idx ,
37673802 i ;
37683803 char * badp ;
3804+ bool done = false;
37693805
37703806 /* pick correct index */
37713807 if (level < path_len && !path_nulls [level ])
@@ -3775,24 +3811,37 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
37753811 errno = 0 ;
37763812 idx = (int ) strtol (c , & badp , 10 );
37773813 if (errno != 0 || badp == c )
3778- idx = npairs ;
3814+ idx = nelems ;
37793815 }
37803816 else
3781- idx = npairs ;
3817+ idx = nelems ;
37823818
37833819 if (idx < 0 )
37843820 {
3785- if (- idx > npairs )
3786- idx = npairs ;
3821+ if (- idx > nelems )
3822+ idx = -1 ;
37873823 else
3788- idx = npairs + idx ;
3824+ idx = nelems + idx ;
37893825 }
37903826
3791- if (idx > npairs )
3792- idx = npairs ;
3827+ if (idx > 0 && idx > nelems )
3828+ idx = nelems ;
3829+
3830+ /*
3831+ * if we're creating, and idx == -1, we prepend the new value to the array
3832+ * also if the array is empty - in which case we don't really care what
3833+ * the idx value is
3834+ */
3835+
3836+ if ((idx == -1 || nelems == 0 ) && create && (level == path_len - 1 ))
3837+ {
3838+ Assert (newval != NULL );
3839+ addJsonbToParseState (st , newval );
3840+ done = true;
3841+ }
37933842
37943843 /* iterate over the array elements */
3795- for (i = 0 ; i < npairs ; i ++ )
3844+ for (i = 0 ; i < nelems ; i ++ )
37963845 {
37973846 int r ;
37983847
@@ -3803,10 +3852,12 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38033852 r = JsonbIteratorNext (it , & v , true); /* skip */
38043853 if (newval != NULL )
38053854 addJsonbToParseState (st , newval );
3855+
3856+ done = true;
38063857 }
38073858 else
3808- (void ) replacePath (it , path_elems , path_nulls , path_len ,
3809- st , level + 1 , newval );
3859+ (void ) setPath (it , path_elems , path_nulls , path_len ,
3860+ st , level + 1 , newval , create );
38103861 }
38113862 else
38123863 {
@@ -3830,6 +3881,12 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
38303881 (void ) pushJsonbValue (st , r , r < WJB_BEGIN_ARRAY ? & v : NULL );
38313882 }
38323883 }
3884+
3885+ if (create && !done && level == path_len - 1 && i == nelems - 1 )
3886+ {
3887+ addJsonbToParseState (st , newval );
3888+ }
3889+
38333890 }
38343891 }
38353892}
0 commit comments