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

Commit a4f5a5e

Browse files
author
Nikita Glukhov
committed
Add jsonpath .min() and .max() item methods
1 parent bbb28da commit a4f5a5e

File tree

9 files changed

+187
-1
lines changed

9 files changed

+187
-1
lines changed

src/backend/utils/adt/jsonpath.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ flattenJsonPathParseItem(StringInfo buf, JsonPathParseItem *item,
226226
case jpiCeiling:
227227
case jpiDouble:
228228
case jpiKeyValue:
229+
case jpiMin:
230+
case jpiMax:
229231
break;
230232
case jpiSequence:
231233
{
@@ -691,6 +693,12 @@ printJsonPathItem(StringInfo buf, JsonPathItem *v, bool inKey, bool printBracket
691693
printJsonPathItem(buf, &elem, false, false);
692694
appendStringInfoChar(buf, ')');
693695
break;
696+
case jpiMin:
697+
appendBinaryStringInfo(buf, ".min()", 6);
698+
break;
699+
case jpiMax:
700+
appendBinaryStringInfo(buf, ".max()", 6);
701+
break;
694702
default:
695703
elog(ERROR, "Unknown jsonpath item type: %d", v->type);
696704
}
@@ -784,6 +792,8 @@ jspInitByBuffer(JsonPathItem *v, char *base, int32 pos)
784792
case jpiDouble:
785793
case jpiKeyValue:
786794
case jpiLast:
795+
case jpiMin:
796+
case jpiMax:
787797
break;
788798
case jpiKey:
789799
case jpiString:
@@ -929,7 +939,9 @@ jspGetNext(JsonPathItem *v, JsonPathItem *a)
929939
v->type == jpiReduce ||
930940
v->type == jpiFold ||
931941
v->type == jpiFoldl ||
932-
v->type == jpiFoldr
942+
v->type == jpiFoldr ||
943+
v->type == jpiMin ||
944+
v->type == jpiMax
933945
);
934946

935947
if (a)

src/backend/utils/adt/jsonpath_exec.c

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2423,6 +2423,73 @@ recursiveExecuteNoUnwrap(JsonPathExecContext *cxt, JsonPathItem *jsp,
24232423
res = recursiveExecuteNext(cxt, jsp, NULL, result, found, false);
24242424
}
24252425
break;
2426+
case jpiMin:
2427+
case jpiMax:
2428+
if (JsonbType(jb) != jbvArray)
2429+
{
2430+
if (cxt->lax)
2431+
res = recursiveExecuteNext(cxt, jsp, NULL, jb, found, true);
2432+
else
2433+
res = jperMakeError(ERRCODE_JSON_ARRAY_NOT_FOUND);
2434+
}
2435+
else
2436+
{
2437+
JsonbValue jbvElementBuf;
2438+
JsonbValue *jbvElement;
2439+
JsonbValue *jbvResult = NULL;
2440+
JsonbIterator *it = NULL;
2441+
JsonbIteratorToken tok;
2442+
int size = JsonbArraySize(jb);
2443+
int i;
2444+
JsonPathItemType cmpop =
2445+
jsp->type == jpiMax ? jpiGreater : jpiLess;
2446+
2447+
if (jb->type == jbvBinary)
2448+
{
2449+
jbvElement = &jbvElementBuf;
2450+
it = JsonbIteratorInit(jb->val.binary.data);
2451+
tok = JsonbIteratorNext(&it, &jbvElementBuf, false);
2452+
if (tok != WJB_BEGIN_ARRAY)
2453+
elog(ERROR, "unexpected jsonb token at the array start");
2454+
}
2455+
2456+
for (i = 0; i < size; i++)
2457+
{
2458+
if (it)
2459+
{
2460+
tok = JsonbIteratorNext(&it, jbvElement, true);
2461+
if (tok != WJB_ELEM)
2462+
break;
2463+
}
2464+
else
2465+
jbvElement = &jb->val.array.elems[i];
2466+
2467+
if (!i)
2468+
{
2469+
jbvResult = it ? copyJsonbValue(jbvElement) : jbvElement;
2470+
}
2471+
else
2472+
{
2473+
res = makeCompare(cmpop, jbvElement, jbvResult);
2474+
2475+
if (jperIsError(res))
2476+
return jperMakeError(ERRCODE_JSON_SCALAR_REQUIRED);
2477+
2478+
if (res == jperOk)
2479+
jbvResult = it ? copyJsonbValue(jbvElement) : jbvElement;
2480+
}
2481+
}
2482+
2483+
if (!jbvResult)
2484+
{
2485+
res = jperNotFound;
2486+
break;
2487+
}
2488+
2489+
res = recursiveExecuteNext(cxt, jsp, NULL, jbvResult, found,
2490+
false);
2491+
}
2492+
break;
24262493
default:
24272494
elog(ERROR,"2Wrong state: %d", jsp->type);
24282495
}

