@@ -116,11 +116,11 @@ jsonStatsInit(JsonStats data, const VariableStatData *vardata)
116
116
data -> rel = vardata -> rel ;
117
117
data -> nullfrac =
118
118
data -> attslot .nnumbers > 0 ? data -> attslot .numbers [0 ] : 0.0 ;
119
- data -> values = data -> attslot .values ;
120
- data -> nvalues = data -> attslot .nvalues ;
119
+ data -> pathdatums = data -> attslot .values + 1 ;
120
+ data -> npaths = data -> attslot .nvalues - 1 ;
121
121
122
122
/* Extract root path prefix */
123
- jb = DatumGetJsonbP (data -> values [0 ]);
123
+ jb = DatumGetJsonbP (data -> attslot . values [0 ]);
124
124
if (!JsonbExtractScalar (& jb -> root , & prefix ) || prefix .type != jbvString )
125
125
{
126
126
free_attstatsslot (& data -> attslot );
@@ -130,6 +130,15 @@ jsonStatsInit(JsonStats data, const VariableStatData *vardata)
130
130
data -> prefix = prefix .val .string .val ;
131
131
data -> prefixlen = prefix .val .string .len ;
132
132
133
+ /* Create path cache, initialze only two fields that acting as flags */
134
+ data -> paths = palloc (sizeof (* data -> paths ) * data -> npaths );
135
+
136
+ for (int i = 0 ; i < data -> npaths ; i ++ )
137
+ {
138
+ data -> paths [i ].data = NULL ;
139
+ data -> paths [i ].path = NULL ;
140
+ }
141
+
133
142
return true;
134
143
}
135
144
@@ -159,7 +168,6 @@ jsonPathStatsGetSpecialStats(JsonPathStats pstats, JsonPathStatsType type)
159
168
160
169
stats = palloc (sizeof (* stats ));
161
170
* stats = * pstats ;
162
- stats -> path = memcpy (palloc (stats -> pathlen ), stats -> path , stats -> pathlen );
163
171
stats -> type = type ;
164
172
165
173
return stats ;
@@ -197,6 +205,50 @@ jsonPathStatsGetArrayLengthStats(JsonPathStats pstats)
197
205
return jsonPathStatsGetSpecialStats (pstats , JsonPathStatsArrayLength );
198
206
}
199
207
208
+ /*
209
+ * jsonPathStatsGetPath
210
+ * Try to use cached path name or extract it from per-path stats datum.
211
+ *
212
+ * Returns true on succces, false on error.
213
+ */
214
+ static inline bool
215
+ jsonPathStatsGetPath (JsonPathStats stats , Datum pathdatum ,
216
+ const char * * path , int * pathlen )
217
+ {
218
+ * path = stats -> path ;
219
+
220
+ if (* path )
221
+ /* use cached path name */
222
+ * pathlen = stats -> pathlen ;
223
+ else
224
+ {
225
+ Jsonb * jsonb = DatumGetJsonbP (pathdatum );
226
+ JsonbValue pathkey ;
227
+ JsonbValue * pathval ;
228
+
229
+ /* extract path from the statistics represented as jsonb document */
230
+ JsonValueInitStringWithLen (& pathkey , "path" , 4 );
231
+ pathval = findJsonbValueFromContainer (& jsonb -> root , JB_FOBJECT , & pathkey );
232
+
233
+ if (!pathval || pathval -> type != jbvString )
234
+ return false; /* XXX invalid stats data, maybe throw error */
235
+
236
+ /* cache extracted path name */
237
+ * path = stats -> path = pathval -> val .string .val ;
238
+ * pathlen = stats -> pathlen = pathval -> val .string .len ;
239
+ }
240
+
241
+ return true;
242
+ }
243
+
244
+ /* Context for bsearch()ing paths */
245
+ typedef struct JsonPathStatsSearchContext
246
+ {
247
+ JsonStats stats ;
248
+ const char * path ;
249
+ int pathlen ;
250
+ } JsonPathStatsSearchContext ;
251
+
200
252
/*
201
253
* jsonPathStatsCompare
202
254
* Compare two JsonPathStats structs, so that we can sort them.
@@ -211,24 +263,21 @@ jsonPathStatsGetArrayLengthStats(JsonPathStats pstats)
211
263
static int
212
264
jsonPathStatsCompare (const void * pv1 , const void * pv2 )
213
265
{
214
- JsonbValue pathkey ;
215
- JsonbValue * path2 ;
216
- JsonbValue const * path1 = pv1 ;
217
- Jsonb * jsonb = DatumGetJsonbP (* (Datum const * ) pv2 );
266
+ JsonPathStatsSearchContext const * cxt = pv1 ;
267
+ Datum const * pathdatum = (Datum const * ) pv2 ;
268
+ int index = pathdatum - cxt -> stats -> pathdatums ;
269
+ JsonPathStats stats = & cxt -> stats -> paths [index ];
270
+ const char * path ;
271
+ int pathlen ;
218
272
int res ;
219
273
220
- /* extract path from the statistics represented as jsonb document */
221
- JsonValueInitStringWithLen (& pathkey , "path" , 4 );
222
- path2 = findJsonbValueFromContainer (& jsonb -> root , JB_FOBJECT , & pathkey );
223
-
224
- if (!path2 || path2 -> type != jbvString )
225
- return 1 ; /* XXX this is sign of invalid stats data */
274
+ if (!jsonPathStatsGetPath (stats , * pathdatum , & path , & pathlen ))
275
+ return 1 ; /* XXX invalid stats data */
226
276
227
277
/* compare the shared part first, then compare by length */
228
- res = strncmp (path1 -> val .string .val , path2 -> val .string .val ,
229
- Min (path1 -> val .string .len , path2 -> val .string .len ));
278
+ res = strncmp (cxt -> path , path , Min (cxt -> pathlen , pathlen ));
230
279
231
- return res ? res : path1 -> val . string . len - path2 -> val . string . len ;
280
+ return res ? res : cxt -> pathlen - pathlen ;
232
281
}
233
282
234
283
/*
@@ -240,26 +289,36 @@ jsonPathStatsCompare(const void *pv1, const void *pv2)
240
289
* should handle it by itself.
241
290
*/
242
291
static JsonPathStats
243
- jsonStatsFindPath (JsonStats jsdata , char * path , int pathlen )
292
+ jsonStatsFindPath (JsonStats jsdata , const char * path , int pathlen )
244
293
{
294
+ JsonPathStatsSearchContext cxt ;
245
295
JsonPathStats stats ;
246
- JsonbValue jbvkey ;
247
296
Datum * pdatum ;
297
+ int index ;
248
298
249
- JsonValueInitStringWithLen (& jbvkey , path , pathlen );
299
+ cxt .stats = jsdata ;
300
+ cxt .path = path ;
301
+ cxt .pathlen = pathlen ;
250
302
251
- pdatum = bsearch (& jbvkey , jsdata -> values + 1 , jsdata -> nvalues - 1 ,
252
- sizeof (* jsdata -> values ), jsonPathStatsCompare );
303
+ pdatum = bsearch (& cxt , jsdata -> pathdatums , jsdata -> npaths ,
304
+ sizeof (* jsdata -> pathdatums ), jsonPathStatsCompare );
253
305
254
306
if (!pdatum )
255
307
return NULL ;
256
308
257
- stats = palloc (sizeof (* stats ));
258
- stats -> datum = pdatum ;
259
- stats -> data = jsdata ;
260
- stats -> path = path ;
261
- stats -> pathlen = pathlen ;
262
- stats -> type = JsonPathStatsValues ;
309
+ index = pdatum - jsdata -> pathdatums ;
310
+ stats = & jsdata -> paths [index ];
311
+
312
+ Assert (stats -> path );
313
+ Assert (stats -> pathlen == pathlen );
314
+
315
+ /* Init all fields if needed (stats->data == NULL means uninitialized) */
316
+ if (!stats -> data )
317
+ {
318
+ stats -> data = jsdata ;
319
+ stats -> datum = pdatum ;
320
+ stats -> type = JsonPathStatsValues ;
321
+ }
263
322
264
323
return stats ;
265
324
}
@@ -346,20 +405,15 @@ JsonPathStats
346
405
jsonPathStatsGetSubpath (JsonPathStats pstats , const char * key )
347
406
{
348
407
JsonPathStats spstats ;
349
- char * path ;
350
- int pathlen ;
351
408
StringInfoData str ;
352
409
353
410
initStringInfo (& str );
354
411
appendBinaryStringInfo (& str , pstats -> path , pstats -> pathlen );
355
412
jsonPathAppendEntry (& str , key );
356
413
357
- path = str .data ;
358
- pathlen = str .len ;
359
-
360
- spstats = jsonStatsFindPath (pstats -> data , path , pathlen );
414
+ spstats = jsonStatsFindPath (pstats -> data , str .data , str .len );
361
415
if (!spstats )
362
- pfree (path );
416
+ pfree (str . data );
363
417
364
418
return spstats ;
365
419
}
@@ -458,27 +512,24 @@ jsonPathStatsGetNextSubpathStats(JsonPathStats stats, JsonPathStats *pkeystats,
458
512
bool keysOnly )
459
513
{
460
514
JsonPathStats keystats = * pkeystats ;
515
+ /* compute next index */
461
516
int index =
462
- (keystats ? keystats -> datum : stats -> datum ) - stats -> data -> values + 1 ;
517
+ (keystats ? keystats -> datum : stats -> datum ) - stats -> data -> pathdatums + 1 ;
463
518
464
- for (; index < stats -> data -> nvalues ; index ++ )
519
+ if (stats -> type != JsonPathStatsValues )
520
+ return false; /* length stats doe not have subpaths */
521
+
522
+ for (; index < stats -> data -> npaths ; index ++ )
465
523
{
466
- JsonbValue pathkey ;
467
- JsonbValue * jbvpath ;
468
- Datum * pdatum = & stats -> data -> values [index ];
469
- Jsonb * jb = DatumGetJsonbP (* pdatum );
524
+ Datum * pathdatum = & stats -> data -> pathdatums [index ];
470
525
const char * path ;
471
526
int pathlen ;
472
527
473
- JsonValueInitStringWithLen (& pathkey , "path" , 4 );
474
- jbvpath = findJsonbValueFromContainer (& jb -> root , JB_FOBJECT , & pathkey );
528
+ keystats = & stats -> data -> paths [index ];
475
529
476
- if (jbvpath -> type != jbvString )
530
+ if (! jsonPathStatsGetPath ( keystats , * pathdatum , & path , & pathlen ) )
477
531
break ; /* invalid path stats */
478
532
479
- path = jbvpath -> val .string .val ;
480
- pathlen = jbvpath -> val .string .len ;
481
-
482
533
/* Break, if subpath does not start from a desired prefix */
483
534
if (pathlen <= stats -> pathlen ||
484
535
memcmp (path , stats -> path , stats -> pathlen ))
@@ -521,14 +572,13 @@ jsonPathStatsGetNextSubpathStats(JsonPathStats stats, JsonPathStats *pkeystats,
521
572
continue ; /* invalid path */
522
573
}
523
574
524
- if (!keystats )
525
- keystats = palloc (sizeof (* keystats ));
526
-
527
- keystats -> datum = pdatum ;
528
- keystats -> data = stats -> data ;
529
- keystats -> pathlen = pathlen ;
530
- keystats -> path = memcpy (palloc (pathlen ), path , pathlen );
531
- keystats -> type = JsonPathStatsValues ;
575
+ /* Init path stats if needed */
576
+ if (!stats -> data )
577
+ {
578
+ keystats -> data = stats -> data ;
579
+ keystats -> datum = pathdatum ;
580
+ keystats -> type = JsonPathStatsValues ;
581
+ }
532
582
533
583
* pkeystats = keystats ;
534
584
@@ -736,9 +786,9 @@ jsonPathStatsExtractData(JsonPathStats pstats, JsonStatType stattype,
736
786
737
787
if ((stattype == JsonStatJsonb ||
738
788
stattype == JsonStatJsonbWithoutSubpaths ) &&
739
- jsonAnalyzeBuildSubPathsData (pstats -> data -> values ,
740
- pstats -> data -> nvalues ,
741
- pstats -> datum - pstats -> data -> values ,
789
+ jsonAnalyzeBuildSubPathsData (pstats -> data -> pathdatums ,
790
+ pstats -> data -> npaths ,
791
+ pstats -> datum - pstats -> data -> pathdatums ,
742
792
pstats -> path ,
743
793
pstats -> pathlen ,
744
794
stattype == JsonStatJsonb ,
@@ -871,7 +921,7 @@ jsonPathStatsFormTuple(JsonPathStats pstats, JsonStatType type, float4 nullfrac)
871
921
* If it is the ordinary root path stats, there is no need to transform
872
922
* the tuple, it can be simply copied.
873
923
*/
874
- if (pstats -> datum == & pstats -> data -> values [ 1 ] &&
924
+ if (pstats -> datum == & pstats -> data -> pathdatums [ 0 ] &&
875
925
pstats -> type == JsonPathStatsValues )
876
926
return heap_copytuple (pstats -> data -> statsTuple );
877
927
0 commit comments