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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy