Skip to content

Commit 0bbcb3a

Browse files
committed
Add into extended statistics clause WITH and option 'method' which command
postgresql generate ndistinct combinations corresponding to a columns order in the index. It is needed to survive ANALYZE if we want to estimate multiple columns and believe they will be used in a query according to definition of an existing index.
1 parent a3699da commit 0bbcb3a

File tree

15 files changed

+212
-111
lines changed

15 files changed

+212
-111
lines changed

src/backend/catalog/system_views.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ CREATE VIEW pg_stats_ext WITH (security_barrier) AS
283283
) AS attnames,
284284
pg_get_statisticsobjdef_expressions(s.oid) as exprs,
285285
s.stxkind AS kinds,
286+
s.options AS options,
286287
sd.stxdinherit AS inherited,
287288
sd.stxdndistinct AS n_distinct,
288289
sd.stxddependencies AS dependencies,

src/backend/commands/statscmds.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,45 @@ compare_int16(const void *a, const void *b)
5555
return (av - bv);
5656
}
5757

58+
/*
59+
* Check correctness of the options list and prepare the list to be stored
60+
* in the statistics relation.
61+
*/
62+
static Datum
63+
transform_extstat_options(List *defList)
64+
{
65+
ArrayBuildState *astate = NULL;
66+
Datum result;
67+
68+
foreach_ptr(DefElem, def, defList)
69+
{
70+
const char *value;
71+
Size len;
72+
text *t;
73+
74+
if (strcmp(def->defname, "method") != 0 || def->arg == NULL)
75+
elog(ERROR, "unrecognized option or value: %s", def->defname);
76+
77+
value = defGetString(def);
78+
79+
len = VARHDRSZ + strlen(def->defname) + 1 + strlen(value);
80+
/* +1 leaves room for sprintf's trailing null */
81+
t = (text *) palloc(len + 1);
82+
SET_VARSIZE(t, len);
83+
sprintf(VARDATA(t), "%s=%s", def->defname, value);
84+
85+
astate = accumArrayResult(astate, PointerGetDatum(t),
86+
false, TEXTOID,
87+
CurrentMemoryContext);
88+
}
89+
90+
result = (astate != NULL) ?
91+
makeArrayResult(astate, CurrentMemoryContext) :
92+
(Datum) 0;
93+
94+
return result;
95+
}
96+
5897
/*
5998
* CREATE STATISTICS
6099
*/
@@ -504,6 +543,10 @@ CreateStatistics(CreateStatsStmt *stmt)
504543
if (exprsDatum == (Datum) 0)
505544
nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
506545

546+
values[Anum_pg_statistic_ext_options-1] = transform_extstat_options(stmt->options);
547+
if (stmt->options == NIL)
548+
nulls[Anum_pg_statistic_ext_options - 1] = true;
549+
507550
/* insert it into pg_statistic_ext */
508551
htup = heap_form_tuple(statrel->rd_att, values, nulls);
509552
CatalogTupleInsert(statrel, htup);

src/backend/parser/gram.y

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4645,7 +4645,7 @@ ExistingIndex: USING INDEX name { $$ = $3; }
46454645

46464646
CreateStatsStmt:
46474647
CREATE STATISTICS opt_qualified_name
4648-
opt_name_list ON stats_params FROM from_list
4648+
opt_name_list ON stats_params FROM from_list opt_reloptions
46494649
{
46504650
CreateStatsStmt *n = makeNode(CreateStatsStmt);
46514651

@@ -4655,10 +4655,11 @@ CreateStatsStmt:
46554655
n->relations = $8;
46564656
n->stxcomment = NULL;
46574657
n->if_not_exists = false;
4658+
n->options = $9;
46584659
$$ = (Node *) n;
46594660
}
46604661
| CREATE STATISTICS IF_P NOT EXISTS any_name
4661-
opt_name_list ON stats_params FROM from_list
4662+
opt_name_list ON stats_params FROM from_list opt_reloptions
46624663
{
46634664
CreateStatsStmt *n = makeNode(CreateStatsStmt);
46644665

@@ -4668,6 +4669,7 @@ CreateStatsStmt:
46684669
n->relations = $11;
46694670
n->stxcomment = NULL;
46704671
n->if_not_exists = true;
4672+
n->options = $12;
46714673
$$ = (Node *) n;
46724674
}
46734675
;

