Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a83586b

Browse files
committed
Add a non-strict version of jsonb_set
jsonb_set_lax() is the same as jsonb_set, except that it takes and extra argument that specifies what to do if the value argument is NULL. The default is 'use_json_null'. Other possibilities are 'raise_exception', 'return_target' and 'delete_key', all these behaviours having been suggested as reasonable by various users. Discussion: https://postgr.es/m/375873e2-c957-3a8d-64f9-26c43c2b16e7@2ndQuadrant.com Reviewed by: Pavel Stehule
1 parent f7cd589 commit a83586b

File tree

6 files changed

+176
-0
lines changed

6 files changed

+176
-0
lines changed

doc/src/sgml/func.sgml

+23
Original file line numberDiff line numberDiff line change
@@ -12231,6 +12231,9 @@ table2-mapping
1223112231
<indexterm>
1223212232
<primary>jsonb_set</primary>
1223312233
</indexterm>
12234+
<indexterm>
12235+
<primary>jsonb_set_lax</primary>
12236+
</indexterm>
1223412237
<indexterm>
1223512238
<primary>jsonb_insert</primary>
1223612239
</indexterm>
@@ -12545,6 +12548,26 @@ table2-mapping
1254512548
</para><para><literal>[{"f1": 1, "f2": null, "f3": [2, 3, 4]}, 2]</literal>
1254612549
</para></entry>
1254712550
</row>
12551+
<row>
12552+
<entry><para><literal>jsonb_set_lax(target jsonb, path text[], new_value jsonb <optional>, create_missing boolean</optional> <optional>, null_value_treatment text</optional>)</literal>
12553+
</para></entry>
12554+
<entry><para><type>jsonb</type></para></entry>
12555+
<entry>
12556+
If <replaceable>new_value</replaceable> is not <literal>null</literal>,
12557+
behaves identically to <literal>jsonb_set</literal>. Otherwise behaves
12558+
according to the value of <replaceable>null_value_treatment</replaceable>
12559+
which must be one of <literal>'raise_exception'</literal>,
12560+
<literal>'use_json_null'</literal>, <literal>'delete_key'</literal>, or
12561+
<literal>'return_target'</literal>. The default is
12562+
<literal>'use_json_null'</literal>.
12563+
</entry>
12564+
<entry><para><literal>jsonb_set_lax('[{"f1":1,"f2":null},2,null,3]', '{0,f1}',null)</literal>
12565+
</para><para><literal>jsonb_set_lax('[{"f1":99,"f2":null},2]', '{0,f3}',null, true, 'return_target')</literal>
12566+
</para></entry>
12567+
<entry><para><literal>[{"f1":null,"f2":null},2,null,3]</literal>
12568+
</para><para><literal>[{"f1": 99, "f2": null}, 2]</literal>
12569+
</para></entry>
12570+
</row>
1254812571
<row>
1254912572
<entry>
1255012573
<para><literal>

src/backend/catalog/system_views.sql

+9
Original file line numberDiff line numberDiff line change
@@ -1264,6 +1264,15 @@ LANGUAGE INTERNAL
12641264
STRICT IMMUTABLE PARALLEL SAFE
12651265
AS 'jsonb_set';
12661266

1267+
CREATE OR REPLACE FUNCTION
1268+
jsonb_set_lax(jsonb_in jsonb, path text[] , replacement jsonb,
1269+
create_if_missing boolean DEFAULT true,
1270+
null_value_treatment text DEFAULT 'use_json_null')
1271+
RETURNS jsonb
1272+
LANGUAGE INTERNAL
1273+
CALLED ON NULL INPUT IMMUTABLE PARALLEL SAFE
1274+
AS 'jsonb_set_lax';
1275+
12671276
CREATE OR REPLACE FUNCTION
12681277
parse_ident(str text, strict boolean DEFAULT true)
12691278
RETURNS text[]

src/backend/utils/adt/jsonfuncs.c

