Skip to content

Commit dfea49f

Browse files
author
Nikita Glukhov
committed
Add json array subscription support for expressions
1 parent f5d96c5 commit dfea49f

File tree

3 files changed

+178
-61
lines changed

3 files changed

+178
-61
lines changed

src/backend/utils/adt/jsonfuncs.c

Lines changed: 64 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -139,8 +139,10 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
139139
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
140140
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
141141
JsonbParseState **state);
142-
static Datum jsonb_set_element(Datum datum, text **path, int path_len, Datum sourceData, Oid source_type);
143-
static Datum jsonb_get_element(Datum datum, text **path, int path_len, bool *isNull);
142+
static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
143+
Datum sourceData, Oid source_type);
144+
static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
145+
bool *isnull, bool as_text);
144146
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
145147
bool *path_nulls, int path_len,
146148
JsonbParseState **st, int level, JsonbValue *newval, int op_type);
@@ -1175,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
11751177
{
11761178
Jsonb *jb = PG_GETARG_JSONB(0);
11771179
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
1178-
Jsonb *res;
11791180
Datum *pathtext;
11801181
bool *pathnulls;
1182+
bool isnull;
11811183
int npath;
1182-
int i;
1183-
bool have_object = false,
1184-
have_array = false;
1185-
JsonbValue *jbvp = NULL;
1186-
JsonbValue tv;
1187-
JsonbContainer *container;
1184+
Datum res;
11881185

11891186
/*
11901187
* If the array contains any null elements, return NULL, on the grounds
@@ -1199,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
11991196
deconstruct_array(path, TEXTOID, -1, false, 'i',
12001197
&pathtext, &pathnulls, &npath);
12011198

1202-
/* Identify whether we have object, array, or scalar at top-level */
1203-
container = &jb->root;
1199+
res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
1200+
1201+
if (isnull)
1202+
PG_RETURN_NULL();
1203+
else
1204+
PG_RETURN_DATUM(res);
1205+
}
12041206

1207+
static Datum
1208+
jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
1209+
{
1210+
Jsonb *res;
1211+
JsonbContainer *container = &jb->root;
1212+
JsonbValue *jbvp = NULL;
1213+
JsonbValue tv;
1214+
int i;
1215+
bool have_object = false,
1216+
have_array = false;
1217+
1218+
*isnull = false;
1219+
1220+
/* Identify whether we have object, array, or scalar at top-level */
12051221
if (JB_ROOT_IS_OBJECT(jb))
12061222
have_object = true;
12071223
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1226,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
12261242
{
12271243
if (as_text)
12281244
{
1229-
PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
1245+
return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
12301246
container,
12311247
VARSIZE(jb))));
12321248
}
12331249
else
12341250
{
12351251
/* not text mode - just hand back the jsonb */
1236-
PG_RETURN_JSONB(jb);
1252+
return JsonbGetDatum(jb);
12371253
}
12381254
}
12391255

@@ -1243,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
12431259
{
12441260
jbvp = findJsonbValueFromContainerLen(container,
12451261
JB_FOBJECT,
1246-
VARDATA_ANY(pathtext[i]),
1247-
VARSIZE_ANY_EXHDR(pathtext[i]));
1262+
VARDATA_ANY(path[i]),
1263+
VARSIZE_ANY_EXHDR(path[i]));
12481264
}
12491265
else if (have_array)
12501266
{
12511267
long lindex;
12521268
uint32 index;
1253-
char *indextext = TextDatumGetCString(pathtext[i]);
1269+
char *indextext = TextDatumGetCString(path[i]);
12541270
char *endptr;
12551271

12561272
errno = 0;
12571273
lindex = strtol(indextext, &endptr, 10);
12581274
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
12591275
lindex > INT_MAX || lindex < INT_MIN)
1260-
PG_RETURN_NULL();
1276+
{
1277+
*isnull = true;
1278+
return PointerGetDatum(NULL);
1279+
}
12611280