src/backend/parser/parse_utilcmd.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2117,6 +2117,12 @@ generateClonedExtStatsStmt(RangeVar *heapRel, Oid heapRelid,
21172117
pfree(exprsString);
21182118
}
21192119

2120+
datum = SysCacheGetAttr(STATEXTOID, ht_stats,
2121+
Anum_pg_statistic_ext_options, &isnull);
2122+
2123+
if (isnull)
2124+
datum = (Datum) 0;
2125+
21202126
/* finally, build the output node */
21212127
stats = makeNode(CreateStatsStmt);
21222128
stats->defnames = NULL;
@@ -2126,6 +2132,7 @@ generateClonedExtStatsStmt(RangeVar *heapRel, Oid heapRelid,
21262132
stats->stxcomment = NULL;
21272133
stats->transformed = true; /* don't need transformStatsStmt again */
21282134
stats->if_not_exists = false;
2135+
stats->options = untransformRelOptions(datum);
21292136

21302137
/* Clean up */
21312138
ReleaseSysCache(ht_stats);

src/backend/statistics/extended_stats.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "access/detoast.h"
2020
#include "access/genam.h"
2121
#include "access/htup_details.h"
22+
#include "access/reloptions.h"
2223
#include "access/table.h"
2324
#include "catalog/indexing.h"
2425
#include "catalog/pg_statistic_ext.h"
@@ -70,6 +71,7 @@ typedef struct StatExtEntry
7071
List *types; /* 'char' list of enabled statistics kinds */
7172
int stattarget; /* statistics target (-1 for default) */
7273
List *exprs; /* expressions */
74+
int method;
7375
} StatExtEntry;
7476

7577

@@ -510,6 +512,25 @@ fetch_statentries_for_relation(Relation pg_statext, Oid relid)
510512

511513
entry->exprs = exprs;
512514

515+
datum = SysCacheGetAttr(STATEXTOID, htup,
516+
Anum_pg_statistic_ext_options, &isnull);
517+
if (!isnull)
518+
{
519+
List *defList = untransformRelOptions(datum);
520+
521+
foreach_ptr(DefElem, def, defList)
522+
{
523+
if (strcmp(def->defname, "method") != 0 || def->arg == NULL)
524+
elog(ERROR, "unrecognized option or value: %s", def->defname);
525+
526+
if (strcmp(defGetString(def), "linear") == 0)
527+
entry->method = EXTSTAT_METHOD_LINEAR;
528+
}
529+
}
530+
531+
if (entry->method == 0)
532+
entry->method = EXTSTAT_METHOD_COMBS;
533+
513534
result = lappend(result, entry);
514535
}
515536

@@ -2560,6 +2581,8 @@ make_build_data(Relation rel, StatExtEntry *stat, int numrows, HeapTuple *rows,
25602581
k--;
25612582
}
25622583

2584+
result->method = stat->method;
2585+
25632586
/* first extract values for all the regular attributes */
25642587
for (i = 0; i < numrows; i++)
25652588
{

src/backend/statistics/mvdistinct.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,10 @@ typedef struct CombinationGenerator
6565
int current; /* index of the next combination to return */
6666
int ncombinations; /* number of combinations (size of array) */
6767
int *combinations; /* array of pre-built combinations */
68+
int method;
6869
} CombinationGenerator;
6970

70-
static CombinationGenerator *generator_init(int n, int k);
71+
static CombinationGenerator *generator_init(int n, int k, int method);
7172
static void generator_free(CombinationGenerator *state);
7273
static int *generator_next(CombinationGenerator *state);
7374
static void generate_combinations(CombinationGenerator *state);
@@ -91,7 +92,9 @@ statext_ndistinct_build(double totalrows, StatsBuildData *data)
9192
int k;
9293
int itemcnt;
9394
int numattrs = data->nattnums;
94-
int numcombs = num_combinations(numattrs);
95+
int numcombs = (data->method == EXTSTAT_METHOD_COMBS) ?
96+
num_combinations(numattrs) :
97+
(numattrs - 1);
9598