+64
Original file line numberDiff line numberDiff line change
@@ -4395,6 +4395,70 @@ jsonb_set(PG_FUNCTION_ARGS)
43954395
}
43964396

43974397

4398+
/*
4399+
* SQL function jsonb_set_lax(jsonb, text[], jsonb, boolean, text)
4400+
*/
4401+
Datum
4402+
jsonb_set_lax(PG_FUNCTION_ARGS)
4403+
{
4404+
/* Jsonb *in = PG_GETARG_JSONB_P(0); */
4405+
/* ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); */
4406+
/* Jsonb *newval = PG_GETARG_JSONB_P(2); */
4407+
/* bool create = PG_GETARG_BOOL(3); */
4408+
text *handle_null;
4409+
char *handle_val;
4410+
4411+
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(3))
4412+
PG_RETURN_NULL();
4413+
4414+
/* could happen if they pass in an explicit NULL */
4415+
if (PG_ARGISNULL(4))
4416+
ereport(ERROR,
4417+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4418+
errmsg("need delete_key, return_target, use_json_null, or raise_exception")));
4419+
4420+
/* if the new value isn't an SQL NULL just call jsonb_set */
4421+
if (! PG_ARGISNULL(2))
4422+
return jsonb_set(fcinfo);
4423+
4424+
handle_null = PG_GETARG_TEXT_P(4);
4425+
handle_val = text_to_cstring(handle_null);
4426+
4427+
if (strcmp(handle_val,"raise_exception") == 0)
4428+
{
4429+
ereport(ERROR,
4430+
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
4431+
errmsg("NULL is not allowed"),
4432+
errdetail("exception raised due to \"null_value_treatment => 'raise_exception'\""),
4433+
errhint("to avoid, either change the null_value_treatment argument or ensure that an SQL NULL is not used")));
4434+
}
4435+
else if (strcmp(handle_val, "use_json_null") == 0)
4436+
{
4437+
Datum newval;
4438+
4439+
newval = DirectFunctionCall1(jsonb_in, CStringGetDatum("null"));
4440+
4441+
fcinfo->args[2].value = newval;
4442+
fcinfo->args[2].isnull = false;
4443+
return jsonb_set(fcinfo);
4444+
}
4445+
else if (strcmp(handle_val, "delete_key") == 0)
4446+
{
4447+
return jsonb_delete_path(fcinfo);
4448+
}
4449+
else if (strcmp(handle_val, "return_target") == 0)
4450+
{
4451+
Jsonb *in = PG_GETARG_JSONB_P(0);
4452+
PG_RETURN_JSONB_P(in);
4453+
}
4454+
else
4455+
{
4456+
ereport(ERROR,
4457+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
4458+
errmsg("need delete_key, return_target, use_json_null, or raise_exception")));
4459+
}
4460+
}
4461+
43984462
/*
43994463
* SQL function jsonb_delete_path(jsonb, text[])
44004464
*/

src/include/catalog/pg_proc.dat

+3
Original file line numberDiff line numberDiff line change
@@ -9305,6 +9305,9 @@
93059305
{ oid => '3304',
93069306
proname => 'jsonb_delete_path', prorettype => 'jsonb',
93079307
proargtypes => 'jsonb _text', prosrc => 'jsonb_delete_path' },
9308+
{ oid => '8945', descr => 'Set part of a jsonb, handle NULL value',
9309+
proname => 'jsonb_set_lax', prorettype => 'jsonb', proisstrict => 'f',
9310+
proargtypes => 'jsonb _text jsonb bool text', prosrc => 'jsonb_set_lax' },
93089311
{ oid => '3305', descr => 'Set part of a jsonb',
93099312
proname => 'jsonb_set', prorettype => 'jsonb',
93109313
proargtypes => 'jsonb _text jsonb bool', prosrc => 'jsonb_set' },

src/test/regress/expected/jsonb.out

