diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 89a609f61c185a2fad448805d7e5ccff63a654ee..95d5a3a4d84857ce792c77a7070f0ea4aa8a8669 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -10323,6 +10323,15 @@ table2-mapping + + + The || operator concatenates the elements at the top level of + each of its operands. It does not operate recursively. For example, if + both operands are objects with a common key field name, the value of the + field in the result will just be the value from the right hand operand. + + + shows the functions that are available for creating json and jsonb values. @@ -10830,17 +10839,24 @@ table2-mapping [{"f1":1},2,null,3] - jsonb_replace(target jsonb, path text[], replacement jsonb) + jsonb_set(target jsonb, path text[], new_value jsonb, create_missing boolean) jsonb Returns target - with the section designated by path - replaced by replacement. - - jsonb_replace('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]') - [{"f1":[2,3,4],"f2":null},2,null,3] - + with the section designated by path + replaced by new_value, or with + new_value added if + create_missing is true ( default is + true) and the item + designated by path does not exist. + + jsonb_set('[{"f1":1,"f2":null},2,null,3]', '{0,f1}','[2,3,4]', false) + jsonb_set('[{"f1":1,"f2":null},2]', '{0,f3}','[2,3,4]') + + [{"f1":[2,3,4],"f2":null},2,null,3] + [{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2] + jsonb_pretty(from_json jsonb) @@ -10891,6 +10907,27 @@ table2-mapping + + + All the items of the path parameter of jsonb_set + must be present in the target, unless + create_missing is true, in which case all but the last item + must be present. If these conditions are not met the target + is returned unchanged. + + + If the last path item is an object key, it will be created if it + is absent and given the new value. If the last path item is an array + index, if it is positive the item to set is found by counting from + the left, and if negative by counting from the right - -1 + designates the rightmost element, and so on. + If the item is out of the range -array_length .. array_length -1, + and create_missing is true, the new value is added at the beginning + of the array if the item is negative, and at the end of the array if + it is positive. + + + The json_typeof function's null return value diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 18921c4bc52aba0ebf11346b3790d7978e7b8d45..e82a53aee93649898bdbc39d8e0f9d398a0eaecf 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 fa059c4d6cf7f2004ced445546f1b32a473061a1..b1c4b022535a190fc2c867dd21ce9cf6426d1170 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 1bedee6e16acc4bced82114f8b8fac976734a8a6..5ca4af4da40238e8a04fcf1aef9f8840100bff81 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 6f855eab41e54bdae165ef9af1a7f583cc3ef48b..fe06ec285dbfe4cbb48881cc78ebc490e827ce0f 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 026ed55a3c9ee73dffc016460719ddbbb4f848a8..3049a87321cbc101f99b30f9ca8faec193d9ec25 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 c589cd1522a1f3fefc5a30661fea2b51b2a5bc03..412bf97b83f0461fa84dcc8a3ec9a33506711f10 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 c4b51e56e37dc703506560ced9fbe0cc579d0865..4ead74b572672ad31da2fc2c181a86d3873c012b 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 382a7fb6ae09d89d12899eb89e3d92545525aad6..2abec221b43bd714415742b8e7765fd15d01663b 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}');