12621281
if (lindex >= 0)
12631282
{
@@ -1275,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
12751294
nelements = container->header & JB_CMASK;
12761295

12771296
if (-lindex > nelements)
1278-
PG_RETURN_NULL();
1297+
{
1298+
*isnull = true;
1299+
return PointerGetDatum(NULL);
1300+
}
12791301
else
12801302
index = nelements + lindex;
12811303
}
@@ -1285,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
12851307
else
12861308
{
12871309
/* scalar, extraction yields a null */
1288-
PG_RETURN_NULL();
1310+
*isnull = true;
1311+
return PointerGetDatum(NULL);
12891312
}
12901313

12911314
if (jbvp == NULL)
1292-
PG_RETURN_NULL();
1315+
{
1316+
*isnull = true;
1317+
return PointerGetDatum(NULL);
1318+
}
12931319
else if (i == npath - 1)
12941320
break;
12951321

@@ -1314,24 +1340,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
13141340
{
13151341
/* special-case outputs for string and null values */
13161342
if (jbvp->type == jbvString)
1317-
PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
1318-
jbvp->val.string.len));
1343+
return PointerGetDatum(
1344+
cstring_to_text_with_len(jbvp->val.string.val,
1345+
jbvp->val.string.len));
13191346
if (jbvp->type == jbvNull)
1320-
PG_RETURN_NULL();
1347+
{
1348+
*isnull = true;
1349+
return PointerGetDatum(NULL);
1350+
}
13211351
}
13221352

13231353
res = JsonbValueToJsonb(jbvp);
13241354

13251355
if (as_text)
13261356
{
1327-
PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
1357+
return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
13281358
&res->root,
13291359
VARSIZE(res))));
13301360
}
13311361
else
13321362
{
13331363
/* not text mode - just hand back the jsonb */
1334-
PG_RETURN_JSONB(res);
1364+
return JsonbGetDatum(res);
13351365
}
13361366
}
13371367

@@ -4009,29 +4039,6 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
40094039
}
40104040
}
40114041

4012-
Datum
4013-
jsonb_get_element(Datum jsonbdatum, text **path, int path_len, bool *is_null)
4014-
{
4015-
Jsonb *jb = DatumGetJsonb(jsonbdatum);
4016-
JsonbValue vbuf;
4017-
JsonbValue *v = JsonbToJsonbValue(jb, &vbuf);
4018-
int level;
4019-
4020-
for (level = 0; level < path_len; level++)
4021-
{
4022-
if (v->type != jbvBinary ||
4023-
!(v = findJsonbValueFromContainerLen(v->val.binary.data, JB_FOBJECT,
4024-
VARDATA_ANY(path[level]),
4025-
VARSIZE_ANY_EXHDR(path[level]))))
4026-
{
4027-
*is_null = true;
4028-
return (Datum) 0;
4029-
}
4030-
}
4031-
4032-
PG_RETURN_JSONB(JsonbValueToJsonb(v));
4033-
}
4034-
40354042
Datum
40364043
jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
40374044
{
@@ -4040,12 +4047,6 @@ jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
40404047
SubscriptionRef *jsonb_ref = (SubscriptionRef *) sbstate->xprstate.expr;
40414048
bool *is_null = sbsdata->isNull;
40424049
bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
4043-
text **path;
4044-
int i = 0;
4045-
4046-
path = (text **) palloc(sbsdata->indexprNumber * sizeof(text*));
4047-
for (i = 0; i < sbsdata->indexprNumber; i++)
4048-
path[i] = DatumGetTextP(sbsdata->upper[i]);
40494050

40504051
if (is_assignment)
40514052
{
@@ -4105,14 +4106,18 @@ jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
41054106
*is_null = false;
41064107
}
41074108

4108-
return jsonb_set_element(sbsdata->containerSource, path,
4109+
return jsonb_set_element(sbsdata->containerSource,
4110+
sbsdata->upper,
41094111
sbsdata->indexprNumber,
41104112
sourceData,
41114113
jsonb_ref->refelemtype);
41124114
}
41134115
else
4114-
return jsonb_get_element(sbsdata->containerSource, path,
4115-
sbsdata->indexprNumber, is_null);
4116+
return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
4117+
sbsdata->upper,
4118+
sbsdata->indexprNumber,
4119+
is_null,
4120+
false);
41164121
}
41174122

