mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-10-04 04:46:52 +02:00
Fix jsonb replace and delete on scalars and empty structures
These operations now error out if attempted on scalars, and simply return the input if attempted on empty arrays or objects. Along the way we remove the unnecessary cloning of the input when it's known to be unchanged. Regression tests covering these cases are added.
This commit is contained in:
parent
ae6157164f
commit
3f2cec797e
@ -3332,7 +3332,7 @@ jsonb_concat(PG_FUNCTION_ARGS)
|
|||||||
out = JsonbValueToJsonb(res);
|
out = JsonbValueToJsonb(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3349,7 +3349,6 @@ jsonb_delete(PG_FUNCTION_ARGS)
|
|||||||
text *key = PG_GETARG_TEXT_PP(1);
|
text *key = PG_GETARG_TEXT_PP(1);
|
||||||
char *keyptr = VARDATA_ANY(key);
|
char *keyptr = VARDATA_ANY(key);
|
||||||
int keylen = VARSIZE_ANY_EXHDR(key);
|
int keylen = VARSIZE_ANY_EXHDR(key);
|
||||||
Jsonb *out = palloc(VARSIZE(in));
|
|
||||||
JsonbParseState *state = NULL;
|
JsonbParseState *state = NULL;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
uint32 r;
|
uint32 r;
|
||||||
@ -3357,10 +3356,13 @@ jsonb_delete(PG_FUNCTION_ARGS)
|
|||||||
*res = NULL;
|
*res = NULL;
|
||||||
bool skipNested = false;
|
bool skipNested = false;
|
||||||
|
|
||||||
SET_VARSIZE(out, VARSIZE(in));
|
if (JB_ROOT_IS_SCALAR(in))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("cannot delete from scalar")));
|
||||||
|
|
||||||
if (JB_ROOT_COUNT(in) == 0)
|
if (JB_ROOT_COUNT(in) == 0)
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(in);
|
||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
@ -3382,13 +3384,9 @@ jsonb_delete(PG_FUNCTION_ARGS)
|
|||||||
res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res == NULL || (res->type == jbvArray && res->val.array.nElems == 0) ||
|
Assert(res != NULL);
|
||||||
(res->type == jbvObject && res->val.object.nPairs == 0))
|
|
||||||
SET_VARSIZE(out, VARHDRSZ);
|
|
||||||
else
|
|
||||||
out = JsonbValueToJsonb(res);
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(JsonbValueToJsonb(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3403,7 +3401,6 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Jsonb *in = PG_GETARG_JSONB(0);
|
Jsonb *in = PG_GETARG_JSONB(0);
|
||||||
int idx = PG_GETARG_INT32(1);
|
int idx = PG_GETARG_INT32(1);
|
||||||
Jsonb *out = palloc(VARSIZE(in));
|
|
||||||
JsonbParseState *state = NULL;
|
JsonbParseState *state = NULL;
|
||||||
JsonbIterator *it;
|
JsonbIterator *it;
|
||||||
uint32 r,
|
uint32 r,
|
||||||
@ -3412,11 +3409,13 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
JsonbValue v,
|
JsonbValue v,
|
||||||
*res = NULL;
|
*res = NULL;
|
||||||
|
|
||||||
|
if (JB_ROOT_IS_SCALAR(in))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("cannot delete from scalar")));
|
||||||
|
|
||||||
if (JB_ROOT_COUNT(in) == 0)
|
if (JB_ROOT_COUNT(in) == 0)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
@ -3435,10 +3434,7 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (idx >= n)
|
if (idx >= n)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
|
|
||||||
@ -3457,13 +3453,9 @@ jsonb_delete_idx(PG_FUNCTION_ARGS)
|
|||||||
res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
res = pushJsonbValue(&state, r, r < WJB_BEGIN_ARRAY ? &v : NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res == NULL || (res->type == jbvArray && res->val.array.nElems == 0) ||
|
Assert (res != NULL);
|
||||||
(res->type == jbvObject && res->val.object.nPairs == 0))
|
|
||||||
SET_VARSIZE(out, VARHDRSZ);
|
|
||||||
else
|
|
||||||
out = JsonbValueToJsonb(res);
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(JsonbValueToJsonb(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -3475,7 +3467,6 @@ jsonb_replace(PG_FUNCTION_ARGS)
|
|||||||
Jsonb *in = PG_GETARG_JSONB(0);
|
Jsonb *in = PG_GETARG_JSONB(0);
|
||||||
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
Jsonb *newval = PG_GETARG_JSONB(2);
|
Jsonb *newval = PG_GETARG_JSONB(2);
|
||||||
Jsonb *out = palloc(VARSIZE(in) + VARSIZE(newval));
|
|
||||||
JsonbValue *res = NULL;
|
JsonbValue *res = NULL;
|
||||||
Datum *path_elems;
|
Datum *path_elems;
|
||||||
bool *path_nulls;
|
bool *path_nulls;
|
||||||
@ -3488,31 +3479,27 @@ jsonb_replace(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
||||||
errmsg("wrong number of array subscripts")));
|
errmsg("wrong number of array subscripts")));
|
||||||
|
|
||||||
|
if (JB_ROOT_IS_SCALAR(in))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("cannot replace path in scalar")));
|
||||||
|
|
||||||
if (JB_ROOT_COUNT(in) == 0)
|
if (JB_ROOT_COUNT(in) == 0)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
deconstruct_array(path, TEXTOID, -1, false, 'i',
|
deconstruct_array(path, TEXTOID, -1, false, 'i',
|
||||||
&path_elems, &path_nulls, &path_len);
|
&path_elems, &path_nulls, &path_len);
|
||||||
|
|
||||||
if (path_len == 0)
|
if (path_len == 0)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, newval);
|
res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, newval);
|
||||||
|
|
||||||
if (res == NULL)
|
Assert (res != NULL);
|
||||||
SET_VARSIZE(out, VARHDRSZ);
|
|
||||||
else
|
|
||||||
out = JsonbValueToJsonb(res);
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(JsonbValueToJsonb(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -3524,7 +3511,6 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
Jsonb *in = PG_GETARG_JSONB(0);
|
Jsonb *in = PG_GETARG_JSONB(0);
|
||||||
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
|
||||||
Jsonb *out = palloc(VARSIZE(in));
|
|
||||||
JsonbValue *res = NULL;
|
JsonbValue *res = NULL;
|
||||||
Datum *path_elems;
|
Datum *path_elems;
|
||||||
bool *path_nulls;
|
bool *path_nulls;
|
||||||
@ -3537,31 +3523,27 @@ jsonb_delete_path(PG_FUNCTION_ARGS)
|
|||||||
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
||||||
errmsg("wrong number of array subscripts")));
|
errmsg("wrong number of array subscripts")));
|
||||||
|
|
||||||
|
if (JB_ROOT_IS_SCALAR(in))
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||||
|
errmsg("cannot delete path in scalar")));
|
||||||
|
|
||||||
if (JB_ROOT_COUNT(in) == 0)
|
if (JB_ROOT_COUNT(in) == 0)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
deconstruct_array(path, TEXTOID, -1, false, 'i',
|
deconstruct_array(path, TEXTOID, -1, false, 'i',
|
||||||
&path_elems, &path_nulls, &path_len);
|
&path_elems, &path_nulls, &path_len);
|
||||||
|
|
||||||
if (path_len == 0)
|
if (path_len == 0)
|
||||||
{
|
PG_RETURN_JSONB(in);
|
||||||
memcpy(out, in, VARSIZE(in));
|
|
||||||
PG_RETURN_POINTER(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
it = JsonbIteratorInit(&in->root);
|
it = JsonbIteratorInit(&in->root);
|
||||||
|
|
||||||
res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, NULL);
|
res = replacePath(&it, path_elems, path_nulls, path_len, &st, 0, NULL);
|
||||||
|
|
||||||
if (res == NULL)
|
Assert (res != NULL);
|
||||||
SET_VARSIZE(out, VARHDRSZ);
|
|
||||||
else
|
|
||||||
out = JsonbValueToJsonb(res);
|
|
||||||
|
|
||||||
PG_RETURN_POINTER(out);
|
PG_RETURN_JSONB(JsonbValueToJsonb(res));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3175,3 +3175,60 @@ select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{d
|
|||||||
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null}
|
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- empty structure and error conditions for delete and replace
|
||||||
|
select '"a"'::jsonb - 'a'; -- error
|
||||||
|
ERROR: cannot delete from scalar
|
||||||
|
select '{}'::jsonb - 'a';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - 'a';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '"a"'::jsonb - 1; -- error
|
||||||
|
ERROR: cannot delete from scalar
|
||||||
|
select '{}'::jsonb - 1 ;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - 1;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '"a"'::jsonb - '{a}'::text[]; -- error
|
||||||
|
ERROR: cannot delete path in scalar
|
||||||
|
select '{}'::jsonb - '{a}'::text[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - '{a}'::text[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select jsonb_replace('"a"','{a}','"b"'); --error
|
||||||
|
ERROR: cannot replace path in scalar
|
||||||
|
select jsonb_replace('{}','{a}','"b"');
|
||||||
|
jsonb_replace
|
||||||
|
---------------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select jsonb_replace('[]','{1}','"b"');
|
||||||
|
jsonb_replace
|
||||||
|
---------------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -3175,3 +3175,60 @@ select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{d
|
|||||||
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null}
|
{"a": 1, "b": [1, 2], "c": {"1": 2}, "d": {"1": [3]}, "n": null}
|
||||||
(1 row)
|
(1 row)
|
||||||
|
|
||||||
|
-- empty structure and error conditions for delete and replace
|
||||||
|
select '"a"'::jsonb - 'a'; -- error
|
||||||
|
ERROR: cannot delete from scalar
|
||||||
|
select '{}'::jsonb - 'a';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - 'a';
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '"a"'::jsonb - 1; -- error
|
||||||
|
ERROR: cannot delete from scalar
|
||||||
|
select '{}'::jsonb - 1 ;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - 1;
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '"a"'::jsonb - '{a}'::text[]; -- error
|
||||||
|
ERROR: cannot delete path in scalar
|
||||||
|
select '{}'::jsonb - '{a}'::text[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select '[]'::jsonb - '{a}'::text[];
|
||||||
|
?column?
|
||||||
|
----------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select jsonb_replace('"a"','{a}','"b"'); --error
|
||||||
|
ERROR: cannot replace path in scalar
|
||||||
|
select jsonb_replace('{}','{a}','"b"');
|
||||||
|
jsonb_replace
|
||||||
|
---------------
|
||||||
|
{}
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
select jsonb_replace('[]','{1}','"b"');
|
||||||
|
jsonb_replace
|
||||||
|
---------------
|
||||||
|
[]
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
@ -767,3 +767,19 @@ select jsonb_delete('{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'
|
|||||||
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{n}'::text[];
|
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{n}'::text[];
|
||||||
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{b,-1}'::text[];
|
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{b,-1}'::text[];
|
||||||
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{d,1,0}'::text[];
|
select '{"n":null, "a":1, "b":[1,2], "c":{"1":2}, "d":{"1":[2,3]}}'::jsonb - '{d,1,0}'::text[];
|
||||||
|
|
||||||
|
|
||||||
|
-- empty structure and error conditions for delete and replace
|
||||||
|
|
||||||
|
select '"a"'::jsonb - 'a'; -- error
|
||||||
|
select '{}'::jsonb - 'a';
|
||||||
|
select '[]'::jsonb - 'a';
|
||||||
|
select '"a"'::jsonb - 1; -- error
|
||||||
|
select '{}'::jsonb - 1 ;
|
||||||
|
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"');
|
||||||
|
Loading…
Reference in New Issue
Block a user