PostgreSQL Source Code  git master
statistics.h File Reference
#include "commands/vacuum.h"
#include "nodes/pathnodes.h"
Include dependency graph for statistics.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Data Structures

struct  MVNDistinctItem
 
struct  MVNDistinct
 
struct  MVDependency
 
struct  MVDependencies
 
struct  MCVItem
 
struct  MCVList
 

Macros

#define STATS_MAX_DIMENSIONS   8 /* max number of attributes */
 
#define STATS_NDISTINCT_MAGIC   0xA352BFA4 /* struct identifier */
 
#define STATS_NDISTINCT_TYPE_BASIC   1 /* struct version */
 
#define STATS_DEPS_MAGIC   0xB4549A2C /* marks serialized bytea */
 
#define STATS_DEPS_TYPE_BASIC   1 /* basic dependencies type */
 
#define STATS_MCV_MAGIC   0xE1A651C2 /* marks serialized bytea */
 
#define STATS_MCV_TYPE_BASIC   1 /* basic MCV list type */
 
#define STATS_MCVLIST_MAX_ITEMS   10000
 

Typedefs

typedef struct MVNDistinctItem MVNDistinctItem
 
typedef struct MVNDistinct MVNDistinct
 
typedef struct MVDependency MVDependency
 
typedef struct MVDependencies MVDependencies
 
typedef struct MCVItem MCVItem
 
typedef struct MCVList MCVList
 

Functions

MVNDistinctstatext_ndistinct_load (Oid mvoid)
 
MVDependenciesstatext_dependencies_load (Oid mvoid)
 
MCVListstatext_mcv_load (Oid mvoid)
 
void BuildRelationExtStatistics (Relation onerel, double totalrows, int numrows, HeapTuple *rows, int natts, VacAttrStats **vacattrstats)
 
int ComputeExtStatisticsRows (Relation onerel, int natts, VacAttrStats **stats)
 
bool statext_is_kind_built (HeapTuple htup, char kind)
 
Selectivity dependencies_clauselist_selectivity (PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Bitmapset **estimatedclauses)
 
Selectivity statext_clauselist_selectivity (PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Bitmapset **estimatedclauses, bool is_or)
 
bool has_stats_of_kind (List *stats, char requiredkind)
 
StatisticExtInfochoose_best_statistics (List *stats, char requiredkind, Bitmapset **clause_attnums, List **clause_exprs, int nclauses)
 
HeapTuple statext_expressions_load (Oid stxoid, int idx)
 

Macro Definition Documentation

◆ STATS_DEPS_MAGIC

#define STATS_DEPS_MAGIC   0xB4549A2C /* marks serialized bytea */

Definition at line 43 of file statistics.h.

◆ STATS_DEPS_TYPE_BASIC

#define STATS_DEPS_TYPE_BASIC   1 /* basic dependencies type */

Definition at line 44 of file statistics.h.

◆ STATS_MAX_DIMENSIONS

#define STATS_MAX_DIMENSIONS   8 /* max number of attributes */

Definition at line 19 of file statistics.h.

◆ STATS_MCV_MAGIC

#define STATS_MCV_MAGIC   0xE1A651C2 /* marks serialized bytea */

Definition at line 66 of file statistics.h.

◆ STATS_MCV_TYPE_BASIC

#define STATS_MCV_TYPE_BASIC   1 /* basic MCV list type */

Definition at line 67 of file statistics.h.

◆ STATS_MCVLIST_MAX_ITEMS

#define STATS_MCVLIST_MAX_ITEMS   10000

Definition at line 70 of file statistics.h.

◆ STATS_NDISTINCT_MAGIC

#define STATS_NDISTINCT_MAGIC   0xA352BFA4 /* struct identifier */

Definition at line 22 of file statistics.h.

◆ STATS_NDISTINCT_TYPE_BASIC

#define STATS_NDISTINCT_TYPE_BASIC   1 /* struct version */

Definition at line 23 of file statistics.h.

Typedef Documentation

◆ MCVItem

typedef struct MCVItem MCVItem

◆ MCVList

typedef struct MCVList MCVList

◆ MVDependencies

◆ MVDependency

typedef struct MVDependency MVDependency

◆ MVNDistinct

typedef struct MVNDistinct MVNDistinct

◆ MVNDistinctItem

Function Documentation

◆ BuildRelationExtStatistics()

void BuildRelationExtStatistics ( Relation  onerel,
double  totalrows,
int  numrows,
HeapTuple rows,
int  natts,
VacAttrStats **  vacattrstats 
)

Definition at line 113 of file extended_stats.c.

