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

Commit ff3482c

Browse files
author
Nikita Glukhov
committed
Add path caching to JsonStatsData
1 parent 2ea748c commit ff3482c

File tree

2 files changed

+134
-77
lines changed

2 files changed

+134
-77
lines changed

src/backend/utils/adt/jsonb_selfuncs.c

Lines changed: 109 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ jsonStatsInit(JsonStats data, const VariableStatData *vardata)
116116
data->rel = vardata->rel;
117117
data->nullfrac =
118118
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;
121121

122122
/* Extract root path prefix */
123-
jb = DatumGetJsonbP(data->values[0]);
123+
jb = DatumGetJsonbP(data->attslot.values[0]);
124124
if (!JsonbExtractScalar(&jb->root, &prefix) || prefix.type != jbvString)
125125
{
126126
free_attstatsslot(&data->attslot);
@@ -130,6 +130,15 @@ jsonStatsInit(JsonStats data, const VariableStatData *vardata)
130130
data->prefix = prefix.val.string.val;
131131
data->prefixlen = prefix.val.string.len;
132132

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+
133142
return true;
134143
}
135144

@@ -159,7 +168,6 @@ jsonPathStatsGetSpecialStats(JsonPathStats pstats, JsonPathStatsType type)
159168

160169
stats = palloc(sizeof(*stats));
161170
*stats = *pstats;
162-
stats->path = memcpy(palloc(stats->pathlen), stats->path, stats->pathlen);
163171
stats->type = type;
164172

165173
return stats;
@@ -197,6 +205,50 @@ jsonPathStatsGetArrayLengthStats(JsonPathStats pstats)
197205
return jsonPathStatsGetSpecialStats(pstats, JsonPathStatsArrayLength);
198206
}
199207

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+
200252
/*
201253
* jsonPathStatsCompare
202254
* Compare two JsonPathStats structs, so that we can sort them.
@@ -211,24 +263,21 @@ jsonPathStatsGetArrayLengthStats(JsonPathStats pstats)
211263
static int
212264
jsonPathStatsCompare(const void *pv1, const void *pv2)
213265
{
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;
218272
int res;
219273

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 */
226276

227277
/* 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));
230279

231-
return res ? res : path1->val.string.len - path2->val.string.len;
280+
return res ? res : cxt->pathlen - pathlen;
232281
}
233282

234283
/*
@@ -240,26 +289,36 @@ jsonPathStatsCompare(const void *pv1, const void *pv2)
240289
* should handle it by itself.
241290
*/
242291
static JsonPathStats
243-
jsonStatsFindPath(JsonStats jsdata, char *path, int pathlen)
292+
jsonStatsFindPath(JsonStats jsdata, const char *path, int pathlen)
244293
{
294+
JsonPathStatsSearchContext cxt;
245295
JsonPathStats stats;
246-
JsonbValue jbvkey;
247296
Datum *pdatum;
297+
int index;
248298

249-
JsonValueInitStringWithLen(&jbvkey, path, pathlen);
299+
cxt.stats = jsdata;
300+
cxt.path = path;
301+
cxt.pathlen = pathlen;
250302

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);
253305

254306
if (!pdatum)
255307
return NULL;
256308

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+
}
263322

264323
return stats;
265324
}
@@ -346,20 +405,15 @@ JsonPathStats
346405
jsonPathStatsGetSubpath(JsonPathStats pstats, const char *key)
347406
{
348407
JsonPathStats spstats;
349-
char *path;
350-
int pathlen;
351408
StringInfoData str;
352409

353410
initStringInfo(&str);
354411
appendBinaryStringInfo(&str, pstats->path, pstats->pathlen);
355412
jsonPathAppendEntry(&str, key);
356413

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);
361415
if (!spstats)
362-
pfree(path);
416+
pfree(str.data);
363417

364418
return spstats;
365419
}
@@ -458,27 +512,24 @@ jsonPathStatsGetNextSubpathStats(JsonPathStats stats, JsonPathStats *pkeystats,
458512
bool keysOnly)
459513
{
460514
JsonPathStats keystats = *pkeystats;
515+
/* compute next index */
461516
int index =
462-
(keystats ? keystats->datum : stats->datum) - stats->data->values + 1;
517+
(keystats ? keystats->datum : stats->datum) - stats->data->pathdatums + 1;
463518

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++)
465523
{
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];
470525
const char *path;
471526
int pathlen;
472527

