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

Commit feb6aa8

Browse files
author
Nikita Glukhov
committed
Add tests for custom jsonpath item methods and functions
1 parent 02077ef commit feb6aa8

File tree

5 files changed

+286
-1
lines changed

5 files changed

+286
-1
lines changed

src/test/regress/expected/jsonb_jsonpath.out

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2695,3 +2695,65 @@ select jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.(a[0
26952695
{"a": [{"b": 1}, {"b": 2}]}
26962696
(1 row)
26972697

2698+
-- extension: user-defined functions and item methods
2699+
-- array_map(jsonpath_fcxt, jsonb) function created in create_function_1.sql
2700+
-- array_map() item method
2701+
select jsonb_path_query('1', 'strict $.array_map(x => x + 10)');
2702+
ERROR: SQL/JSON array not found
2703+
DETAIL: jsonpath .array_map() is applied to not an array
2704+
select jsonb_path_query('1', 'lax $.array_map(x => x + 10)');
2705+
jsonb_path_query
2706+
------------------
2707+
11
2708+
(1 row)
2709+
2710+
select jsonb_path_query('[1, 2, 3]', '$.array_map(x => x + 10)');
2711+
jsonb_path_query
2712+
------------------
2713+
[11, 12, 13]
2714+
(1 row)
2715+
2716+
select jsonb_path_query('[1, 2, 3]', '$.array_map(x => x + 10)[*]');
2717+
jsonb_path_query
2718+
------------------
2719+
11
2720+
12
2721+
13
2722+
(3 rows)
2723+
2724+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', '$.array_map(a => a.array_map(x => x + 10))');
2725+
jsonb_path_query
2726+
----------------------------------------
2727+
[[11, 12], [13, 14, 15], [], [16, 17]]
2728+
(1 row)
2729+
2730+
-- array_map() function
2731+
select jsonb_path_query('1', 'strict array_map($, x => x + 10)');
2732+
jsonb_path_query
2733+
------------------
2734+
11
2735+
(1 row)
2736+
2737+
select jsonb_path_query('1', 'lax array_map($, x => x + 10)');
2738+
jsonb_path_query
2739+
------------------
2740+
11
2741+
(1 row)
2742+
2743+
select jsonb_path_query('[3, 4, 5]', 'array_map($[*], (x, i) => x + i * 10)');
2744+
jsonb_path_query
2745+
------------------
2746+
3
2747+
14
2748+
25
2749+
(3 rows)
2750+
2751+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'array_map($[*], x => [array_map(x[*], x => x + 10)])');
2752+
jsonb_path_query
2753+
------------------
2754+
[11, 12]
2755+
[13, 14, 15]
2756+
[]
2757+
[16, 17]
2758+
(4 rows)
2759+

src/test/regress/input/create_function_1.source

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ CREATE FUNCTION test_support_func(internal)
7373
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
7474
LANGUAGE C STRICT;
7575

76+
-- Tests creating a custom jsonpath item method
77+
CREATE FUNCTION array_map(jsonpath_fcxt)
78+
RETURNS int8
79+
AS '@libdir@/regress@DLSUFFIX@', 'jsonpath_array_map'
80+
LANGUAGE C;
81+
7682
-- Things that shouldn't work:
7783

7884
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL

src/test/regress/output/create_function_1.source

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ CREATE FUNCTION test_support_func(internal)
6464
RETURNS internal
6565
AS '@libdir@/regress@DLSUFFIX@', 'test_support_func'
6666
LANGUAGE C STRICT;
67+
-- Tests creating a custom jsonpath item method
68+
CREATE FUNCTION array_map(jsonpath_fcxt)
69+
RETURNS int8
70+
AS '@libdir@/regress@DLSUFFIX@', 'jsonpath_array_map'
71+
LANGUAGE C;
6772
-- Things that shouldn't work:
6873
CREATE FUNCTION test1 (int) RETURNS int LANGUAGE SQL
6974
AS 'SELECT ''not an integer'';';

src/test/regress/regress.c

Lines changed: 198 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
#include "optimizer/plancat.h"
3636
#include "port/atomics.h"
3737
#include "utils/builtins.h"
38-
#include "utils/geo_decls.h"
38+
#include "utils/geo_decls.h"
39+
#include "utils/jsonpath.h"
3940
#include "utils/rel.h"
4041
#include "utils/typcache.h"
4142
#include "utils/memutils.h"
@@ -940,3 +941,199 @@ test_support_func(PG_FUNCTION_ARGS)
940941

941942
PG_RETURN_POINTER(ret);
942943
}
944+
945+
PG_FUNCTION_INFO_V1(jsonpath_array_map);
946+
Datum
947+
jsonpath_array_map(PG_FUNCTION_ARGS)
948+
{
949+
JsonPathFuncContext *fcxt = (JsonPathFuncContext *) PG_GETARG_POINTER(0);
950+
JsonPathExecContext *cxt = fcxt->cxt;
951+
JsonItem *jb = fcxt->item;
952+
JsonPathItem *func = &fcxt->args[jb ? 0 : 1];
953+
void **funccache = &fcxt->argscache[jb ? 0 : 1];
954+
JsonPathExecResult res;
955+
JsonItem *args[3];
956+
JsonItem jbvidx;
957+
int index = 0;
958+
int nargs = 1;
959+
960+
if (fcxt->nargs != (jb ? 1 : 2))
961+
{
962+
if (jspThrowErrors(cxt))
963+
ereport(ERROR,
964+
(errcode(ERRCODE_JSON_SCALAR_REQUIRED),
965+
errmsg(ERRMSG_JSON_SCALAR_REQUIRED),
966+
errdetail("jsonpath .array_map() requires %d arguments "
967+
"but given %d", jb ? 1 : 2, fcxt->nargs)));
968+
969+
PG_RETURN_INT64(jperError);
970+
}
971+
972+
if (func->type == jpiLambda && func->content.lambda.nparams > 1)
973+
{
974+
args[nargs++] = &jbvidx;
975+
JsonItemGetType(&jbvidx) = jbvNumeric;
976+
}
977+
978+
if (!jb)
979+
{
980+
JsonValueList items = {0};
981+
JsonValueListIterator iter;
982+
JsonItem *item;
983+
984+
res = jspExecuteItem(cxt, &fcxt->args[0], fcxt->jb, &items);
985+
986+
if (jperIsError(res))
987+
PG_RETURN_INT64(res);
988+
989+
JsonValueListInitIterator(&items, &iter);
990+
991+
while ((item = JsonValueListNext(&items, &iter)))
992+
{
993+
JsonValueList reslist = {0};
994+
995+
args[0] = item;
996+
997+
if (nargs > 1)
998+
{
999+
JsonItemNumeric(&jbvidx) = DatumGetNumeric(
1000+
DirectFunctionCall1(int4_numeric, Int32GetDatum(index)));
1001+
index++;
1002+
}
1003+
1004+
res = jspExecuteLambda(cxt, func, fcxt->jb, &reslist,
1005+
args, nargs, funccache);
1006+
1007+
if (jperIsError(res))
1008+
PG_RETURN_INT64(res);
1009+
1010+
if (JsonValueListLength(&reslist) != 1)
1011+
{
1012+
if (jspThrowErrors(cxt))
1013+
ereport(ERROR,
1014+
(errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1015+
errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1016+
errdetail("lambda expression in .array_map() "
1017+
"should return singleton item")));
1018+
1019+
PG_RETURN_INT64(jperError);
1020+
}
1021+
1022+
JsonValueListAppend(fcxt->result, JsonValueListHead(&reslist));
1023+
}
1024+
}
1025+
else if (JsonbType(jb) != jbvArray)
1026+
{
1027+
JsonValueList reslist = {0};
1028+
1029+
if (!jspAutoWrap(cxt))
1030+
{
1031+
if (jspThrowErrors(cxt))
1032+
ereport(ERROR,
1033+
(errcode(ERRCODE_JSON_ARRAY_NOT_FOUND),
1034+
errmsg(ERRMSG_JSON_ARRAY_NOT_FOUND),
1035+
errdetail("jsonpath .array_map() is applied to "
1036+
"not an array")));
1037+
1038+
PG_RETURN_INT64(jperError);
1039+
}
1040+
1041+
args[0] = jb;
1042+
1043+
if (nargs > 1)
1044+
JsonItemNumeric(&jbvidx) = DatumGetNumeric(
1045+
DirectFunctionCall1(int4_numeric, Int32GetDatum(0)));
1046+
1047+
res = jspExecuteLambda(cxt, func, jb, &reslist, args, nargs, funccache);
1048+
1049+
if (jperIsError(res))
1050+
PG_RETURN_INT64(res);
1051+
1052+
if (JsonValueListLength(&reslist) != 1)
1053+
{
1054+
if (jspThrowErrors(cxt))
1055+
ereport(ERROR,
1056+
(errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1057+
errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1058+
errdetail("lambda expression in jsonpath .array_map() "
1059+
"should return singleton item")));
1060+
1061+
PG_RETURN_INT64(jperError);
1062+
}
1063+
1064+
JsonValueListAppend(fcxt->result, JsonValueListHead(&reslist));
1065+
}
1066+
else
1067+
{
1068+
JsonbValue elembuf;
1069+
JsonbValue *elem;
1070+
JsonxIterator it;
1071+
JsonbIteratorToken tok;
1072+
JsonValueList result = {0};
1073+
int size = JsonxArraySize(jb, cxt->isJsonb);
1074+
int i;
1075+
bool isBinary = JsonItemIsBinary(jb);
1076+
1077+
if (isBinary && size > 0)
1078+
{
1079+
elem = &elembuf;
1080+
JsonxIteratorInit(&it, JsonItemBinary(jb).data, cxt->isJsonb);
1081+
tok = JsonxIteratorNext(&it, &elembuf, false);
1082+
if (tok != WJB_BEGIN_ARRAY)
1083+
elog(ERROR, "unexpected jsonb token at the array start");
1084+
}
1085+
1086+
if (nargs > 1)
1087+
{
1088+
nargs = 3;
1089+
args[2] = jb;
1090+
}
1091+
1092+
for (i = 0; i < size; i++)
1093+
{
1094+
JsonValueList reslist = {0};
1095+
JsonItem elemjsi;
1096+
1097+
if (isBinary)
1098+
{
1099+
tok = JsonxIteratorNext(&it, elem, true);
1100+
if (tok != WJB_ELEM)
1101+
break;
1102+
}
1103+
else
1104+
elem = &JsonItemArray(jb).elems[i];
1105+
1106+
args[0] = JsonbValueToJsonItem(elem, &elemjsi);
1107+
1108+
if (nargs > 1)
1109+
{
1110+
JsonItemNumeric(&jbvidx) = DatumGetNumeric(
1111+
DirectFunctionCall1(int4_numeric, Int32GetDatum(index)));
1112+
index++;
1113+
}
1114+
1115+
res = jspExecuteLambda(cxt, func, jb, &reslist, args, nargs, funccache);
1116+
1117+
if (jperIsError(res))
1118+
PG_RETURN_INT64(res);
1119+
1120+
if (JsonValueListLength(&reslist) != 1)
1121+
{
1122+
if (jspThrowErrors(cxt))
1123+
ereport(ERROR,
1124+
(errcode(ERRCODE_SINGLETON_JSON_ITEM_REQUIRED),
1125+
errmsg(ERRMSG_SINGLETON_JSON_ITEM_REQUIRED),
1126+
errdetail("lambda expression in jsonpath .array_map() "
1127+
"should return singleton item")));
1128+
1129+
PG_RETURN_INT64(jperError);
1130+
}
1131+
1132+
JsonValueListConcat(&result, reslist);
1133+
}
1134+
1135+
JsonAppendWrappedItems(fcxt->result, &result, cxt->isJsonb);
1136+
}
1137+
1138+
PG_RETURN_INT64(jperOk);
1139+
}

