aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/jsonfuncs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/utils/adt/jsonfuncs.c')
-rw-r--r--src/backend/utils/adt/jsonfuncs.c151
1 files changed, 104 insertions, 47 deletions
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);
+ }
+
}
}
}