116 {
117  Relation pg_stext;
118  ListCell *lc;
119  List *statslist;
120  MemoryContext cxt;
121  MemoryContext oldcxt;
122  int64 ext_cnt;
123 
124  /* Do nothing if there are no columns to analyze. */
125  if (!natts)
126  return;
127 
128  /* the list of stats has to be allocated outside the memory context */
129  pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
130  statslist = fetch_statentries_for_relation(pg_stext, RelationGetRelid(onerel));
131 
132  /* memory context for building each statistics object */
134  "BuildRelationExtStatistics",
136  oldcxt = MemoryContextSwitchTo(cxt);
137 
138  /* report this phase */
139  if (statslist != NIL)
140  {
141  const int index[] = {
144  };
145  const int64 val[] = {
147  list_length(statslist)
148  };
149 
151  }
152 
153  ext_cnt = 0;
154  foreach(lc, statslist)
155  {
157  MVNDistinct *ndistinct = NULL;
158  MVDependencies *dependencies = NULL;
159  MCVList *mcv = NULL;
160  Datum exprstats = (Datum) 0;
161  VacAttrStats **stats;
162  ListCell *lc2;
163  int stattarget;
165 
166  /*
167  * Check if we can build these stats based on the column analyzed. If
168  * not, report this fact (except in autovacuum) and move on.
169  */
170  stats = lookup_var_attr_stats(onerel, stat->columns, stat->exprs,
171  natts, vacattrstats);
172  if (!stats)
173  {
176  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
177  errmsg("statistics object \"%s.%s\" could not be computed for relation \"%s.%s\"",
178  stat->schema, stat->name,
179  get_namespace_name(onerel->rd_rel->relnamespace),
180  RelationGetRelationName(onerel)),
181  errtable(onerel)));
182  continue;
183  }
184 
185  /* compute statistics target for this statistics object */
186  stattarget = statext_compute_stattarget(stat->stattarget,
187  bms_num_members(stat->columns),
188  stats);
189 
190  /*
191  * Don't rebuild statistics objects with statistics target set to 0
192  * (we just leave the existing values around, just like we do for
193  * regular per-column statistics).
194  */
195  if (stattarget == 0)
196  continue;
197 
198  /* evaluate expressions (if the statistics object has any) */
199  data = make_build_data(onerel, stat, numrows, rows, stats, stattarget);
200 
201  /* compute statistic of each requested type */
202  foreach(lc2, stat->types)
203  {
204  char t = (char) lfirst_int(lc2);
205 
206  if (t == STATS_EXT_NDISTINCT)
207  ndistinct = statext_ndistinct_build(totalrows, data);
208  else if (t == STATS_EXT_DEPENDENCIES)
209  dependencies = statext_dependencies_build(data);
210  else if (t == STATS_EXT_MCV)
211  mcv = statext_mcv_build(data, totalrows, stattarget);
212  else if (t == STATS_EXT_EXPRESSIONS)
213  {
214  AnlExprData *exprdata;
215  int nexprs;
216 
217  /* should not happen, thanks to checks when defining stats */
218  if (!stat->exprs)
219  elog(ERROR, "requested expression stats, but there are no expressions");
220 
221  exprdata = build_expr_data(stat->exprs, stattarget);
222  nexprs = list_length(stat->exprs);
223 
224  compute_expr_stats(onerel, totalrows,
225  exprdata, nexprs,
226  rows, numrows);
227 
228  exprstats = serialize_expr_stats(exprdata, nexprs);
229  }
230  }
231 
232  /* store the statistics in the catalog */
233  statext_store(stat->statOid, ndistinct, dependencies, mcv, exprstats, stats);
234 
235  /* for reporting progress */
237  ++ext_cnt);
238 
239  /* free the data used for building this statistics object */
240  MemoryContextReset(cxt);
241  }
242 
243  MemoryContextSwitchTo(oldcxt);
244  MemoryContextDelete(cxt);
245 
246  list_free(statslist);
247 
248  table_close(pg_stext, RowExclusiveLock);
249 }
bool IsAutoVacuumWorkerProcess(void)
Definition: autovacuum.c:3411
void pgstat_progress_update_param(int index, int64 val)
void pgstat_progress_update_multi_param(int nparam, const int *index, const int64 *val)
int bms_num_members(const Bitmapset *a)
Definition: bitmapset.c:648
MVDependencies * statext_dependencies_build(StatsBuildData *data)
Definition: dependencies.c:350
int errcode(int sqlerrcode)
Definition: elog.c:698
int errmsg(const char *fmt,...)
Definition: elog.c:909
#define WARNING
Definition: elog.h:30
#define ERROR
Definition: elog.h:33
#define elog(elevel,...)
Definition: elog.h:218
#define ereport(elevel,...)
Definition: elog.h:143
static StatsBuildData * make_build_data(Relation onerel, StatExtEntry *stat, int numrows, HeapTuple *rows, VacAttrStats **stats, int stattarget)
static int statext_compute_stattarget(int stattarget, int natts, VacAttrStats **stats)
static AnlExprData * build_expr_data(List *exprs, int stattarget)
static VacAttrStats ** lookup_var_attr_stats(Relation rel, Bitmapset *attrs, List *exprs, int nvacatts, VacAttrStats **vacatts)
static List * fetch_statentries_for_relation(Relation pg_statext, Oid relid)
static void compute_expr_stats(Relation onerel, double totalrows, AnlExprData *exprdata, int nexprs, HeapTuple *rows, int numrows)
static void statext_store(Oid statOid, MVNDistinct *ndistinct, MVDependencies *dependencies, MCVList *mcv, Datum exprs, VacAttrStats **stats)
static Datum serialize_expr_stats(AnlExprData *exprdata, int nexprs)
long val
Definition: informix.c:664
void list_free(List *list)
Definition: list.c:1505
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3316
MCVList * statext_mcv_build(StatsBuildData *data, double totalrows, int stattarget)
Definition: mcv.c:184
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:143
MemoryContext CurrentMemoryContext
Definition: mcxt.c:42
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:218
#define AllocSetContextCreate
Definition: memutils.h:173
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:195
MVNDistinct * statext_ndistinct_build(double totalrows, StatsBuildData *data)
Definition: mvdistinct.c:89
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:109
const void * data
#define lfirst(lc)
Definition: pg_list.h:169
static int list_length(const List *l)
Definition: pg_list.h:149
#define NIL
Definition: pg_list.h:65
#define lfirst_int(lc)
Definition: pg_list.h:170
uintptr_t Datum
Definition: postgres.h:411
#define PROGRESS_ANALYZE_EXT_STATS_COMPUTED
Definition: progress.h:42
#define PROGRESS_ANALYZE_PHASE
Definition: progress.h:38
#define PROGRESS_ANALYZE_PHASE_COMPUTE_EXT_STATS
Definition: progress.h:51
#define PROGRESS_ANALYZE_EXT_STATS_TOTAL
Definition: progress.h:41
#define RelationGetRelid(relation)
Definition: rel.h:478
#define RelationGetRelationName(relation)
Definition: rel.h:512
int errtable(Relation rel)
Definition: relcache.c:5753
Definition: pg_list.h:51
Form_pg_class rd_rel
Definition: rel.h:109
Definition: type.h:90
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, bms_num_members(), build_expr_data(), compute_expr_stats(), CurrentMemoryContext, data, elog, ereport, errcode(), errmsg(), ERROR, errtable(), fetch_statentries_for_relation(), get_namespace_name(), IsAutoVacuumWorkerProcess(), lfirst, lfirst_int, list_free(), list_length(), lookup_var_attr_stats(), make_build_data(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NIL, pgstat_progress_update_multi_param(), pgstat_progress_update_param(), PROGRESS_ANALYZE_EXT_STATS_COMPUTED, PROGRESS_ANALYZE_EXT_STATS_TOTAL, PROGRESS_ANALYZE_PHASE, PROGRESS_ANALYZE_PHASE_COMPUTE_EXT_STATS, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, serialize_expr_stats(), statext_compute_stattarget(), statext_dependencies_build(), statext_mcv_build(), statext_ndistinct_build(), statext_store(), table_close(), table_open(), val, and WARNING.

Referenced by do_analyze_rel().

◆ choose_best_statistics()

StatisticExtInfo* choose_best_statistics ( List stats,
char  requiredkind,
Bitmapset **  clause_attnums,
List **  clause_exprs,
int  nclauses 
)

Definition at line 1237 of file extended_stats.c.

1240 {
1241  ListCell *lc;
1242  StatisticExtInfo *best_match = NULL;
1243  int best_num_matched = 2; /* goal #1: maximize */
1244  int best_match_keys = (STATS_MAX_DIMENSIONS + 1); /* goal #2: minimize */
1245 
1246  foreach(lc, stats)
1247  {
1248  int i;
1249  StatisticExtInfo *info = (StatisticExtInfo *) lfirst(lc);
1250  Bitmapset *matched_attnums = NULL;
1251  Bitmapset *matched_exprs = NULL;
1252  int num_matched;
1253  int numkeys;
1254 
1255  /* skip statistics that are not of the correct type */
1256  if (info->kind != requiredkind)
1257  continue;
1258 
1259  /*
1260  * Collect attributes and expressions in remaining (unestimated)
1261  * clauses fully covered by this statistic object.
1262  *
1263  * We know already estimated clauses have both clause_attnums and
1264  * clause_exprs set to NULL. We leave the pointers NULL if already
1265  * estimated, or we reset them to NULL after estimating the clause.
1266  */
1267  for (i = 0; i < nclauses; i++)
1268  {
1269  Bitmapset *expr_idxs = NULL;
1270 
1271  /* ignore incompatible/estimated clauses */
1272  if (!clause_attnums[i] && !clause_exprs[i])
1273  continue;
1274 
1275  /* ignore clauses that are not covered by this object */
1276  if (!bms_is_subset(clause_attnums[i], info->keys) ||
1277  !stat_covers_expressions(info, clause_exprs[i], &expr_idxs))
1278  continue;
1279 
1280  /* record attnums and indexes of expressions covered */
1281  matched_attnums = bms_add_members(matched_attnums, clause_attnums[i]);
1282  matched_exprs = bms_add_members(matched_exprs, expr_idxs);
1283  }
1284 
1285  num_matched = bms_num_members(matched_attnums) + bms_num_members(matched_exprs);
1286 
1287  bms_free(matched_attnums);
1288  bms_free(matched_exprs);
1289 
1290  /*
1291  * save the actual number of keys in the stats so that we can choose
1292  * the narrowest stats with the most matching keys.
1293  */
1294  numkeys = bms_num_members(info->keys) + list_length(info->exprs);
1295 
1296  /*
1297  * Use this object when it increases the number of matched attributes
1298  * and expressions or when it matches the same number of attributes
1299  * and expressions but these stats have fewer keys than any previous
1300  * match.
1301  */
1302  if (num_matched > best_num_matched ||
1303  (num_matched == best_num_matched && numkeys < best_match_keys))
1304  {
1305  best_match = info;
1306  best_num_matched = num_matched;
1307  best_match_keys = numkeys;
1308  }
1309  }
1310 
1311  return best_match;
1312 }
bool bms_is_subset(const Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:315
void bms_free(Bitmapset *a)
Definition: bitmapset.c:208
Bitmapset * bms_add_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:795
static bool stat_covers_expressions(StatisticExtInfo *stat, List *exprs, Bitmapset **expr_idxs)
int i
Definition: isn.c:73
#define STATS_MAX_DIMENSIONS
Definition: statistics.h:19
Bitmapset * keys
Definition: pathnodes.h:939

References bms_add_members(), bms_free(), bms_is_subset(), bms_num_members(), StatisticExtInfo::exprs, i, StatisticExtInfo::keys, StatisticExtInfo::kind, lfirst, list_length(), stat_covers_expressions(), and STATS_MAX_DIMENSIONS.

Referenced by statext_mcv_clauselist_selectivity().

◆ ComputeExtStatisticsRows()

int ComputeExtStatisticsRows ( Relation  onerel,
int  natts,
VacAttrStats **  stats 
)

Definition at line 265 of file extended_stats.c.

267 {
268  Relation pg_stext;
269  ListCell *lc;
270  List *lstats;
271  MemoryContext cxt;
272  MemoryContext oldcxt;
273  int result = 0;
274 
275  /* If there are no columns to analyze, just return 0. */
276  if (!natts)
277  return 0;
278 
280  "ComputeExtStatisticsRows",
282  oldcxt = MemoryContextSwitchTo(cxt);
283 
284  pg_stext = table_open(StatisticExtRelationId, RowExclusiveLock);
285  lstats = fetch_statentries_for_relation(pg_stext, RelationGetRelid(onerel));
286 
287  foreach(lc, lstats)
288  {
290  int stattarget;
291  VacAttrStats **stats;
292  int nattrs = bms_num_members(stat->columns);
293 
294  /*
295  * Check if we can build this statistics object based on the columns
296  * analyzed. If not, ignore it (don't report anything, we'll do that
297  * during the actual build BuildRelationExtStatistics).
298  */
299  stats = lookup_var_attr_stats(onerel, stat->columns, stat->exprs,
300  natts, vacattrstats);
301 
302  if (!stats)
303  continue;
304 
305  /*
306  * Compute statistics target, based on what's set for the statistic
307  * object itself, and for its attributes.
308  */
309  stattarget = statext_compute_stattarget(stat->stattarget,
310  nattrs, stats);
311 
312  /* Use the largest value for all statistics objects. */
313  if (stattarget > result)
314  result = stattarget;
315  }
316 
317  table_close(pg_stext, RowExclusiveLock);
318 
319  MemoryContextSwitchTo(oldcxt);
320  MemoryContextDelete(cxt);
321 
322  /* compute sample size based on the statistics target */
323  return (300 * result);
324 }

References ALLOCSET_DEFAULT_SIZES, AllocSetContextCreate, bms_num_members(), CurrentMemoryContext, fetch_statentries_for_relation(), lfirst, lookup_var_attr_stats(), MemoryContextDelete(), MemoryContextSwitchTo(), RelationGetRelid, RowExclusiveLock, statext_compute_stattarget(), table_close(), and table_open().

Referenced by do_analyze_rel().

◆ dependencies_clauselist_selectivity()

Selectivity dependencies_clauselist_selectivity ( PlannerInfo root,
List clauses,
int  varRelid,
JoinType  jointype,
SpecialJoinInfo sjinfo,
RelOptInfo rel,
Bitmapset **  estimatedclauses 
)

Definition at line 1393 of file dependencies.c.

1400 {
1401  Selectivity s1 = 1.0;
1402  ListCell *l;
1403  Bitmapset *clauses_attnums = NULL;
1404  AttrNumber *list_attnums;
1405  int listidx;
1406  MVDependencies **func_dependencies;
1407  int nfunc_dependencies;
1408  int total_ndeps;
1409  MVDependency **dependencies;
1410  int ndependencies;
1411  int i;
1412  AttrNumber attnum_offset;
1413 
1414  /* unique expressions */
1415  Node **unique_exprs;
1416  int unique_exprs_cnt;
1417 
1418  /* check if there's any stats that might be useful for us. */
1419  if (!has_stats_of_kind(rel->statlist, STATS_EXT_DEPENDENCIES))
1420  return 1.0;
1421 
1422  list_attnums = (AttrNumber *) palloc(sizeof(AttrNumber) *
1423  list_length(clauses));
1424 
1425  /*
1426  * We allocate space as if every clause was a unique expression, although
1427  * that's probably overkill. Some will be simple column references that
1428  * we'll translate to attnums, and there might be duplicates. But it's
1429  * easier and cheaper to just do one allocation than repalloc later.
1430  */
1431  unique_exprs = (Node **) palloc(sizeof(Node *) * list_length(clauses));
1432  unique_exprs_cnt = 0;
1433 
1434  /*
1435  * Pre-process the clauses list to extract the attnums seen in each item.
1436  * We need to determine if there's any clauses which will be useful for
1437  * dependency selectivity estimations. Along the way we'll record all of
1438  * the attnums for each clause in a list which we'll reference later so we
1439  * don't need to repeat the same work again. We'll also keep track of all
1440  * attnums seen.
1441  *
1442  * We also skip clauses that we already estimated using different types of
1443  * statistics (we treat them as incompatible).
1444  *
1445  * To handle expressions, we assign them negative attnums, as if it was a
1446  * system attribute (this is fine, as we only allow extended stats on user
1447  * attributes). And then we offset everything by the number of
1448  * expressions, so that we can store the values in a bitmapset.
1449  */
1450  listidx = 0;
1451  foreach(l, clauses)
1452  {
1453  Node *clause = (Node *) lfirst(l);
1455  Node *expr = NULL;
1456 
1457  /* ignore clause by default */
1458  list_attnums[listidx] = InvalidAttrNumber;
1459 
1460  if (!bms_is_member(listidx, *estimatedclauses))
1461  {
1462  /*
1463  * If it's a simple column reference, just extract the attnum. If
1464  * it's an expression, assign a negative attnum as if it was a
1465  * system attribute.
1466  */
1467  if (dependency_is_compatible_clause(clause, rel->relid, &attnum))
1468  {
1469  list_attnums[listidx] = attnum;
1470  }
1471  else if (dependency_is_compatible_expression(clause, rel->relid,
1472  rel->statlist,
1473  &expr))
1474  {
1475  /* special attnum assigned to this expression */
1477 
1478  Assert(expr != NULL);
1479 
1480  /* If the expression is duplicate, use the same attnum. */
1481  for (i = 0; i < unique_exprs_cnt; i++)
1482  {
1483  if (equal(unique_exprs[i], expr))
1484  {
1485  /* negative attribute number to expression */
1486  attnum = -(i + 1);
1487  break;
1488  }
1489  }
1490 
1491  /* not found in the list, so add it */
1492  if (attnum == InvalidAttrNumber)
1493  {
1494  unique_exprs[unique_exprs_cnt++] = expr;
1495 
1496  /* after incrementing the value, to get -1, -2, ... */
1497  attnum = (-unique_exprs_cnt);
1498  }
1499 
1500  /* remember which attnum was assigned to this clause */
1501  list_attnums[listidx] = attnum;
1502  }
1503  }
1504 
1505  listidx++;
1506  }
1507 
1508  Assert(listidx == list_length(clauses));
1509 
1510  /*
1511  * How much we need to offset the attnums? If there are no expressions,
1512  * then no offset is needed. Otherwise we need to offset enough for the
1513  * lowest value (-unique_exprs_cnt) to become 1.
1514  */
1515  if (unique_exprs_cnt > 0)
1516  attnum_offset = (unique_exprs_cnt + 1);
1517  else
1518  attnum_offset = 0;
1519 
1520  /*
1521  * Now that we know how many expressions there are, we can offset the
1522  * values just enough to build the bitmapset.
1523  */
1524  for (i = 0; i < list_length(clauses); i++)
1525  {
1527 
1528  /* ignore incompatible or already estimated clauses */
1529  if (list_attnums[i] == InvalidAttrNumber)
1530  continue;
1531 
1532  /* make sure the attnum is in the expected range */
1533  Assert(list_attnums[i] >= (-unique_exprs_cnt));
1534  Assert(list_attnums[i] <= MaxHeapAttributeNumber);
1535 
1536  /* make sure the attnum is positive (valid AttrNumber) */
1537  attnum = list_attnums[i] + attnum_offset;
1538 
1539  /*
1540  * Either it's a regular attribute, or it's an expression, in which
1541  * case we must not have seen it before (expressions are unique).
1542  *
1543  * XXX Check whether it's a regular attribute has to be done using the
1544  * original attnum, while the second check has to use the value with
1545  * an offset.
1546  */
1547  Assert(AttrNumberIsForUserDefinedAttr(list_attnums[i]) ||
1548  !bms_is_member(attnum, clauses_attnums));
1549 
1550  /*
1551  * Remember the offset attnum, both for attributes and expressions.
1552  * We'll pass list_attnums to clauselist_apply_dependencies, which
1553  * uses it to identify clauses in a bitmap. We could also pass the
1554  * offset, but this is more convenient.
1555  */
1556  list_attnums[i] = attnum;
1557 
1558  clauses_attnums = bms_add_member(clauses_attnums, attnum);
1559  }
1560 
1561  /*
1562  * If there's not at least two distinct attnums and expressions, then
1563  * reject the whole list of clauses. We must return 1.0 so the calling
1564  * function's selectivity is unaffected.
1565  */
1566  if (bms_membership(clauses_attnums) != BMS_MULTIPLE)
1567  {
1568  bms_free(clauses_attnums);
1569  pfree(list_attnums);
1570  return 1.0;
1571  }
1572 
1573  /*
1574  * Load all functional dependencies matching at least two parameters. We
1575  * can simply consider all dependencies at once, without having to search
1576  * for the best statistics object.
1577  *
1578  * To not waste cycles and memory, we deserialize dependencies only for
1579  * statistics that match at least two attributes. The array is allocated
1580  * with the assumption that all objects match - we could grow the array to
1581  * make it just the right size, but it's likely wasteful anyway thanks to
1582  * moving the freed chunks to freelists etc.
1583  */
1584  func_dependencies = (MVDependencies **) palloc(sizeof(MVDependencies *) *
1585  list_length(rel->statlist));
1586  nfunc_dependencies = 0;
1587  total_ndeps = 0;
1588 
1589  foreach(l, rel->statlist)
1590  {
1592  int nmatched;
1593  int nexprs;
1594  int k;
1595  MVDependencies *deps;
1596 
1597  /* skip statistics that are not of the correct type */
1598  if (stat->kind != STATS_EXT_DEPENDENCIES)
1599  continue;
1600 
1601  /*
1602  * Count matching attributes - we have to undo the attnum offsets. The
1603  * input attribute numbers are not offset (expressions are not
1604  * included in stat->keys, so it's not necessary). But we need to
1605  * offset it before checking against clauses_attnums.
1606  */
1607  nmatched = 0;
1608  k = -1;
1609  while ((k = bms_next_member(stat->keys, k)) >= 0)
1610  {
1612 
1613  /* skip expressions */
1615  continue;
1616 
1617  /* apply the same offset as above */
1618  attnum += attnum_offset;
1619 
1620  if (bms_is_member(attnum, clauses_attnums))
1621  nmatched++;
1622  }
1623 
1624  /* count matching expressions */
1625  nexprs = 0;
1626  for (i = 0; i < unique_exprs_cnt; i++)
1627  {
1628  ListCell *lc;
1629 
1630  foreach(lc, stat->exprs)
1631  {
1632  Node *stat_expr = (Node *) lfirst(lc);
1633 
1634  /* try to match it */
1635  if (equal(stat_expr, unique_exprs[i]))
1636  nexprs++;
1637  }
1638  }
1639 
1640  /*
1641  * Skip objects matching fewer than two attributes/expressions from
1642  * clauses.
1643  */
1644  if (nmatched + nexprs < 2)
1645  continue;
1646 
1647  deps = statext_dependencies_load(stat->statOid);
1648 
1649  /*
1650  * The expressions may be represented by different attnums in the
1651  * stats, we need to remap them to be consistent with the clauses.
1652  * That will make the later steps (e.g. picking the strongest item and
1653  * so on) much simpler and cheaper, because it won't need to care
1654  * about the offset at all.
1655  *
1656  * When we're at it, we can ignore dependencies that are not fully
1657  * matched by clauses (i.e. referencing attributes or expressions that
1658  * are not in the clauses).
1659  *
1660  * We have to do this for all statistics, as long as there are any
1661  * expressions - we need to shift the attnums in all dependencies.
1662  *
1663  * XXX Maybe we should do this always, because it also eliminates some
1664  * of the dependencies early. It might be cheaper than having to walk
1665  * the longer list in find_strongest_dependency later, especially as
1666  * we need to do that repeatedly?
1667  *
1668  * XXX We have to do this even when there are no expressions in
1669  * clauses, otherwise find_strongest_dependency may fail for stats
1670  * with expressions (due to lookup of negative value in bitmap). So we
1671  * need to at least filter out those dependencies. Maybe we could do
1672  * it in a cheaper way (if there are no expr clauses, we can just
1673  * discard all negative attnums without any lookups).
1674  */
1675  if (unique_exprs_cnt > 0 || stat->exprs != NIL)
1676  {
1677  int ndeps = 0;
1678 
1679  for (i = 0; i < deps->ndeps; i++)
1680  {
1681  bool skip = false;
1682  MVDependency *dep = deps->deps[i];
1683  int j;
1684 
1685  for (j = 0; j < dep->nattributes; j++)
1686  {
1687  int idx;
1688  Node *expr;
1689  int k;
1690  AttrNumber unique_attnum = InvalidAttrNumber;
1692 
1693  /* undo the per-statistics offset */
1694  attnum = dep->attributes[j];
1695 
1696  /*
1697  * For regular attributes we can simply check if it
1698  * matches any clause. If there's no matching clause, we
1699  * can just ignore it. We need to offset the attnum
1700  * though.
1701  */
1703  {
1704  dep->attributes[j] = attnum + attnum_offset;
1705 
1706  if (!bms_is_member(dep->attributes[j], clauses_attnums))
1707  {
1708  skip = true;
1709  break;
1710  }
1711 
1712  continue;
1713  }
1714 
1715  /*
1716  * the attnum should be a valid system attnum (-1, -2,
1717  * ...)
1718  */
1720 
1721  /*
1722  * For expressions, we need to do two translations. First
1723  * we have to translate the negative attnum to index in
1724  * the list of expressions (in the statistics object).
1725  * Then we need to see if there's a matching clause. The
1726  * index of the unique expression determines the attnum
1727  * (and we offset it).
1728  */
1729  idx = -(1 + attnum);
1730 
1731  /* Is the expression index is valid? */
1732  Assert((idx >= 0) && (idx < list_length(stat->exprs)));
1733 
1734  expr = (Node *) list_nth(stat->exprs, idx);
1735 
1736  /* try to find the expression in the unique list */
1737  for (k = 0; k < unique_exprs_cnt; k++)
1738  {
1739  /*
1740  * found a matching unique expression, use the attnum
1741  * (derived from index of the unique expression)
1742  */
1743  if (equal(unique_exprs[k], expr))
1744  {
1745  unique_attnum = -(k + 1) + attnum_offset;
1746  break;
1747  }
1748  }
1749 
1750  /*
1751  * Found no matching expression, so we can simply skip
1752  * this dependency, because there's no chance it will be
1753  * fully covered.
1754  */
1755  if (unique_attnum == InvalidAttrNumber)
1756  {
1757  skip = true;
1758  break;
1759  }
1760 
1761  /* otherwise remap it to the new attnum */
1762  dep->attributes[j] = unique_attnum;
1763  }
1764 
1765  /* if found a matching dependency, keep it */
1766  if (!skip)
1767  {
1768  /* maybe we've skipped something earlier, so move it */
1769  if (ndeps != i)
1770  deps->deps[ndeps] = deps->deps[i];
1771 
1772  ndeps++;
1773  }
1774  }
1775 
1776  deps->ndeps = ndeps;
1777  }
1778 
1779  /*
1780  * It's possible we've removed all dependencies, in which case we
1781  * don't bother adding it to the list.
1782  */
1783  if (deps->ndeps > 0)
1784  {
1785  func_dependencies[nfunc_dependencies] = deps;
1786  total_ndeps += deps->ndeps;
1787  nfunc_dependencies++;
1788  }
1789  }
1790 
1791  /* if no matching stats could be found then we've nothing to do */
1792  if (nfunc_dependencies == 0)
1793  {
1794  pfree(func_dependencies);
1795  bms_free(clauses_attnums);
1796  pfree(list_attnums);
1797  pfree(unique_exprs);
1798  return 1.0;
1799  }
1800 
1801  /*
1802  * Work out which dependencies we can apply, starting with the
1803  * widest/strongest ones, and proceeding to smaller/weaker ones.
1804  */
1805  dependencies = (MVDependency **) palloc(sizeof(MVDependency *) *
1806  total_ndeps);
1807  ndependencies = 0;
1808 
1809  while (true)
1810  {
1811  MVDependency *dependency;
1813 
1814  /* the widest/strongest dependency, fully matched by clauses */
1815  dependency = find_strongest_dependency(func_dependencies,
1816  nfunc_dependencies,
1817  clauses_attnums);
1818  if (!dependency)
1819  break;
1820 
1821  dependencies[ndependencies++] = dependency;
1822 
1823  /* Ignore dependencies using this implied attribute in later loops */
1824  attnum = dependency->attributes[dependency->nattributes - 1];
1825  clauses_attnums = bms_del_member(clauses_attnums, attnum);
1826  }
1827 
1828  /*
1829  * If we found applicable dependencies, use them to estimate all
1830  * compatible clauses on attributes that they refer to.
1831  */
1832  if (ndependencies != 0)
1833  s1 = clauselist_apply_dependencies(root, clauses, varRelid, jointype,
1834  sjinfo, dependencies, ndependencies,
1835  list_attnums, estimatedclauses);
1836 
1837  /* free deserialized functional dependencies (and then the array) */
1838  for (i = 0; i < nfunc_dependencies; i++)
1839  pfree(func_dependencies[i]);
1840 
1841  pfree(dependencies);
1842  pfree(func_dependencies);
1843  bms_free(clauses_attnums);
1844  pfree(list_attnums);
1845  pfree(unique_exprs);
1846 
1847  return s1;
1848 }
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:259
int16 AttrNumber
Definition: attnum.h:21
#define AttributeNumberIsValid(attributeNumber)
Definition: attnum.h:34
#define AttrNumberIsForUserDefinedAttr(attributeNumber)
Definition: attnum.h:41
#define InvalidAttrNumber
Definition: attnum.h:23
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1045
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:427
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:738
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition: bitmapset.c:775
BMS_Membership bms_membership(const Bitmapset *a)
Definition: bitmapset.c:674
@ BMS_MULTIPLE
Definition: bitmapset.h:70
static bool dependency_is_compatible_expression(Node *clause, Index relid, List *statlist, Node **expr)
static bool dependency_is_compatible_clause(Node *clause, Index relid, AttrNumber *attnum)
Definition: dependencies.c:741
static Selectivity clauselist_apply_dependencies(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, MVDependency **dependencies, int ndependencies, AttrNumber *list_attnums, Bitmapset **estimatedclauses)
MVDependencies * statext_dependencies_load(Oid mvoid)
Definition: dependencies.c:621
static MVDependency * find_strongest_dependency(MVDependencies **dependencies, int ndependencies, Bitmapset *attnums)
Definition: dependencies.c:929
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3161
bool has_stats_of_kind(List *stats, char requiredkind)
#define MaxHeapAttributeNumber
Definition: htup_details.h:47
int j
Definition: isn.c:74
Assert(fmt[strlen(fmt) - 1] !='\n')
void pfree(void *pointer)
Definition: mcxt.c:1169
void * palloc(Size size)
Definition: mcxt.c:1062
double Selectivity
Definition: nodes.h:671
int16 attnum
Definition: pg_attribute.h:83
static const struct exclude_list_item skip[]
Definition: pg_checksums.c:116
static void * list_nth(const List *list, int n)
Definition: pg_list.h:278
char * s1
uint32 ndeps
Definition: statistics.h:61
MVDependency * deps[FLEXIBLE_ARRAY_MEMBER]
Definition: statistics.h:62
AttrNumber nattributes
Definition: statistics.h:53
AttrNumber attributes[FLEXIBLE_ARRAY_MEMBER]
Definition: statistics.h:54
Definition: nodes.h:539
Index relid
Definition: pathnodes.h:709
List * statlist
Definition: pathnodes.h:719

References Assert(), attnum, AttributeNumberIsValid, MVDependency::attributes, AttrNumberIsForUserDefinedAttr, bms_add_member(), bms_del_member(), bms_free(), bms_is_member(), bms_membership(), BMS_MULTIPLE, bms_next_member(), clauselist_apply_dependencies(), dependency_is_compatible_clause(), dependency_is_compatible_expression(), MVDependencies::deps, equal(), find_strongest_dependency(), has_stats_of_kind(), i, idx(), InvalidAttrNumber, j, lfirst, list_length(), list_nth(), MaxHeapAttributeNumber, MVDependency::nattributes, MVDependencies::ndeps, NIL, palloc(), pfree(), RelOptInfo::relid, s1, skip, statext_dependencies_load(), and RelOptInfo::statlist.

Referenced by statext_clauselist_selectivity().

◆ has_stats_of_kind()

bool has_stats_of_kind ( List stats,
char  requiredkind 
)

Definition at line 1146 of file extended_stats.c.

1147 {
1148  ListCell *l;
1149 
1150  foreach(l, stats)
1151  {
1153 
1154  if (stat->kind == requiredkind)
1155  return true;
1156  }
1157 
1158  return false;
1159 }

References lfirst.

Referenced by dependencies_clauselist_selectivity(), and statext_mcv_clauselist_selectivity().

◆ statext_clauselist_selectivity()

Selectivity statext_clauselist_selectivity ( PlannerInfo root,
List clauses,
int  varRelid,
JoinType  jointype,
SpecialJoinInfo sjinfo,
RelOptInfo rel,
Bitmapset **  estimatedclauses,
bool  is_or 
)

Definition at line 1974 of file extended_stats.c.

1978 {
1979  Selectivity sel;
1980 
1981  /* First, try estimating clauses using a multivariate MCV list. */
1982  sel = statext_mcv_clauselist_selectivity(root, clauses, varRelid, jointype,
1983  sjinfo, rel, estimatedclauses, is_or);
1984 
1985  /*
1986  * Functional dependencies only work for clauses connected by AND, so for
1987  * OR clauses we're done.
1988  */
1989  if (is_or)
1990  return sel;
1991 
1992  /*
1993  * Then, apply functional dependencies on the remaining clauses by calling
1994  * dependencies_clauselist_selectivity. Pass 'estimatedclauses' so the
1995  * function can properly skip clauses already estimated above.
1996  *
1997  * The reasoning for applying dependencies last is that the more complex
1998  * stats can track more complex correlations between the attributes, and
1999  * so may be considered more reliable.
2000  *
2001  * For example, MCV list can give us an exact selectivity for values in
2002  * two columns, while functional dependencies can only provide information
2003  * about the overall strength of the dependency.
2004  */
2005  sel *= dependencies_clauselist_selectivity(root, clauses, varRelid,
2006  jointype, sjinfo, rel,
2007  estimatedclauses);
2008 
2009  return sel;
2010 }
Selectivity dependencies_clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Bitmapset **estimatedclauses)
static Selectivity statext_mcv_clauselist_selectivity(PlannerInfo *root, List *clauses, int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo, RelOptInfo *rel, Bitmapset **estimatedclauses, bool is_or)

References dependencies_clauselist_selectivity(), and statext_mcv_clauselist_selectivity().

Referenced by clauselist_selectivity_ext(), and clauselist_selectivity_or().

◆ statext_dependencies_load()

MVDependencies* statext_dependencies_load ( Oid  mvoid)

Definition at line 621 of file dependencies.c.

622 {
623  MVDependencies *result;
624  bool isnull;
625  Datum deps;
626  HeapTuple htup;
627 
629  if (!HeapTupleIsValid(htup))
630  elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
631 
632  deps = SysCacheGetAttr(STATEXTDATASTXOID, htup,
633  Anum_pg_statistic_ext_data_stxddependencies, &isnull);
634  if (isnull)
635  elog(ERROR,
636  "requested statistics kind \"%c\" is not yet built for statistics object %u",
637  STATS_EXT_DEPENDENCIES, mvoid);
638 
640 
641  ReleaseSysCache(htup);
642 
643  return result;
644 }
MVDependencies * statext_dependencies_deserialize(bytea *data)
Definition: dependencies.c:501
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1198
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1150
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:1411
@ STATEXTDATASTXOID
Definition: syscache.h:92

References DatumGetByteaPP, elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1(), statext_dependencies_deserialize(), STATEXTDATASTXOID, and SysCacheGetAttr().

Referenced by dependencies_clauselist_selectivity().

◆ statext_expressions_load()

HeapTuple statext_expressions_load ( Oid  stxoid,
int  idx 
)

Definition at line 2399 of file extended_stats.c.

2400 {
2401  bool isnull;
2402  Datum value;
2403  HeapTuple htup;
2404  ExpandedArrayHeader *eah;
2405  HeapTupleHeader td;
2406  HeapTupleData tmptup;
2407  HeapTuple tup;
2408 
2410  if (!HeapTupleIsValid(htup))
2411  elog(ERROR, "cache lookup failed for statistics object %u", stxoid);
2412 
2414  Anum_pg_statistic_ext_data_stxdexpr, &isnull);
2415  if (isnull)
2416  elog(ERROR,
2417  "requested statistics kind \"%c\" is not yet built for statistics object %u",
2418  STATS_EXT_DEPENDENCIES, stxoid);
2419 
2421 
2423 
2424  td = DatumGetHeapTupleHeader(eah->dvalues[idx]);
2425 
2426  /* Build a temporary HeapTuple control structure */
2427  tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
2428  ItemPointerSetInvalid(&(tmptup.t_self));
2429  tmptup.t_tableOid = InvalidOid;
2430  tmptup.t_data = td;
2431 
2432  tup = heap_copytuple(&tmptup);
2433 
2434  ReleaseSysCache(htup);
2435 
2436  return tup;
2437 }
ExpandedArrayHeader * DatumGetExpandedArray(Datum d)
void deconstruct_expanded_array(ExpandedArrayHeader *eah)
#define DatumGetHeapTupleHeader(X)
Definition: fmgr.h:295
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
#define HeapTupleHeaderGetDatumLength(tup)
Definition: htup_details.h:446
static struct @142 value
#define ItemPointerSetInvalid(pointer)
Definition: itemptr.h:172
#define InvalidOid
Definition: postgres_ext.h:36
Datum * dvalues
Definition: array.h:139
ItemPointerData t_self
Definition: htup.h:65
uint32 t_len
Definition: htup.h:64
HeapTupleHeader t_data
Definition: htup.h:68
Oid t_tableOid
Definition: htup.h:66

