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

Commit ffdfbaa

Browse files
author
Nikita Glukhov
committed
Add jsonpath .min() and .max() item methods
1 parent 5cf7282 commit ffdfbaa

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, "unrecognized jsonpath item type: %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

@@ -529,6 +530,8 @@ key_name:
529530
| FOLD_P
530531
| FOLDL_P
531532
| FOLDR_P
533+
| MIN_P
534+
| MAX_P
532535
;
533536

534537
method:
@@ -539,6 +542,8 @@ method:
539542
| DOUBLE_P { $$ = jpiDouble; }
540543
| CEILING_P { $$ = jpiCeiling; }
541544
| KEYVALUE_P { $$ = jpiKeyValue; }
545+
| MIN_P { $$ = jpiMin; }
546+
| MAX_P { $$ = jpiMax; }
542547
;
543548
%%
544549

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
@@ -1736,6 +1736,75 @@ select jsonb '[[1, 2], [3, 4, 5], [], [6, 7]]' @* '$.fold($1 + $2.fold($1 + $2,
17361736
1428
17371737
(1 row)
17381738

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

src/test/regress/expected/jsonpath.out

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

404+
select '$.min().abs() + 5'::jsonpath;
405+
jsonpath
406+
---------------------
407+
($.min().abs() + 5)
408+
(1 row)
409+
410+
select '$.max().floor()'::jsonpath;
411+
jsonpath
412+
-----------------
413+
$.max().floor()
414+
(1 row)
415+
404416
select '$ ? (@ starts with "abc")'::jsonpath;
405417
jsonpath
406418
-------------------------

src/test/regress/sql/jsonb_jsonpath.sql

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

393+
-- extension: min/max item methods
394+
select jsonb '1' @* 'strict $.min()';
395+
select jsonb '1' @* 'lax $.min()';
396+
select jsonb '[]' @* '$.min()';
397+
select jsonb '[]' @* '$.max()';
398+
select jsonb '[null]' @* '$.min()';
399+
select jsonb '[null]' @* '$.max()';
400+
select jsonb '[1, 2, 3]' @* '$.min()';
401+
select jsonb '[1, 2, 3]' @* '$.max()';
402+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.min()';
403+
select jsonb '[2, 3, 5, null, 1, 4, null]' @* '$.max()';
404+
select jsonb '["aa", null, "a", "bbb"]' @* '$.min()';
405+
select jsonb '["aa", null, "a", "bbb"]' @* '$.max()';
406+
select jsonb '[1, null, "2"]' @* '$.max()';
407+
393408
-- extension: path sequences
394409
select jsonb '[1,2,3,4,5]' @* '10, 20, $[*], 30';
395410
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
@@ -71,6 +71,8 @@ select '$.datetime()'::jsonpath;
7171
select '$.datetime("datetime template")'::jsonpath;
7272
select '$.reduce($1 + $2 + @[1])'::jsonpath;
7373
select '$.fold($1 + $2 + @[1], 2 + 3)'::jsonpath;
74+
select '$.min().abs() + 5'::jsonpath;
75+
select '$.max().floor()'::jsonpath;
7476

7577
select '$ ? (@ starts with "abc")'::jsonpath;
7678
select '$ ? (@ starts with $var)'::jsonpath;

0 commit comments

Comments
 (0)