aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/backend/catalog/system_views.sql8
-rw-r--r--src/backend/utils/adt/jsonfuncs.c151
-rw-r--r--src/include/catalog/catversion.h2
-rw-r--r--src/include/catalog/pg_proc.h4
-rw-r--r--src/include/utils/jsonb.h2
-rw-r--r--src/test/regress/expected/jsonb.out124
-rw-r--r--src/test/regress/expected/jsonb_1.out124
-rw-r--r--src/test/regress/sql/jsonb.sql46
8 files changed, 341 insertions, 120 deletions
diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql
index 18921c4bc52..e82a53aee93 100644
--- a/src/backend/catalog/system_views.sql
+++ b/src/backend/catalog/system_views.sql
@@ -922,3 +922,11 @@ RETURNS interval
LANGUAGE INTERNAL
STRICT IMMUTABLE
AS 'make_interval';
+
+CREATE OR REPLACE FUNCTION
+ jsonb_set(jsonb_in jsonb, path text[] , replacement jsonb,
+ create_if_missing boolean DEFAULT true)
+RETURNS jsonb
+LANGUAGE INTERNAL
+STRICT IMMUTABLE
+AS 'jsonb_set';
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index fa059c4d6cf..b1c4b022535 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -124,18 +124,20 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
char *key,
uint32 keylen);
-/* functions supporting jsonb_delete, jsonb_replace and jsonb_concat */
+/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
-static JsonbValue *replacePath(JsonbIterator **it, Datum *path_elems,
- bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval);
-static void replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems);
-static void replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs);
+static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len,
+ JsonbParseState **st, int level, Jsonb *newval,
+ bool create);
+static void setPathObject(JsonbIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len, JsonbParseState **st,
+ int level,
+ Jsonb *newval, uint32 npairs, bool create);
+static void setPathArray(JsonbIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len, JsonbParseState **st,
+ int level, Jsonb *newval, uint32 nelems, bool create);
static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
/* state for json_object_keys */
@@ -3443,14 +3445,16 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
}
/*
- * SQL function jsonb_replace(jsonb, text[], jsonb)
+ * SQL function jsonb_set(jsonb, text[], jsonb, boolean)
+ *
*/
Datum
-jsonb_replace(PG_FUNCTION_ARGS)
+jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
Jsonb *newval = PG_GETARG_JSONB(2);
+ bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
bool *path_nulls;
@@ -3466,9 +3470,9 @@ jsonb_replace(PG_FUNCTION_ARGS)
if (JB_ROOT_IS_SCALAR(in))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("cannot replace path in scalar")));
+ errmsg("cannot set path in scalar")));
- if (JB_ROOT_COUNT(in) == 0)
+ if (JB_ROOT_COUNT(in) == 0 && !create)
PG_RETURN_JSONB(in);
deconstruct_array(path, TEXTOID, -1, false, 'i',
@@ -3479,7 +3483,8 @@ jsonb_replace(PG_FUNCTION_ARGS)
it = JsonbIteratorInit(&in->root);
- res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, newval);
+ res = setPath(&it, path_elems, path_nulls, path_len, &st,
+ 0, newval, create);
Assert(res != NULL);
@@ -3523,7 +3528,7 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
it = JsonbIteratorInit(&in->root);
- res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, NULL);
+ res = setPath(&it, path_elems, path_nulls, path_len, &st, 0, NULL, false);
Assert(res != NULL);
@@ -3563,7 +3568,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
*/
pushJsonbValue(state, r1, NULL);
while ((r1 = JsonbIteratorNext(it1, &v1, true)) != WJB_END_OBJECT)
- pushJsonbValue(state, r1, &v1 );
+ pushJsonbValue(state, r1, &v1);
/*
* Append the all tokens from v2 to res, include last WJB_END_OBJECT
@@ -3586,7 +3591,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
pushJsonbValue(state, r1, &v1);
}
- while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_END_ARRAY)
+ while ((r2 = JsonbIteratorNext(it2, &v2, true)) != WJB_END_ARRAY)
{
Assert(r2 == WJB_ELEM);
pushJsonbValue(state, WJB_ELEM, &v2);
@@ -3642,12 +3647,18 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
}
/*
- * do most of the heavy work for jsonb_replace
+ * Do most of the heavy work for jsonb_set
+ *
+ * If newval is null, the element is to be removed.
+ *
+ * If create is true, we create the new value if the key or array index
+ * does not exist. All path elemnts before the last must already exist
+ * whether or not create is true, or nothing is done.
*/
static JsonbValue *
-replacePath(JsonbIterator **it, Datum *path_elems,
- bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval)
+setPath(JsonbIterator **it, Datum *path_elems,
+ bool *path_nulls, int path_len,
+ JsonbParseState **st, int level, Jsonb *newval, bool create)
{
JsonbValue v;
JsonbValue *res = NULL;
@@ -3659,8 +3670,8 @@ replacePath(JsonbIterator **it, Datum *path_elems,
{
case WJB_BEGIN_ARRAY:
(void) pushJsonbValue(st, r, NULL);
- replacePathArray(it, path_elems, path_nulls, path_len, st, level,
- newval, v.val.array.nElems);
+ setPathArray(it, path_elems, path_nulls, path_len, st, level,
+ newval, v.val.array.nElems, create);
r = JsonbIteratorNext(it, &v, false);
Assert(r == WJB_END_ARRAY);
res = pushJsonbValue(st, r, NULL);
@@ -3668,8 +3679,8 @@ replacePath(JsonbIterator **it, Datum *path_elems,
break;
case WJB_BEGIN_OBJECT:
(void) pushJsonbValue(st, r, NULL);
- replacePathObject(it, path_elems, path_nulls, path_len, st, level,
- newval, v.val.object.nPairs);
+ setPathObject(it, path_elems, path_nulls, path_len, st, level,
+ newval, v.val.object.nPairs, create);
r = JsonbIteratorNext(it, &v, true);
Assert(r == WJB_END_OBJECT);
res = pushJsonbValue(st, r, NULL);
@@ -3687,12 +3698,12 @@ replacePath(JsonbIterator **it, Datum *path_elems,
}
/*
- * Object walker for replacePath
+ * Object walker for setPath
*/
static void
-replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems)
+setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
+ int path_len, JsonbParseState **st, int level,
+ Jsonb *newval, uint32 npairs, bool create)
{
JsonbValue v;
int i;
@@ -3702,7 +3713,19 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (level >= path_len || path_nulls[level])
done = true;
- for (i = 0; i < nelems; i++)
+ /* empty object is a special case for create */
+ if ((npairs == 0) && create && (level == path_len - 1))
+ {
+ JsonbValue new = k;
+
+ new.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
+ new.val.string.val = VARDATA_ANY(path_elems[level]);
+
+ (void) pushJsonbValue(st, WJB_KEY, &new);
+ addJsonbToParseState(st, newval);
+ }
+
+ for (i = 0; i < npairs; i++)
{
int r = JsonbIteratorNext(it, &k, true);
@@ -3721,16 +3744,28 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, WJB_KEY, &k);
addJsonbToParseState(st, newval);
}
+ done = true;
}
else
{
(void) pushJsonbValue(st, r, &k);
- replacePath(it, path_elems, path_nulls, path_len,
- st, level + 1, newval);
+ setPath(it, path_elems, path_nulls, path_len,
+ st, level + 1, newval, create);
}
}
else
{
+ if (create && !done && level == path_len - 1 && i == npairs - 1)
+ {
+ JsonbValue new = k;
+
+ new.val.string.len = VARSIZE_ANY_EXHDR(path_elems[level]);
+ new.val.string.val = VARDATA_ANY(path_elems[level]);
+
+ (void) pushJsonbValue(st, WJB_KEY, &new);
+ addJsonbToParseState(st, newval);
+ }
+
(void) pushJsonbValue(st, r, &k);
r = JsonbIteratorNext(it, &v, false);
(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
@@ -3755,17 +3790,18 @@ replacePathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
}
/*
- * Array walker for replacePath
+ * Array walker for setPath
*/
static void
-replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
- int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs)
+setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
+ int path_len, JsonbParseState **st, int level,
+ Jsonb *newval, uint32 nelems, bool create)
{
JsonbValue v;
int idx,
i;
char *badp;
+ bool done = false;
/* pick correct index */
if (level < path_len && !path_nulls[level])
@@ -3775,24 +3811,37 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
errno = 0;
idx = (int) strtol(c, &badp, 10);
if (errno != 0 || badp == c)
- idx = npairs;
+ idx = nelems;
}
else
- idx = npairs;
+ idx = nelems;
if (idx < 0)
{
- if (-idx > npairs)
- idx = npairs;
+ if (-idx > nelems)
+ idx = -1;
else
- idx = npairs + idx;
+ idx = nelems + idx;
}
- if (idx > npairs)
- idx = npairs;
+ if (idx > 0 && idx > nelems)
+ idx = nelems;
+
+ /*
+ * if we're creating, and idx == -1, we prepend the new value to the array
+ * also if the array is empty - in which case we don't really care what
+ * the idx value is
+ */
+
+ if ((idx == -1 || nelems == 0) && create && (level == path_len - 1))
+ {
+ Assert(newval != NULL);
+ addJsonbToParseState(st, newval);
+ done = true;
+ }
/* iterate over the array elements */
- for (i = 0; i < npairs; i++)
+ for (i = 0; i < nelems; i++)
{
int r;
@@ -3803,10 +3852,12 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (newval != NULL)
addJsonbToParseState(st, newval);
+
+ done = true;
}
else
- (void) replacePath(it, path_elems, path_nulls, path_len,
- st, level + 1, newval);
+ (void) setPath(it, path_elems, path_nulls, path_len,
+ st, level + 1, newval, create);
}
else
{
@@ -3830,6 +3881,12 @@ replacePathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
}
}
+
+ if (create && !done && level == path_len - 1 && i == nelems - 1)
+ {
+ addJsonbToParseState(st, newval);
+ }
+
}
}
}
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1bedee6e16a..5ca4af4da40 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 201505291
+#define CATALOG_VERSION_NO 201505311
#endif
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 6f855eab41e..fe06ec285db 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -4859,8 +4859,8 @@ DATA(insert OID = 3301 ( jsonb_concat PGNSP PGUID 12 1 0 0 0 f f f f t f i 2
DATA(insert OID = 3302 ( jsonb_delete PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ _null_ _null_ _null_ jsonb_delete _null_ _null_ _null_ ));
DATA(insert OID = 3303 ( jsonb_delete PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ _null_ _null_ _null_ jsonb_delete_idx _null_ _null_ _null_ ));
DATA(insert OID = 3304 ( jsonb_delete PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 1009" _null_ _null_ _null_ _null_ _null_ jsonb_delete_path _null_ _null_ _null_ ));
-DATA(insert OID = 3305 ( jsonb_replace PGNSP PGUID 12 1 0 0 0 f f f f t f i 3 0 3802 "3802 1009 3802" _null_ _null_ _null_ _null_ _null_ jsonb_replace _null_ _null_ _null_ ));
-DESCR("Replace part of a jsonb");
+DATA(insert OID = 3305 ( jsonb_set PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 3802 "3802 1009 3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_set _null_ _null_ _null_ ));
+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 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ ));
DESCR("Indented text from jsonb");
/* txid */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 026ed55a3c9..3049a87321c 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -406,7 +406,7 @@ extern Datum jsonb_delete_idx(PG_FUNCTION_ARGS);
extern Datum jsonb_delete_path(PG_FUNCTION_ARGS);
/* replacement */
-extern Datum jsonb_replace(PG_FUNCTION_ARGS);
+extern Datum jsonb_set(PG_FUNCTION_ARGS);
/* Support functions */
extern uint32 getJsonbOffset(const JsonbContainer *jc, int index);
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index c589cd1522a..412bf97b83f 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3079,62 +3079,62 @@ select '{"a":1, "b":2, "c":3}'::jsonb - -4;
{"a": 1, "b": 2, "c": 3}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
+ jsonb_set
--------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": [1, 2, 3]}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
+ jsonb_set
-----------------------------------------------------------------------------
{"a": 1, "b": [1, [1, 2, 3]], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
+ jsonb_set
-----------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [[1, 2, 3], 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
+ jsonb_set
---------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
+ jsonb_set
-------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": {"1": 2}}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
+ jsonb_set
----------------------------------------------------------------------------
{"a": 1, "b": [1, {"1": 2}], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
+ jsonb_set
----------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [{"1": 2}, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
+ jsonb_set
---------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
+ jsonb_set
--------------------------------------------------------------------------
{"a": 1, "b": [1, "test"], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
+ jsonb_set
---------------------------------------------------------------------------------
{"a": 1, "b": [1, {"f": "test"}], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
@@ -3218,17 +3218,85 @@ select '[]'::jsonb - '{a}'::text[];
[]
(1 row)
-select jsonb_replace('"a"','{a}','"b"'); --error
-ERROR: cannot replace path in scalar
-select jsonb_replace('{}','{a}','"b"');
- jsonb_replace
----------------
+select jsonb_set('"a"','{a}','"b"'); --error
+ERROR: cannot set path in scalar
+select jsonb_set('{}','{a}','"b"', false);
+ jsonb_set
+-----------
{}
(1 row)
-select jsonb_replace('[]','{1}','"b"');
- jsonb_replace
----------------
+select jsonb_set('[]','{1}','"b"', false);
+ jsonb_set
+-----------
[]
(1 row)
+-- jsonb_set adding instead of replacing
+-- prepend to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}');
+ jsonb_set
+-------------------------------------------------------
+ {"a": 1, "b": [{"foo": 123}, 0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+-- append to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}');
+ jsonb_set
+-------------------------------------------------------
+ {"a": 1, "b": [0, 1, 2, {"foo": 123}], "c": {"d": 4}}
+(1 row)
+
+-- check nesting levels addition
+select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}');
+ jsonb_set
+---------------------------------------------------------------------
+ {"a": 1, "b": [4, 5, [0, 1, 2, {"foo": 123}], 6, 7], "c": {"d": 4}}
+(1 row)
+
+-- add new key
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}');
+ jsonb_set
+------------------------------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4, "e": {"foo": 123}}}
+(1 row)
+
+-- adding doesn't do anything if elements before last aren't present
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}');
+ jsonb_set
+-----------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}');
+ jsonb_set
+-----------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+-- add to empty object
+select jsonb_set('{}','{x}','{"foo":123}');
+ jsonb_set
+---------------------
+ {"x": {"foo": 123}}
+(1 row)
+
+--add to empty array
+select jsonb_set('[]','{0}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
+select jsonb_set('[]','{99}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
+select jsonb_set('[]','{-99}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
index c4b51e56e37..4ead74b5726 100644
--- a/src/test/regress/expected/jsonb_1.out
+++ b/src/test/regress/expected/jsonb_1.out
@@ -3079,62 +3079,62 @@ select '{"a":1, "b":2, "c":3}'::jsonb - -4;
{"a": 1, "b": 2, "c": 3}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
+ jsonb_set
--------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": [1, 2, 3]}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
+ jsonb_set
-----------------------------------------------------------------------------
{"a": 1, "b": [1, [1, 2, 3]], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
+ jsonb_set
-----------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [[1, 2, 3], 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
+ jsonb_set
---------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
+ jsonb_set
-------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": {"1": 2}}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
+ jsonb_set
----------------------------------------------------------------------------
{"a": 1, "b": [1, {"1": 2}], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
+ jsonb_set
----------------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [{"1": 2}, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
+ jsonb_set
---------------------------------------------------------------------
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
+ jsonb_set
--------------------------------------------------------------------------
{"a": 1, "b": [1, "test"], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
- jsonb_replace
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
+ jsonb_set
---------------------------------------------------------------------------------
{"a": 1, "b": [1, {"f": "test"}], "c": {"1": 2}, "d": {"1": [2, 3]}, "n": null}
(1 row)
@@ -3218,17 +3218,85 @@ select '[]'::jsonb - '{a}'::text[];
[]
(1 row)
-select jsonb_replace('"a"','{a}','"b"'); --error
-ERROR: cannot replace path in scalar
-select jsonb_replace('{}','{a}','"b"');
- jsonb_replace
----------------
+select jsonb_set('"a"','{a}','"b"'); --error
+ERROR: cannot set path in scalar
+select jsonb_set('{}','{a}','"b"', false);
+ jsonb_set
+-----------
{}
(1 row)
-select jsonb_replace('[]','{1}','"b"');
- jsonb_replace
----------------
+select jsonb_set('[]','{1}','"b"', false);
+ jsonb_set
+-----------
[]
(1 row)
+-- jsonb_set adding instead of replacing
+-- prepend to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}');
+ jsonb_set
+-------------------------------------------------------
+ {"a": 1, "b": [{"foo": 123}, 0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+-- append to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}');
+ jsonb_set
+-------------------------------------------------------
+ {"a": 1, "b": [0, 1, 2, {"foo": 123}], "c": {"d": 4}}
+(1 row)
+
+-- check nesting levels addition
+select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}');
+ jsonb_set
+---------------------------------------------------------------------
+ {"a": 1, "b": [4, 5, [0, 1, 2, {"foo": 123}], 6, 7], "c": {"d": 4}}
+(1 row)
+
+-- add new key
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}');
+ jsonb_set
+------------------------------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4, "e": {"foo": 123}}}
+(1 row)
+
+-- adding doesn't do anything if elements before last aren't present
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}');
+ jsonb_set
+-----------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}');
+ jsonb_set
+-----------------------------------------
+ {"a": 1, "b": [0, 1, 2], "c": {"d": 4}}
+(1 row)
+
+-- add to empty object
+select jsonb_set('{}','{x}','{"foo":123}');
+ jsonb_set
+---------------------
+ {"x": {"foo": 123}}
+(1 row)
+
+--add to empty array
+select jsonb_set('[]','{0}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
+select jsonb_set('[]','{99}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
+select jsonb_set('[]','{-99}','{"foo":123}');
+ jsonb_set
+----------------
+ [{"foo": 123}]
+(1 row)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 382a7fb6ae0..2abec221b43 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -747,18 +747,18 @@ select '{"a":1, "b":2, "c":3}'::jsonb - -2;
select '{"a":1, "b":2, "c":3}'::jsonb - -3;
select '{"a":1, "b":2, "c":3}'::jsonb - -4;
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '[1,2,3]');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '[1,2,3]');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '[1,2,3]');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '[1,2,3]');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{n}', '{"1": 2}');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"1": 2}');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,1,0}', '{"1": 2}');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{d,NULL,0}', '{"1": 2}');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
-select jsonb_replace('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '"test"');
+select jsonb_set('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb, '{b,-1}', '{"f": "test"}');
select jsonb_delete('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{n}'::text[]);
select jsonb_delete('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}', '{b,-1}'::text[]);
@@ -780,6 +780,26 @@ select '[]'::jsonb - 1;
select '"a"'::jsonb - '{a}'::text[]; -- error
select '{}'::jsonb - '{a}'::text[];
select '[]'::jsonb - '{a}'::text[];
-select jsonb_replace('"a"','{a}','"b"'); --error
-select jsonb_replace('{}','{a}','"b"');
-select jsonb_replace('[]','{1}','"b"');
+select jsonb_set('"a"','{a}','"b"'); --error
+select jsonb_set('{}','{a}','"b"', false);
+select jsonb_set('[]','{1}','"b"', false);
+
+-- jsonb_set adding instead of replacing
+
+-- prepend to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,-33}','{"foo":123}');
+-- append to array
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{b,33}','{"foo":123}');
+-- check nesting levels addition
+select jsonb_set('{"a":1,"b":[4,5,[0,1,2],6,7],"c":{"d":4}}','{b,2,33}','{"foo":123}');
+-- add new key
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{c,e}','{"foo":123}');
+-- adding doesn't do anything if elements before last aren't present
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,-33}','{"foo":123}');
+select jsonb_set('{"a":1,"b":[0,1,2],"c":{"d":4}}','{x,y}','{"foo":123}');
+-- add to empty object
+select jsonb_set('{}','{x}','{"foo":123}');
+--add to empty array
+select jsonb_set('[]','{0}','{"foo":123}');
+select jsonb_set('[]','{99}','{"foo":123}');
+select jsonb_set('[]','{-99}','{"foo":123}');