References DatumGetExpandedArray(), DatumGetHeapTupleHeader, deconstruct_expanded_array(), ExpandedArrayHeader::dvalues, elog, ERROR, heap_copytuple(), HeapTupleHeaderGetDatumLength, HeapTupleIsValid, idx(), InvalidOid, ItemPointerSetInvalid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1(), STATEXTDATASTXOID, SysCacheGetAttr(), HeapTupleData::t_data, HeapTupleData::t_len, HeapTupleData::t_self, HeapTupleData::t_tableOid, and value.

Referenced by examine_variable().

◆ statext_is_kind_built()

bool statext_is_kind_built ( HeapTuple  htup,
char  kind 
)

Definition at line 389 of file extended_stats.c.

390 {
392 
393  switch (type)
394  {
395  case STATS_EXT_NDISTINCT:
396  attnum = Anum_pg_statistic_ext_data_stxdndistinct;
397  break;
398 
399  case STATS_EXT_DEPENDENCIES:
400  attnum = Anum_pg_statistic_ext_data_stxddependencies;
401  break;
402 
403  case STATS_EXT_MCV:
404  attnum = Anum_pg_statistic_ext_data_stxdmcv;
405  break;
406 
407  case STATS_EXT_EXPRESSIONS:
408  attnum = Anum_pg_statistic_ext_data_stxdexpr;
409  break;
410 
411  default:
412  elog(ERROR, "unexpected statistics type requested: %d", type);
413  }
414 
415  return !heap_attisnull(htup, attnum, NULL);
416 }
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:359