473-
JsonValueInitStringWithLen(&pathkey, "path", 4);
474-
jbvpath = findJsonbValueFromContainer(&jb->root, JB_FOBJECT, &pathkey);
528+
keystats = &stats->data->paths[index];
475529

476-
if (jbvpath->type != jbvString)
530+
if (!jsonPathStatsGetPath(keystats, *pathdatum, &path, &pathlen))
477531
break; /* invalid path stats */
478532

479-
path = jbvpath->val.string.val;
480-
pathlen = jbvpath->val.string.len;
481-
482533
/* Break, if subpath does not start from a desired prefix */
483534
if (pathlen <= stats->pathlen ||
484535
memcmp(path, stats->path, stats->pathlen))
@@ -521,14 +572,13 @@ jsonPathStatsGetNextSubpathStats(JsonPathStats stats, JsonPathStats *pkeystats,
521572
continue; /* invalid path */
522573
}
523574

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+
}
532582

533583
*pkeystats = keystats;
534584

@@ -736,9 +786,9 @@ jsonPathStatsExtractData(JsonPathStats pstats, JsonStatType stattype,
736786

737787
if ((stattype == JsonStatJsonb ||
738788
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,
742792
pstats->path,
743793
pstats->pathlen,
744794
stattype == JsonStatJsonb,
@@ -871,7 +921,7 @@ jsonPathStatsFormTuple(JsonPathStats pstats, JsonStatType type, float4 nullfrac)
871921
* If it is the ordinary root path stats, there is no need to transform
872922
* the tuple, it can be simply copied.
873923
*/
874-
if (pstats->datum == &pstats->data->values[1] &&
924+
if (pstats->datum == &pstats->data->pathdatums[0] &&
875925
pstats->type == JsonPathStatsValues)
876926
return heap_copytuple(pstats->data->statsTuple);
877927

src/include/utils/json_selfuncs.h

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,35 +27,42 @@
2727
#define JSON_PATH_ROOT_ARRAY "$[*]"
2828
#define JSON_PATH_ROOT_ARRAY_LEN 4
2929

30-
typedef struct JsonStatData
31-
{
32-
AttStatsSlot attslot;
33-
HeapTuple statsTuple;
34-
RelOptInfo *rel;
35-
Datum *values;
36-
int nvalues;
37-
float4 nullfrac;
38-
const char *prefix;
39-
int prefixlen;
40-
bool acl_ok;
41-
} JsonStatData, *JsonStats;
42-
4330
typedef enum
4431
{
4532
JsonPathStatsValues,
4633
JsonPathStatsLength,
4734
JsonPathStatsArrayLength,
4835
} JsonPathStatsType;
4936

37+
typedef struct JsonStatData JsonStatData, *JsonStats;
38+
39+
/* Per-path JSON stats */
5040
typedef struct JsonPathStatsData
5141
{
52-
Datum *datum;
53-
JsonStats data;
54-
char *path;
55-
int pathlen;
56-
JsonPathStatsType type;
42+
JsonStats data; /* pointer to per-column control structure */
43+
Datum *datum; /* pointer to JSONB datum with stats data */
44+
const char *path; /* path string, points directly to JSONB data */
45+
int pathlen; /* path length */
46+
JsonPathStatsType type; /* type of stats (values, lengths etc.) */
5747
} JsonPathStatsData, *JsonPathStats;
5848

49+
/* Per-column JSON stats */
50+
struct JsonStatData
51+
{
52+
HeapTuple statsTuple; /* original pg_statistic tuple */
53+
AttStatsSlot attslot; /* data extracted from STATISTIC_KIND_JSON
54+
* slot of statsTuple */
55+
RelOptInfo *rel; /* Relation, or NULL if not identifiable */
56+
Datum *pathdatums; /* path JSONB datums */
57+
JsonPathStatsData *paths; /* cached paths */
58+
int npaths; /* number of paths */
59+
float4 nullfrac; /* NULL fraction */
60+
const char *prefix; /* global path prefix which needs to be used
61+
* for searching in pathdatums */
62+
int prefixlen; /* path prefix length */
63+
bool acl_ok; /* ACL check is Ok */
64+
};
65+
5966
typedef enum JsonStatType
6067
{
6168
JsonStatJsonb,

0 commit comments

Comments
 (0)