src/backend/utils/adt/jsonpath_gram.y

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ makeItemObject(List *fields)
287287
%token <str> ANY_P STRICT_P LAX_P LAST_P STARTS_P WITH_P LIKE_REGEX_P FLAG_P
288288
%token <str> ABS_P SIZE_P TYPE_P FLOOR_P DOUBLE_P CEILING_P DATETIME_P
289289
%token <str> KEYVALUE_P MAP_P REDUCE_P FOLD_P FOLDL_P FOLDR_P
290+
%token <str> MIN_P MAX_P
290291

291292
%type <result> result
292293

@@ -528,6 +529,8 @@ key_name:
528529
| FOLD_P
529530
| FOLDL_P
530531
| FOLDR_P
532+
| MIN_P
533+
| MAX_P
531534
;
532535

533536
method:
@@ -538,6 +541,8 @@ method:
538541
| DOUBLE_P { $$ = jpiDouble; }
539542
| CEILING_P { $$ = jpiCeiling; }
540543
| KEYVALUE_P { $$ = jpiKeyValue; }
544+
| MIN_P { $$ = jpiMin; }
545+
| MAX_P { $$ = jpiMax; }
541546
;
542547
%%
543548

src/backend/utils/adt/jsonpath_scan.l

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,8 @@ static keyword keywords[] = {
274274
{ 3, false, ABS_P, "abs"},
275275
{ 3, false, LAX_P, "lax"},
276276
{ 3, false, MAP_P, "map"},
277+
{ 3, false, MAX_P, "max"},
278+
{ 3, false, MIN_P, "min"},
277279
{ 4, false, FLAG_P, "flag"},
278280
{ 4, false, FOLD_P, "fold"},
279281
{ 4, false, LAST_P, "last"},

src/include/utils/jsonpath.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ typedef enum JsonPathItemType {
9393
jpiFold,
9494
jpiFoldl,
9595
jpiFoldr,
96+
jpiMin,
97+
jpiMax,
9698
} JsonPathItemType;
9799

98100
/* XQuery regex mode flags for LIKE_REGEX predicate */

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1742,6 +1742,75 @@ select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.fold($1 + $2.fold($1 + $2,
17421742
1428
17431743
(1 row)
17441744

1745+
-- extension: min/max item methods
1746+
select jsonb '1' @* 'strict $.min()';
1747+
ERROR: SQL/JSON array not found
1748+
select jsonb '1' @* 'lax $.min()';
1749+
?column?
1750+
----------
1751+
1
1752+
(1 row)
1753+
1754+
select jsonb '[]' @* '$.min()';
1755+
?column?
1756+
----------
1757+
(0 rows)
1758+
1759+
select jsonb '[]' @* '$.max()';
1760+
?column?
1761+
----------
1762+
(0 rows)
1763+
1764+
select jsonb '[null]' @* '$.min()';
1765+
?column?
1766+
----------
1767+
null
1768+
(1 row)
1769+
1770+
select jsonb '[null]' @* '$.max()';
1771+
?column?
1772+
----------
1773+
null
1774+
(1 row)
1775+
1776+
select jsonb '[1, 2, 3]' @* '$.min()';
1777+
?column?
1778+
----------
1779+
1
1780+
(1 row)
1781+
1782+
select jsonb '[1, 2, 3]' @* '$.max()';
1783+
?column?
1784+
----------
1785+
3
1786+
(1 row)
1787+
1788+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.min()';
1789+
?column?
1790+
----------
1791+
1
1792+
(1 row)
1793+
1794+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.max()';
1795+
?column?
1796+
----------
1797+
5
1798+
(1 row)
1799+
1800+
select jsonb '["aa", null, "a", "bbb"]' @* '$.min()';
1801+
?column?
1802+
----------
1803+
"a"
1804+
(1 row)
1805+
1806+
select jsonb '["aa", null, "a", "bbb"]' @* '$.max()';
1807+
?column?
1808+
----------
1809+
"bbb"
1810+
(1 row)
1811+
1812+
select jsonb '[1, null, "2"]' @* '$.max()';
1813+
ERROR: SQL/JSON scalar required
17451814
-- extension: path sequences
17461815
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
17471816
?column?

src/test/regress/expected/jsonpath.out

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,18 @@ select '$.fold($1 + $2 + @[1], 2 + 3)'::jsonpath;
413413
$.fold(($"1" + $"2") + @[1], 2 + 3)
414414
(1 row)
415415

416+
select '$.min().abs() + 5'::jsonpath;
417+
jsonpath
418+
---------------------
419+
($.min().abs() + 5)
420+
(1 row)
421+
422+
select '$.max().floor()'::jsonpath;
423+
jsonpath
424+
-----------------
425+
$.max().floor()
426+
(1 row)
427+
416428
select '$ ? (@ starts with "abc")'::jsonpath;
417429
jsonpath
418430
-------------------------

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,21 @@ select jsonb '[1, 2, 3]' @* '$.foldl([$1, $2], [])';
391391
select jsonb '[1, 2, 3]' @* '$.foldr([$2, $1], [])';
392392
select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.fold($1 + $2.fold($1 + $2, 100), 1000)';
393393

394+
-- extension: min/max item methods
395+
select jsonb '1' @* 'strict $.min()';
396+
select jsonb '1' @* 'lax $.min()';
397+
select jsonb '[]' @* '$.min()';
398+
select jsonb '[]' @* '$.max()';
399+
select jsonb '[null]' @* '$.min()';
400+
select jsonb '[null]' @* '$.max()';
401+
select jsonb '[1, 2, 3]' @* '$.min()';
402+
select jsonb '[1, 2, 3]' @* '$.max()';
403+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.min()';
404+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.max()';
405+
select jsonb '["aa", null, "a", "bbb"]' @* '$.min()';
406+
select jsonb '["aa", null, "a", "bbb"]' @* '$.max()';
407+
select jsonb '[1, null, "2"]' @* '$.max()';
408+
394409
-- extension: path sequences
395410
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
396411
select jsonb '[1,2,3,4,5]' @* 'lax 10, 20, $[*].a, 30';

src/test/regress/sql/jsonpath.sql

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ select '$.datetime()'::jsonpath;
7373
select '$.datetime("datetime template")'::jsonpath;
7474
select '$.reduce($1 + $2 + @[1])'::jsonpath;
7575
select '$.fold($1 + $2 + @[1], 2 + 3)'::jsonpath;
76+
select '$.min().abs() + 5'::jsonpath;
77+
select '$.max().floor()'::jsonpath;
7678

7779
select '$ ? (@ starts with "abc")'::jsonpath;
7880
select '$ ? (@ starts with $var)'::jsonpath;

0 commit comments

Comments
 (0)