diff options
author | Teodor Sigaev <teodor@sigaev.ru> | 2016-04-06 19:20:17 +0300 |
---|---|---|
committer | Teodor Sigaev <teodor@sigaev.ru> | 2016-04-06 19:25:00 +0300 |
commit | 0b62fd036e1ac48a8432bb9664b21e1f036c1b08 (patch) | |
tree | 09a2b9176bb6b7ed77b86af1c212e7e2a18678a5 /src/backend/utils/adt/jsonfuncs.c | |
parent | 3b3fcc4eeaeecff315420833975e7c87d760bfe1 (diff) | |
download | postgresql-0b62fd036e1ac48a8432bb9664b21e1f036c1b08.tar.gz postgresql-0b62fd036e1ac48a8432bb9664b21e1f036c1b08.zip |
Add jsonb_insert
It inserts a new value into an jsonb array at arbitrary position or
a new key to jsonb object.
Author: Dmitry Dolgov
Reviewers: Petr Jelinek, Vitaly Burovoy, Andrew Dunstan
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r-- | src/backend/utils/adt/jsonfuncs.c | 134 |
1 files changed, 110 insertions, 24 deletions
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index 97e0e8e0596..fb149dcd477 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -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 create); + int op_type); static void setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, int level, - Jsonb *newval, uint32 npairs, bool create); + Jsonb *newval, uint32 npairs, int op_type); static void setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, int path_len, JsonbParseState **st, - int level, Jsonb *newval, uint32 nelems, bool create); + int level, Jsonb *newval, uint32 nelems, int op_type); 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 create) + JsonbParseState **st, int level, Jsonb *newval, int op_type) { 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, create); + newval, v.val.array.nElems, op_type); 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, create); + newval, v.val.object.nPairs, op_type); 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 create) + Jsonb *newval, uint32 npairs, int op_type) { 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, create); + st, level + 1, newval, op_type); } } 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 create) + Jsonb *newval, uint32 nelems, int op_type) { 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_type); } 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); } |