9699
result = palloc(offsetof(MVNDistinct, items) +
97100
numcombs * sizeof(MVNDistinctItem));
@@ -106,7 +109,7 @@ statext_ndistinct_build(double totalrows, StatsBuildData *data)
106109
CombinationGenerator *generator;
107110

108111
/* generate combinations of K out of N elements */
109-
generator = generator_init(numattrs, k);
112+
generator = generator_init(numattrs, k, data->method);
110113

111114
while ((combination = generator_next(generator)))
112115
{
@@ -586,7 +589,7 @@ num_combinations(int n)
586589
* generating them on the fly.
587590
*/
588591
static CombinationGenerator *
589-
generator_init(int n, int k)
592+
generator_init(int n, int k, int method)
590593
{
591594
CombinationGenerator *state;
592595

@@ -595,7 +598,10 @@ generator_init(int n, int k)
595598
/* allocate the generator state as a single chunk of memory */
596599
state = (CombinationGenerator *) palloc(sizeof(CombinationGenerator));
597600

598-
state->ncombinations = n_choose_k(n, k);
601+
if (method == EXTSTAT_METHOD_LINEAR)
602+
state->ncombinations = 1;
603+
else
604+
state->ncombinations = n_choose_k(n, k);
599605

600606
/* pre-allocate space for all combinations */
601607
state->combinations = (int *) palloc(sizeof(int) * k * state->ncombinations);
@@ -605,13 +611,23 @@ generator_init(int n, int k)
605611
state->n = n;
606612

607613
/* now actually pre-generate all the combinations of K elements */
608-
generate_combinations(state);
614+
if (method == EXTSTAT_METHOD_LINEAR)
615+
{
616+
int i;
617+
618+
for (i = 0; i < k; i++)
619+
state->combinations[i] = i;
620+
}
621+
else
622+
{
623+
generate_combinations(state);
609624

610-
/* make sure we got the expected number of combinations */
611-
Assert(state->current == state->ncombinations);
625+
/* make sure we got the expected number of combinations */
626+
Assert(state->current == state->ncombinations);
612627

613-
/* reset the number, so we start with the first one */
614-
state->current = 0;
628+
/* reset the number, so we start with the first one */
629+
state->current = 0;
630+
}
615631

616632
return state;
617633
}

src/backend/utils/cache/typcache.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ lookup_type_cache(Oid type_id, int flags)
468468

469469
tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_id));
470470
if (!HeapTupleIsValid(tp))
471-
ereport(ERROR,
471+
ereport(PANIC,
472472
(errcode(ERRCODE_UNDEFINED_OBJECT),
473473
errmsg("type with OID %u does not exist", type_id)));
474474
typtup = (Form_pg_type) GETSTRUCT(tp);

src/bin/psql/describe.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4776,8 +4776,10 @@ listExtendedStats(const char *pattern)
47764776
appendPQExpBuffer(&buf,
47774777
"pg_catalog.format('%%s FROM %%s', \n"
47784778
" pg_catalog.pg_get_statisticsobjdef_columns(es.oid), \n"
4779-
" es.stxrelid::pg_catalog.regclass) AS \"%s\"",
4780-
gettext_noop("Definition"));
4779+
" es.stxrelid::pg_catalog.regclass) AS \"%s\", \n"
4780+
" es.options AS \"%s\" \n",
4781+
gettext_noop("Definition"),
4782+
gettext_noop("options"));
47814783
else
47824784
appendPQExpBuffer(&buf,
47834785
"pg_catalog.format('%%s FROM %%s', \n"

src/include/catalog/pg_statistic_ext.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ CATALOG(pg_statistic_ext,3381,StatisticExtRelationId)
5757
pg_node_tree stxexprs; /* A list of expression trees for stats
5858
* attributes that are not simple column
5959
* references. */
60+
text options[1] BKI_DEFAULT(_null_);
6061
#endif
6162

6263
} FormData_pg_statistic_ext;

src/include/nodes/parsenodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3418,6 +3418,7 @@ typedef struct CreateStatsStmt
34183418
char *stxcomment; /* comment to apply to stats, or NULL */
34193419
bool transformed; /* true when transformStatsStmt is finished */
34203420
bool if_not_exists; /* do nothing if stats name already exists */
3421+
List *options;
34213422
} CreateStatsStmt;
34223423

34233424
/*

0 commit comments

Comments
 (0)