@@ -144,6 +144,8 @@ typedef struct JsonPathExecContext
144
144
* evaluation */
145
145
int lastGeneratedObjectId ; /* "id" counter for .keyvalue()
146
146
* evaluation */
147
+ void * * cache ;
148
+ MemoryContext cache_mcxt ;
147
149
int innermostArraySize ; /* for LAST array index evaluation */
148
150
bool laxMode ; /* true for "lax" mode, false for "strict"
149
151
* mode */
@@ -211,6 +213,12 @@ typedef struct JsonPathUserFuncContext
211
213
bool silent ; /* error suppression flag */
212
214
} JsonPathUserFuncContext ;
213
215
216
+ typedef struct JsonpathQueryContext
217
+ {
218
+ void * cache ; /* jsonpath executor cache */
219
+ FuncCallContext * srfcxt ; /* SRF context */
220
+ } JsonpathQueryContext ;
221
+
214
222
/* Structures for JSON_TABLE execution */
215
223
typedef struct JsonTableScanState JsonTableScanState ;
216
224
typedef struct JsonTableJoinState JsonTableJoinState ;
@@ -297,13 +305,16 @@ static float8 float8_mod_error(float8 val1, float8 val2, bool *error);
297
305
298
306
static void freeUserFuncContext (JsonPathUserFuncContext * cxt );
299
307
static JsonPathExecResult executeUserFunc (FunctionCallInfo fcinfo ,
300
- JsonPathUserFuncContext * cxt , bool isJsonb , bool copy );
308
+ JsonPathUserFuncContext * cxt , bool isJsonb , bool copy ,
309
+ void * * fn_extra );
301
310
302
311
static JsonPathExecResult executeJsonPath (JsonPath * path , void * vars ,
303
312
JsonPathVarCallback getVar ,
304
313
Jsonx * json , bool isJsonb ,
305
314
bool throwErrors ,
306
- JsonValueList * result );
315
+ JsonValueList * result ,
316
+ void * * pCache ,
317
+ MemoryContext cacheCxt );
307
318
static JsonPathExecResult executeItem (JsonPathExecContext * cxt ,
308
319
JsonPathItem * jsp , JsonItem * jb ,
309
320
JsonValueList * found );
@@ -475,7 +486,8 @@ static bool JsonTableNextRow(JsonTableScanState *scan, bool isJsonb);
475
486
static Datum
476
487
jsonx_path_exists (PG_FUNCTION_ARGS , bool isJsonb )
477
488
{
478
- JsonPathExecResult res = executeUserFunc (fcinfo , NULL , isJsonb , false);
489
+ JsonPathExecResult res = executeUserFunc (fcinfo , NULL , isJsonb , false,
490
+ & fcinfo -> flinfo -> fn_extra );
479
491
480
492
if (jperIsError (res ))
481
493
PG_RETURN_NULL ();
@@ -522,7 +534,8 @@ jsonx_path_match(PG_FUNCTION_ARGS, bool isJsonb)
522
534
{
523
535
JsonPathUserFuncContext cxt ;
524
536
525
- (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false);
537
+ (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false,
538
+ & fcinfo -> flinfo -> fn_extra );
526
539
527
540
freeUserFuncContext (& cxt );
528
541
@@ -584,22 +597,27 @@ json_path_match_opr(PG_FUNCTION_ARGS)
584
597
static Datum
585
598
jsonx_path_query (PG_FUNCTION_ARGS , bool isJsonb )
586
599
{
600
+ JsonpathQueryContext * cxt = fcinfo -> flinfo -> fn_extra ;
587
601
FuncCallContext * funcctx ;
588
602
List * found ;
589
603
JsonItem * v ;
590
604
ListCell * c ;
591
605
Datum res ;
592
606
593
- if (SRF_IS_FIRSTCALL ())
607
+ if (!cxt )
608
+ cxt = fcinfo -> flinfo -> fn_extra =
609
+ MemoryContextAllocZero (fcinfo -> flinfo -> fn_mcxt , sizeof (* cxt ));
610
+
611
+ if (SRF_IS_FIRSTCALL_EXT (& cxt -> srfcxt ))
594
612
{
595
613
JsonPathUserFuncContext jspcxt ;
596
614
MemoryContext oldcontext ;
597
615
598
- funcctx = SRF_FIRSTCALL_INIT ( );
616
+ funcctx = SRF_FIRSTCALL_INIT_EXT ( & cxt -> srfcxt );
599
617
oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
600
618
601
619
/* jsonb and jsonpath arguments are copied into SRF context. */
602
- (void ) executeUserFunc (fcinfo , & jspcxt , isJsonb , true);
620
+ (void ) executeUserFunc (fcinfo , & jspcxt , isJsonb , true, & cxt -> cache );
603
621
604
622
/*
605
623
* Don't free jspcxt because items in jspcxt.found can reference
@@ -610,13 +628,13 @@ jsonx_path_query(PG_FUNCTION_ARGS, bool isJsonb)
610
628
MemoryContextSwitchTo (oldcontext );
611
629
}
612
630
613
- funcctx = SRF_PERCALL_SETUP ( );
631
+ funcctx = SRF_PERCALL_SETUP_EXT ( & cxt -> srfcxt );
614
632
found = funcctx -> user_fctx ;
615
633
616
634
c = list_head (found );
617
635
618
636
if (c == NULL )
619
- SRF_RETURN_DONE (funcctx );
637
+ SRF_RETURN_DONE_EXT (funcctx , & cxt -> srfcxt );
620
638
621
639
v = lfirst (c );
622
640
funcctx -> user_fctx = list_delete_first (found );
@@ -651,7 +669,8 @@ jsonx_path_query_array(PG_FUNCTION_ARGS, bool isJsonb)
651
669
JsonPathUserFuncContext cxt ;
652
670
Datum res ;
653
671
654
- (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false);
672
+ (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false,
673
+ & fcinfo -> flinfo -> fn_extra );
655
674
656
675
res = JsonbValueToJsonxDatum (wrapItemsInArray (& cxt .found , isJsonb ), isJsonb );
657
676
@@ -683,7 +702,8 @@ jsonx_path_query_first(PG_FUNCTION_ARGS, bool isJsonb)
683
702
JsonPathUserFuncContext cxt ;
684
703
Datum res ;
685
704
686
- (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false);
705
+ (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false,
706
+ & fcinfo -> flinfo -> fn_extra );
687
707
688
708
if (JsonValueListLength (& cxt .found ) >= 1 )
689
709
res = JsonItemToJsonxDatum (JsonValueListHead (& cxt .found ), isJsonb );
@@ -721,7 +741,8 @@ jsonx_path_query_first_text(PG_FUNCTION_ARGS, bool isJsonb)
721
741
JsonPathUserFuncContext cxt ;
722
742
text * txt ;
723
743
724
- (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false);
744
+ (void ) executeUserFunc (fcinfo , & cxt , isJsonb , false,
745
+ & fcinfo -> flinfo -> fn_extra );
725
746
726
747
if (JsonValueListLength (& cxt .found ) >= 1 )
727
748
txt = JsonItemUnquoteText (JsonValueListHead (& cxt .found ), isJsonb );
@@ -771,7 +792,7 @@ freeUserFuncContext(JsonPathUserFuncContext *cxt)
771
792
*/
772
793
static JsonPathExecResult
773
794
executeUserFunc (FunctionCallInfo fcinfo , JsonPathUserFuncContext * cxt ,
774
- bool isJsonb , bool copy )
795
+ bool isJsonb , bool copy , void * * fn_extra )
775
796
{
776
797
Datum js_toasted = PG_GETARG_DATUM (0 );
777
798
struct varlena * js_detoasted = copy ?
@@ -809,7 +830,8 @@ executeUserFunc(FunctionCallInfo fcinfo, JsonPathUserFuncContext *cxt,
809
830
}
810
831
811
832
res = executeJsonPath (jp , vars , getJsonPathVariableFromJsonx ,
812
- js , isJsonb , !silent , cxt ? & cxt -> found : NULL );
833
+ js , isJsonb , !silent , cxt ? & cxt -> found : NULL ,
834
+ fn_extra , fcinfo -> flinfo -> fn_mcxt );
813
835
814
836
if (!cxt && !copy )
815
837
{
@@ -854,7 +876,7 @@ executeUserFunc(FunctionCallInfo fcinfo, JsonPathUserFuncContext *cxt,
854
876
static JsonPathExecResult
855
877
executeJsonPath (JsonPath * path , void * vars , JsonPathVarCallback getVar ,
856
878
Jsonx * json , bool isJsonb , bool throwErrors ,
857
- JsonValueList * result )
879
+ JsonValueList * result , void * * pCache , MemoryContext cacheCxt )
858
880
{
859
881
JsonPathExecContext cxt ;
860
882
JsonPathExecResult res ;
@@ -863,6 +885,66 @@ executeJsonPath(JsonPath *path, void *vars, JsonPathVarCallback getVar,
863
885
JsonbValue * jbv = JsonItemJbv (& jsi );
864
886
JsonItemStackEntry root ;
865
887
888
+ if (path -> ext_items_count )
889
+ {
890
+ if (pCache )
891
+ {
892
+ struct
893
+ {
894
+ JsonPath * path ;
895
+ void * * cache ;
896
+ } * cache = * pCache ;
897
+
898
+ if (!cache )
899
+ cache = * pCache = MemoryContextAllocZero (cacheCxt , sizeof (* cache ));
900
+
901
+ if (cache -> path &&
902
+ (VARSIZE (path ) != VARSIZE (cache -> path ) ||
903
+ memcmp (path , cache -> path , VARSIZE (path ))))
904
+ {
905
+ /* invalidate cache TODO optimize */
906
+ cache -> path = NULL ;
907
+
908
+ if (cache -> cache )
909
+ {
910
+ pfree (cache -> cache );
911
+ cache -> cache = NULL ;
912
+ }
913
+ }
914
+
915
+ if (cache -> path )
916
+ {
917
+ Assert (cache -> cache );
918
+ }
919
+ else
920
+ {
921
+ Assert (!cache -> cache );
922
+
923
+ cache -> path = MemoryContextAlloc (cacheCxt , VARSIZE (path ));
924
+ memcpy (cache -> path , path , VARSIZE (path ));
925
+
926
+ cache -> cache = MemoryContextAllocZero (cacheCxt ,
927
+ sizeof (cxt .cache [0 ]) *
928
+ path -> ext_items_count );
929
+ }
930
+
931
+ cxt .cache = cache -> cache ;
932
+ cxt .cache_mcxt = cacheCxt ;
933
+
934
+ path = cache -> path ; /* use cached jsonpath value */
935
+ }
936
+ else
937
+ {
938
+ cxt .cache = palloc0 (sizeof (cxt .cache [0 ]) * path -> ext_items_count );
939
+ cxt .cache_mcxt = CurrentMemoryContext ;
940
+ }
941
+ }
942
+ else
943
+ {
944
+ cxt .cache = NULL ;
945
+ cxt .cache_mcxt = NULL ;
946
+ }
947
+
866
948
jspInit (& jsp , path );
867
949
868
950
if (isJsonb )
@@ -4169,7 +4251,8 @@ JsonPathExists(Datum jb, JsonPath *jp, List *vars, bool isJsonb,
4169
4251
{
4170
4252
Jsonx * js = DatumGetJsonxP (jb , isJsonb );
4171
4253
JsonPathExecResult res = executeJsonPath (jp , vars , EvalJsonPathVar ,
4172
- js , isJsonb , !error , NULL );
4254
+ js , isJsonb , !error ,
4255
+ NULL , NULL , NULL );
4173
4256
4174
4257
Assert (error || !jperIsError (res ));
4175
4258
@@ -4190,7 +4273,8 @@ JsonPathQuery(Datum jb, JsonPath *jp, JsonWrapper wrapper, bool *empty,
4190
4273
JsonPathExecResult res PG_USED_FOR_ASSERTS_ONLY ;
4191
4274
int count ;
4192
4275
4193
- res = executeJsonPath (jp , vars , EvalJsonPathVar , js , isJsonb , !error , & found );
4276
+ res = executeJsonPath (jp , vars , EvalJsonPathVar , js , isJsonb , !error ,
4277
+ & found , NULL , NULL );
4194
4278
4195
4279
Assert (error || !jperIsError (res ));
4196
4280
@@ -4259,11 +4343,11 @@ JsonPathValue(Datum jb, JsonPath *jp, bool *empty, bool *error, List *vars,
4259
4343
Jsonx * js = DatumGetJsonxP (jb , isJsonb );
4260
4344
JsonItem * res ;
4261
4345
JsonValueList found = { 0 };
4262
- JsonPathExecResult jper PG_USED_FOR_ASSERTS_ONLY ;
4346
+ JsonPathExecResult jper ;
4263
4347
int count ;
4264
4348
4265
4349
jper = executeJsonPath (jp , vars , EvalJsonPathVar , js , isJsonb , !error ,
4266
- & found );
4350
+ & found , NULL , NULL );
4267
4351
4268
4352
Assert (error || !jperIsError (jper ));
4269
4353
@@ -4615,7 +4699,8 @@ JsonTableResetContextItem(JsonTableScanState *scan, Datum item, bool isJsonb)
4615
4699
oldcxt = MemoryContextSwitchTo (scan -> mcxt );
4616
4700
4617
4701
res = executeJsonPath (scan -> path , scan -> args , EvalJsonPathVar , js , isJsonb ,
4618
- scan -> errorOnError , & scan -> found );
4702
+ scan -> errorOnError , & scan -> found ,
4703
+ NULL /* FIXME */ , NULL );
4619
4704
4620
4705
MemoryContextSwitchTo (oldcxt );
4621
4706
0 commit comments