References attnum, elog, ERROR, heap_attisnull(), and generate_unaccent_rules::type.

Referenced by get_relation_statistics().

◆ statext_mcv_load()

MCVList* statext_mcv_load ( Oid  mvoid)

Definition at line 562 of file mcv.c.

563 {
564  MCVList *result;
565  bool isnull;
566  Datum mcvlist;
568 
569  if (!HeapTupleIsValid(htup))
570  elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
571 
572  mcvlist = SysCacheGetAttr(STATEXTDATASTXOID, htup,
573  Anum_pg_statistic_ext_data_stxdmcv, &isnull);
574 
575  if (isnull)
576  elog(ERROR,
577  "requested statistics kind \"%c\" is not yet built for statistics object %u",
578  STATS_EXT_DEPENDENCIES, mvoid);
579 
580  result = statext_mcv_deserialize(DatumGetByteaP(mcvlist));
581 
582  ReleaseSysCache(htup);
583 
584  return result;
585 }
#define DatumGetByteaP(X)
Definition: fmgr.h:331
MCVList * statext_mcv_deserialize(bytea *data)
Definition: mcv.c:999

References DatumGetByteaP, elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1(), statext_mcv_deserialize(), STATEXTDATASTXOID, and SysCacheGetAttr().

Referenced by mcv_clauselist_selectivity(), and statext_mcv_clauselist_selectivity().

◆ statext_ndistinct_load()

MVNDistinct* statext_ndistinct_load ( Oid  mvoid)

Definition at line 149 of file mvdistinct.c.

150 {
151  MVNDistinct *result;
152  bool isnull;
153  Datum ndist;
154  HeapTuple htup;
155 
157  if (!HeapTupleIsValid(htup))
158  elog(ERROR, "cache lookup failed for statistics object %u", mvoid);
159 
160  ndist = SysCacheGetAttr(STATEXTDATASTXOID, htup,
161  Anum_pg_statistic_ext_data_stxdndistinct, &isnull);
162  if (isnull)
163  elog(ERROR,
164  "requested statistics kind \"%c\" is not yet built for statistics object %u",
165  STATS_EXT_NDISTINCT, mvoid);
166 
168 
169  ReleaseSysCache(htup);
170 
171  return result;
172 }
MVNDistinct * statext_ndistinct_deserialize(bytea *data)
Definition: mvdistinct.c:250

References DatumGetByteaPP, elog, ERROR, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1(), statext_ndistinct_deserialize(), STATEXTDATASTXOID, and SysCacheGetAttr().

Referenced by estimate_multivariate_ndistinct().