+57
Original file line numberDiff line numberDiff line change
@@ -4511,6 +4511,63 @@ select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"'
45114511
ERROR: path element at position 3 is not an integer: "non_integer"
45124512
select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"');
45134513
ERROR: path element at position 3 is null
4514+
-- jsonb_set_lax
4515+
\pset null NULL
4516+
-- pass though non nulls to jsonb_set
4517+
select jsonb_set_lax('{"a":1,"b":2}','{b}','5') ;
4518+
jsonb_set_lax
4519+
------------------
4520+
{"a": 1, "b": 5}
4521+
(1 row)
4522+
4523+
select jsonb_set_lax('{"a":1,"b":2}','{d}','6', true) ;
4524+
jsonb_set_lax
4525+
--------------------------
4526+
{"a": 1, "b": 2, "d": 6}
4527+
(1 row)
4528+
4529+
-- using the default treatment
4530+
select jsonb_set_lax('{"a":1,"b":2}','{b}',null);
4531+
jsonb_set_lax
4532+
---------------------
4533+
{"a": 1, "b": null}
4534+
(1 row)
4535+
4536+
select jsonb_set_lax('{"a":1,"b":2}','{d}',null,true);
4537+
jsonb_set_lax
4538+
-----------------------------
4539+
{"a": 1, "b": 2, "d": null}
4540+
(1 row)
4541+
4542+
-- errors
4543+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, true, null);
4544+
ERROR: need delete_key, return_target, use_json_null, or raise_exception
4545+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, true, 'no_such_treatment');
4546+
ERROR: need delete_key, return_target, use_json_null, or raise_exception
4547+
-- explicit treatments
4548+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'raise_exception') as raise_exception;
4549+
ERROR: NULL is not allowed
4550+
DETAIL: exception raised due to "null_value_treatment => 'raise_exception'"
4551+
HINT: to avoid, either change the null_value_treatment argument or ensure that an SQL NULL is not used
4552+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'return_target') as return_target;
4553+
return_target
4554+
------------------
4555+
{"a": 1, "b": 2}
4556+
(1 row)
4557+
4558+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'delete_key') as delete_key;
4559+
delete_key
4560+
------------
4561+
{"a": 1}
4562+
(1 row)
4563+
4564+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'use_json_null') as use_json_null;
4565+
use_json_null
4566+
---------------------
4567+
{"a": 1, "b": null}
4568+
(1 row)
4569+
4570+
\pset null
45144571
-- jsonb_insert
45154572
select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"');
45164573
jsonb_insert

src/test/regress/sql/jsonb.sql

+20
Original file line numberDiff line numberDiff line change
@@ -1153,6 +1153,26 @@ select jsonb_set('{"a": [1, 2, 3]}', '{a, non_integer}', '"new_value"');
11531153
select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, non_integer}', '"new_value"');
11541154
select jsonb_set('{"a": {"b": [1, 2, 3]}}', '{a, b, NULL}', '"new_value"');
11551155

1156+
-- jsonb_set_lax
1157+
1158+
\pset null NULL
1159+
1160+
-- pass though non nulls to jsonb_set
1161+
select jsonb_set_lax('{"a":1,"b":2}','{b}','5') ;
1162+
select jsonb_set_lax('{"a":1,"b":2}','{d}','6', true) ;
1163+
-- using the default treatment
1164+
select jsonb_set_lax('{"a":1,"b":2}','{b}',null);
1165+
select jsonb_set_lax('{"a":1,"b":2}','{d}',null,true);
1166+
-- errors
1167+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, true, null);
1168+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, true, 'no_such_treatment');
1169+
-- explicit treatments
1170+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'raise_exception') as raise_exception;
1171+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'return_target') as return_target;
1172+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'delete_key') as delete_key;
1173+
select jsonb_set_lax('{"a":1,"b":2}', '{b}', null, null_value_treatment => 'use_json_null') as use_json_null;
1174+
1175+
\pset null
11561176

11571177
-- jsonb_insert
11581178
select jsonb_insert('{"a": [0,1,2]}', '{a, 1}', '"new_value"');

0 commit comments

Comments
 (0)