src/test/regress/sql/jsonb_jsonpath.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,3 +607,18 @@ select jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.(a)[
607607
select jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.a[*].(b)');
608608
select jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.(a)[*].(b)');
609609
select jsonb_path_query('{"a": [{"b": 1, "c": 10}, {"b": 2, "c": 20}]}', '$.(a[0 to 1].b)');
610+
611+
-- extension: user-defined functions and item methods
612+
-- array_map(jsonpath_fcxt, jsonb) function created in create_function_1.sql
613+
-- array_map() item method
614+
select jsonb_path_query('1', 'strict $.array_map(x => x + 10)');
615+
select jsonb_path_query('1', 'lax $.array_map(x => x + 10)');
616+
select jsonb_path_query('[1, 2, 3]', '$.array_map(x => x + 10)');
617+
select jsonb_path_query('[1, 2, 3]', '$.array_map(x => x + 10)[*]');
618+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', '$.array_map(a => a.array_map(x => x + 10))');
619+
620+
-- array_map() function
621+
select jsonb_path_query('1', 'strict array_map($, x => x + 10)');
622+
select jsonb_path_query('1', 'lax array_map($, x => x + 10)');
623+
select jsonb_path_query('[3, 4, 5]', 'array_map($[*], (x, i) => x + i * 10)');
624+
select jsonb_path_query('[[1, 2], [3, 4, 5], [], [6, 7]]', 'array_map($[*], x => [array_map(x[*], x => x + 10)])');

0 commit comments

Comments
 (0)