@ -33,6 +33,15 @@
# 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 creat e) ;
int op_typ e) ;
static void setPathObject ( JsonbIterator * * it , Datum * path_elems ,
bool * path_nulls , int path_len , JsonbParseState * * st ,
int level ,
Jsonb * newval , uint32 npairs , bool creat e) ;
Jsonb * newval , uint32 npairs , int op_typ e) ;
static void setPathArray ( JsonbIterator * * it , Datum * path_elems ,
bool * path_nulls , int path_len , JsonbParseState * * st ,
int level , Jsonb * newval , uint32 nelems , bool creat e) ;
int level , Jsonb * newval , uint32 nelems , int op_typ e) ;
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 creat e)
JsonbParseState * * st , int level , Jsonb * newval , int op_typ e)
{
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 , creat e) ;
newval , v . val . array . nElems , op_typ e) ;
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 , creat e) ;
newval , v . val . object . nPairs , op_typ e) ;
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 creat e)
Jsonb * newval , uint32 npairs , int op_typ e)
{
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 , creat e) ;
st , level + 1 , newval , op_typ e) ;
}
}
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 creat e)
Jsonb * newval , uint32 nelems , int op_typ e)
{
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_typ e) ;
}
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 ) ;
}