41184123
Datum
@@ -4180,7 +4185,7 @@ jsonb_subscription(PG_FUNCTION_ARGS)
41804185
}
41814186

41824187
Datum
4183-
jsonb_set_element(Datum jsonbdatum, text **path, int path_len,
4188+
jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
41844189
Datum sourceData, Oid source_type)
41854190
{
41864191
Jsonb *jb = DatumGetJsonb(jsonbdatum);
@@ -4197,7 +4202,7 @@ jsonb_set_element(Datum jsonbdatum, text **path, int path_len,
41974202

41984203
it = JsonbIteratorInit(&jb->root);
41994204

4200-
res = setPath(&it, (Datum *) path, path_nulls, path_len, &state, 0,
4205+
res = setPath(&it, path, path_nulls, path_len, &state, 0,
42014206
newval, JB_PATH_CREATE);
42024207

42034208
pfree(path_nulls);

src/test/regress/expected/jsonb.out

Lines changed: 97 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3442,12 +3442,90 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
34423442
ERROR: cannot replace existing key
34433443
HINT: Try using the function jsonb_set to replace key value.
34443444
-- jsonb subscript
3445+
select ('123'::jsonb)['a'];
3446+
jsonb
3447+
-------
3448+
3449+
(1 row)
3450+
3451+
select ('123'::jsonb)[0];
3452+
jsonb
3453+
-------
3454+
3455+
(1 row)
3456+
34453457
select ('{"a": 1}'::jsonb)['a'];
34463458
jsonb
34473459
-------
34483460
1
34493461
(1 row)
34503462

3463+
select ('{"a": 1}'::jsonb)[0];
3464+
jsonb
3465+
-------
3466+
3467+
(1 row)
3468+
3469+
select ('{"a": 1}'::jsonb)['not_exist'];
3470+
jsonb
3471+
-------
3472+
3473+
(1 row)
3474+
3475+
select ('[1, "2", null]'::jsonb)['a'];
3476+
jsonb
3477+
-------
3478+
3479+
(1 row)
3480+
3481+
select ('[1, "2", null]'::jsonb)[0];
3482+
jsonb
3483+
-------
3484+
1
3485+
(1 row)
3486+
3487+
select ('[1, "2", null]'::jsonb)['1'];
3488+
jsonb
3489+
-------
3490+
"2"
3491+
(1 row)
3492+
3493+
select ('[1, "2", null]'::jsonb)[1.0];
3494+
jsonb
3495+
-------
3496+
3497+
(1 row)
3498+
3499+
select ('[1, "2", null]'::jsonb)[2];
3500+
jsonb
3501+
-------
3502+
null
3503+
(1 row)
3504+
3505+
select ('[1, "2", null]'::jsonb)[3];
3506+
jsonb
3507+
-------
3508+
3509+
(1 row)
3510+
3511+
select ('[1, "2", null]'::jsonb)[-2];
3512+
jsonb
3513+
-------
3514+
"2"
3515+
(1 row)
3516+
3517+
select ('[1, "2", null]'::jsonb)[1]['a'];
3518+
jsonb
3519+
-------
3520+
3521+
(1 row)
3522+
3523+
select ('[1, "2", null]'::jsonb)[1][0];
3524+
jsonb
3525+
-------
3526+
3527+
(1 row)
3528+
34513529
select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
34523530
jsonb
34533531
-------
@@ -3460,7 +3538,13 @@ select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
34603538
[1, 2, 3]
34613539
(1 row)
34623540

3463-
select ('{"a": 1}'::jsonb)['not_exist'];
3541+
select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
3542+
jsonb
3543+
-------
3544+
2
3545+
(1 row)
3546+
3547+
select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
34643548
jsonb
34653549
-------
34663550

@@ -3484,6 +3568,18 @@ select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'
34843568

34853569
(1 row)
34863570

3571+
select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
3572+
jsonb
3573+
-----------------------
3574+
["aaa", "bbb", "ccc"]
3575+
(1 row)
3576+
3577+
select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
3578+
jsonb
3579+
-------
3580+
"ccc"
3581+
(1 row)
3582+
34873583
create TEMP TABLE test_jsonb_subscript (
34883584
id int,
34893585
test_json jsonb

0 commit comments

Comments
 (0)