PostgreSQL Source Code  git master
statscmds.c File Reference
#include "postgres.h"
#include "access/heapam.h"
#include "access/relation.h"
#include "access/relscan.h"
#include "access/table.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_statistic_ext_data.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "statistics/statistics.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/syscache.h"
#include "utils/typcache.h"
Include dependency graph for statscmds.c:

Go to the source code of this file.

Functions

static char * ChooseExtendedStatisticName (const char *name1, const char *name2, const char *label, Oid namespaceid)
 
static char * ChooseExtendedStatisticNameAddition (List *exprs)
 
static int compare_int16 (const void *a, const void *b)
 
ObjectAddress CreateStatistics (CreateStatsStmt *stmt)
 
ObjectAddress AlterStatistics (AlterStatsStmt *stmt)
 
void RemoveStatisticsById (Oid statsOid)
 
Oid StatisticsGetRelation (Oid statId, bool missing_ok)
 

Function Documentation

◆ AlterStatistics()

ObjectAddress AlterStatistics ( AlterStatsStmt stmt)

Definition at line 579 of file statscmds.c.

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert, CatalogTupleUpdate(), DeconstructQualifiedName(), AlterStatsStmt::defnames, ereport, errcode(), errmsg(), ERROR, get_statistics_object_oid(), GetUserId(), heap_freetuple(), heap_modify_tuple(), Int32GetDatum, InvalidObjectAddress, InvokeObjectPostAlterHook, AlterStatsStmt::missing_ok, NameListToString(), NOTICE, OBJECT_STATISTIC_EXT, ObjectAddressSet, ObjectIdGetDatum, OidIsValid, pg_statistics_object_ownercheck(), RelationGetDescr, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), STATEXTOID, AlterStatsStmt::stxstattarget, HeapTupleData::t_self, table_close(), table_open(), and WARNING.

Referenced by ProcessUtilitySlow().

580 {
581  Relation rel;
582  Oid stxoid;
583  HeapTuple oldtup;
584  HeapTuple newtup;
585  Datum repl_val[Natts_pg_statistic_ext];
586  bool repl_null[Natts_pg_statistic_ext];
587  bool repl_repl[Natts_pg_statistic_ext];
588  ObjectAddress address;
589  int newtarget = stmt->stxstattarget;
590 
591  /* Limit statistics target to a sane range */
592  if (newtarget < -1)
593  {
594  ereport(ERROR,
595  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
596  errmsg("statistics target %d is too low",
597  newtarget)));
598  }
599  else if (newtarget > 10000)
600  {
601  newtarget = 10000;
603  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
604  errmsg("lowering statistics target to %d",
605  newtarget)));
606  }
607 
608  /* lookup OID of the statistics object */
609  stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
610 
611  /*
612  * If we got here and the OID is not valid, it means the statistics does
613  * not exist, but the command specified IF EXISTS. So report this as a
614  * simple NOTICE and we're done.
615  */
616  if (!OidIsValid(stxoid))
617  {
618  char *schemaname;
619  char *statname;
620 
621  Assert(stmt->missing_ok);
622 
623  DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
624 
625  if (schemaname)
626  ereport(NOTICE,
627  (errmsg("statistics object \"%s.%s\" does not exist, skipping",
628  schemaname, statname)));
629  else
630  ereport(NOTICE,
631  (errmsg("statistics object \"%s\" does not exist, skipping",
632  statname)));
633 
634  return InvalidObjectAddress;
635  }
636 
637  /* Search pg_statistic_ext */
638  rel = table_open(StatisticExtRelationId, RowExclusiveLock);
639 
640  oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
641 
642  /* Must be owner of the existing statistics object */
645  NameListToString(stmt->defnames));
646 
647  /* Build new tuple. */
648  memset(repl_val, 0, sizeof(repl_val));
649  memset(repl_null, false, sizeof(repl_null));
650  memset(repl_repl, false, sizeof(repl_repl));
651 
652  /* replace the stxstattarget column */
653  repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
654  repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(newtarget);
655 
656  newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
657  repl_val, repl_null, repl_repl);
658 
659  /* Update system catalog. */
660  CatalogTupleUpdate(rel, &newtup->t_self, newtup);
661 
662  InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
663 
664  ObjectAddressSet(address, StatisticExtRelationId, stxoid);
665 
666  /*
667  * NOTE: because we only support altering the statistics target, not the
668  * other fields, there is no need to update dependencies.
669  */
670 
671  heap_freetuple(newtup);
672  ReleaseSysCache(oldtup);
673 
675 
676  return address;
677 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define RelationGetDescr(relation)
Definition: rel.h:503
Oid GetUserId(void)
Definition: miscinit.c:478
int errcode(int sqlerrcode)
Definition: elog.c:698
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2854
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3308
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
Oid get_statistics_object_oid(List *names, bool missing_ok)
Definition: namespace.c:2223
#define RowExclusiveLock
Definition: lockdefs.h:38
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
#define WARNING
Definition: elog.h:40
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1127
char * NameListToString(List *names)
Definition: namespace.c:3147
uintptr_t Datum
Definition: postgres.h:411
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
#define ereport(elevel,...)
Definition: elog.h:157
#define NOTICE
Definition: elog.h:37
#define Assert(condition)
Definition: c.h:804
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define Int32GetDatum(X)
Definition: postgres.h:523
const ObjectAddress InvalidObjectAddress
int errmsg(const char *fmt,...)
Definition: elog.c:909
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
Definition: aclchk.c:5408

◆ ChooseExtendedStatisticName()

static char * ChooseExtendedStatisticName ( const char *  name1,
const char *  name2,
const char *  label,
Oid  namespaceid 
)
static

Definition at line 747 of file statscmds.c.

References GetSysCacheOid2, makeObjectName(), NAMEDATALEN, ObjectIdGetDatum, OidIsValid, pfree(), PointerGetDatum, snprintf, STATEXTNAMENSP, and strlcpy().

Referenced by CreateStatistics().

749 {
750  int pass = 0;
751  char *stxname = NULL;
752  char modlabel[NAMEDATALEN];
753 
754  /* try the unmodified label first */
755  strlcpy(modlabel, label, sizeof(modlabel));
756 
757  for (;;)
758  {
759  Oid existingstats;
760 
761  stxname = makeObjectName(name1, name2, modlabel);
762 
763  existingstats = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
764  PointerGetDatum(stxname),
765  ObjectIdGetDatum(namespaceid));
766  if (!OidIsValid(existingstats))
767  break;
768 
769  /* found a conflict, so try a new name component */
770  pfree(stxname);
771  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
772  }
773 
774  return stxname;
775 }
#define PointerGetDatum(X)
Definition: postgres.h:600
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:710
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2198
#define NAMEDATALEN
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
static char * label
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition: syscache.h:195
#define snprintf
Definition: port.h:216

◆ ChooseExtendedStatisticNameAddition()

static char * ChooseExtendedStatisticNameAddition ( List exprs)
static

Definition at line 789 of file statscmds.c.

References buf, IsA, lfirst, name, StatsElem::name, NAMEDATALEN, pstrdup(), and strlcpy().

Referenced by CreateStatistics().

790 {
791  char buf[NAMEDATALEN * 2];
792  int buflen = 0;
793  ListCell *lc;
794 
795  buf[0] = '\0';
796  foreach(lc, exprs)
797  {
798  StatsElem *selem = (StatsElem *) lfirst(lc);
799  const char *name;
800 
801  /* It should be one of these, but just skip if it happens not to be */
802  if (!IsA(selem, StatsElem))
803  continue;
804 
805  name = selem->name;
806 
807  if (buflen > 0)
808  buf[buflen++] = '_'; /* insert _ between names */
809 
810  /*
811  * We use fixed 'expr' for expressions, which have empty column names.
812  * For indexes this is handled in ChooseIndexColumnNames, but we have
813  * no such function for stats and it does not seem worth adding. If a
814  * better name is needed, the user can specify it explicitly.
815  */
816  if (!name)
817  name = "expr";
818 
819  /*
820  * At this point we have buflen <= NAMEDATALEN. name should be less
821  * than NAMEDATALEN already, but use strlcpy for paranoia.
822  */
823  strlcpy(buf + buflen, name, NAMEDATALEN);
824  buflen += strlen(buf + buflen);
825  if (buflen >= NAMEDATALEN)
826  break;
827  }
828  return pstrdup(buf);
829 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
char * name
Definition: parsenodes.h:2930
char * pstrdup(const char *in)
Definition: mcxt.c:1299
#define NAMEDATALEN
static char * buf
Definition: pg_test_fsync.c:68
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define lfirst(lc)
Definition: pg_list.h:169
const char * name
Definition: encode.c:515

◆ compare_int16()

static int compare_int16 ( const void *  a,
const void *  b 
)
static

Definition at line 51 of file statscmds.c.

References av.

Referenced by CreateStatistics().

52 {
53  int av = *(const int16 *) a;
54  int bv = *(const int16 *) b;
55 
56  /* this can't overflow if int is wider than int16 */
57  return (av - bv);
58 }
signed short int16
Definition: c.h:428
struct @11::@12 av[32]

◆ CreateStatistics()

ObjectAddress CreateStatistics ( CreateStatsStmt stmt)

Definition at line 64 of file statscmds.c.

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, Assert, attname, buildint2vector(), CacheInvalidateRelcache(), CatalogTupleInsert(), CharGetDatum, ChooseExtendedStatisticName(), ChooseExtendedStatisticNameAddition(), compare_int16(), construct_array(), CreateComments(), CStringGetDatum, CStringGetTextDatum, CreateStatsStmt::defnames, DEPENDENCY_AUTO, DEPENDENCY_NORMAL, equal(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail_relkind_not_supported(), errmsg(), ERROR, StatsElem::expr, CreateStatsStmt::exprs, exprType(), format_type_be(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, CreateStatsStmt::if_not_exists, Int32GetDatum, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHook, IsA, IsSystemRelation(), lappend(), lengthof, lfirst, lfirst_node, list_length(), lookup_type_cache(), TypeCacheEntry::lt_opr, StatsElem::name, NameGetDatum, namestrcpy(), NIL, nodeToString(), NoLock, NOTICE, ObjectAddressSet, ObjectAddressSubSet, ObjectIdGetDatum, pfree(), pg_class_ownercheck(), PointerGetDatum, qsort, QualifiedNameGetCreationNamespace(), RelationData::rd_att, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnOwner(), recordDependencyOnSingleRelExpr(), relation_close(), relation_openrv(), RelationGetNamespace, RelationGetRelationName, RelationGetRelid, CreateStatsStmt::relations, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheExists2, ShareUpdateExclusiveLock, CreateStatsStmt::stat_types, STATEXTNAMENSP, STATS_MAX_DIMENSIONS, strVal, CreateStatsStmt::stxcomment, table_open(), generate_unaccent_rules::type, TYPECACHE_LT_OPR, types, and values.

Referenced by ATExecAddStatistics(), and ProcessUtilitySlow().

65 {
66  int16 attnums[STATS_MAX_DIMENSIONS];
67  int nattnums = 0;
68  int numcols;
69  char *namestr;
70  NameData stxname;
71  Oid statoid;
72  Oid namespaceId;
73  Oid stxowner = GetUserId();
74  HeapTuple htup;
75  Datum values[Natts_pg_statistic_ext];
76  bool nulls[Natts_pg_statistic_ext];
77  Datum datavalues[Natts_pg_statistic_ext_data];
78  bool datanulls[Natts_pg_statistic_ext_data];
79  int2vector *stxkeys;
80  List *stxexprs = NIL;
81  Datum exprsDatum;
82  Relation statrel;
83  Relation datarel;
84  Relation rel = NULL;
85  Oid relid;
86  ObjectAddress parentobject,
87  myself;
88  Datum types[4]; /* one for each possible type of statistic */
89  int ntypes;
90  ArrayType *stxkind;
91  bool build_ndistinct;
92  bool build_dependencies;
93  bool build_mcv;
94  bool build_expressions;
95  bool requested_type = false;
96  int i;
97  ListCell *cell;
98  ListCell *cell2;
99 
100  Assert(IsA(stmt, CreateStatsStmt));
101 
102  /*
103  * Examine the FROM clause. Currently, we only allow it to be a single
104  * simple table, but later we'll probably allow multiple tables and JOIN
105  * syntax. The grammar is already prepared for that, so we have to check
106  * here that what we got is what we can support.
107  */
108  if (list_length(stmt->relations) != 1)
109  ereport(ERROR,
110  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
111  errmsg("only a single relation is allowed in CREATE STATISTICS")));
112 
113  foreach(cell, stmt->relations)
114  {
115  Node *rln = (Node *) lfirst(cell);
116 
117  if (!IsA(rln, RangeVar))
118  ereport(ERROR,
119  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
120  errmsg("only a single relation is allowed in CREATE STATISTICS")));
121 
122  /*
123  * CREATE STATISTICS will influence future execution plans but does
124  * not interfere with currently executing plans. So it should be
125  * enough to take only ShareUpdateExclusiveLock on relation,
126  * conflicting with ANALYZE and other DDL that sets statistical
127  * information, but not with normal queries.
128  */
130 
131  /* Restrict to allowed relation types */
132  if (rel->rd_rel->relkind != RELKIND_RELATION &&
133  rel->rd_rel->relkind != RELKIND_MATVIEW &&
134  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
135  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
136  ereport(ERROR,
137  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
138  errmsg("cannot define statistics for relation \"%s\"",
140  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
141 
142  /* You must own the relation to create stats on it */
143  if (!pg_class_ownercheck(RelationGetRelid(rel), stxowner))
146 
147  /* Creating statistics on system catalogs is not allowed */
149  ereport(ERROR,
150  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
151  errmsg("permission denied: \"%s\" is a system catalog",
152  RelationGetRelationName(rel))));
153  }
154 
155  Assert(rel);
156  relid = RelationGetRelid(rel);
157 
158  /*
159  * If the node has a name, split it up and determine creation namespace.
160  * If not (a possibility not considered by the grammar, but one which can
161  * occur via the "CREATE TABLE ... (LIKE)" command), then we put the
162  * object in the same namespace as the relation, and cons up a name for
163  * it.
164  */
165  if (stmt->defnames)
166  namespaceId = QualifiedNameGetCreationNamespace(stmt->defnames,
167  &namestr);
168  else
169  {
170  namespaceId = RelationGetNamespace(rel);
173  "stat",
174  namespaceId);
175  }
176  namestrcpy(&stxname, namestr);
177 
178  /*
179  * Deal with the possibility that the statistics object already exists.
180  */
182  CStringGetDatum(namestr),
183  ObjectIdGetDatum(namespaceId)))
184  {
185  if (stmt->if_not_exists)
186  {
187  ereport(NOTICE,
189  errmsg("statistics object \"%s\" already exists, skipping",
190  namestr)));
191  relation_close(rel, NoLock);
192  return InvalidObjectAddress;
193  }
194 
195  ereport(ERROR,
197  errmsg("statistics object \"%s\" already exists", namestr)));
198  }
199 
200  /*
201  * Make sure no more than STATS_MAX_DIMENSIONS columns are used. There
202  * might be duplicates and so on, but we'll deal with those later.
203  */
204  numcols = list_length(stmt->exprs);
205  if (numcols > STATS_MAX_DIMENSIONS)
206  ereport(ERROR,
207  (errcode(ERRCODE_TOO_MANY_COLUMNS),
208  errmsg("cannot have more than %d columns in statistics",
210 
211  /*
212  * Convert the expression list to a simple array of attnums, but also keep
213  * a list of more complex expressions. While at it, enforce some
214  * constraints.
215  *
216  * XXX We do only the bare minimum to separate simple attribute and
217  * complex expressions - for example "(a)" will be treated as a complex
218  * expression. No matter how elaborate the check is, there'll always be a
219  * way around it, if the user is determined (consider e.g. "(a+0)"), so
220  * it's not worth protecting against it.
221  */
222  foreach(cell, stmt->exprs)
223  {
224  StatsElem *selem = lfirst_node(StatsElem, cell);
225 
226  if (selem->name) /* column reference */
227  {
228  char *attname;
229  HeapTuple atttuple;
230  Form_pg_attribute attForm;
232 
233  attname = selem->name;
234 
235  atttuple = SearchSysCacheAttName(relid, attname);
236  if (!HeapTupleIsValid(atttuple))
237  ereport(ERROR,
238  (errcode(ERRCODE_UNDEFINED_COLUMN),
239  errmsg("column \"%s\" does not exist",
240  attname)));
241  attForm = (Form_pg_attribute) GETSTRUCT(atttuple);
242 
243  /* Disallow use of system attributes in extended stats */
244  if (attForm->attnum <= 0)
245  ereport(ERROR,
246  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
247  errmsg("statistics creation on system columns is not supported")));
248 
249  /* Disallow data types without a less-than operator */
250  type = lookup_type_cache(attForm->atttypid, TYPECACHE_LT_OPR);
251  if (type->lt_opr == InvalidOid)
252  ereport(ERROR,
253  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
254  errmsg("column \"%s\" cannot be used in statistics because its type %s has no default btree operator class",
255  attname, format_type_be(attForm->atttypid))));
256 
257  attnums[nattnums] = attForm->attnum;
258  nattnums++;
259  ReleaseSysCache(atttuple);
260  }
261  else /* expression */
262  {
263  Node *expr = selem->expr;
264  Oid atttype;
266 
267  Assert(expr != NULL);
268 
269  /*
270  * Disallow data types without a less-than operator.
271  *
272  * We ignore this for statistics on a single expression, in which
273  * case we'll build the regular statistics only (and that code can
274  * deal with such data types).
275  */
276  if (list_length(stmt->exprs) > 1)
277  {
278  atttype = exprType(expr);
279  type = lookup_type_cache(atttype, TYPECACHE_LT_OPR);
280  if (type->lt_opr == InvalidOid)
281  ereport(ERROR,
282  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
283  errmsg("expression cannot be used in multivariate statistics because its type %s has no default btree operator class",
284  format_type_be(atttype))));
285  }
286 
287  stxexprs = lappend(stxexprs, expr);
288  }
289  }
290 
291  /*
292  * Parse the statistics kinds.
293  *
294  * First check that if this is the case with a single expression, there
295  * are no statistics kinds specified (we don't allow that for the simple
296  * CREATE STATISTICS form).
297  */
298  if ((list_length(stmt->exprs) == 1) && (list_length(stxexprs) == 1))
299  {
300  /* statistics kinds not specified */
301  if (list_length(stmt->stat_types) > 0)
302  ereport(ERROR,
303  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
304  errmsg("when building statistics on a single expression, statistics kinds may not be specified")));
305  }
306 
307  /* OK, let's check that we recognize the statistics kinds. */
308  build_ndistinct = false;
309  build_dependencies = false;
310  build_mcv = false;
311  foreach(cell, stmt->stat_types)
312  {
313  char *type = strVal((Value *) lfirst(cell));
314 
315  if (strcmp(type, "ndistinct") == 0)
316  {
317  build_ndistinct = true;
318  requested_type = true;
319  }
320  else if (strcmp(type, "dependencies") == 0)
321  {
322  build_dependencies = true;
323  requested_type = true;
324  }
325  else if (strcmp(type, "mcv") == 0)
326  {
327  build_mcv = true;
328  requested_type = true;
329  }
330  else
331  ereport(ERROR,
332  (errcode(ERRCODE_SYNTAX_ERROR),
333  errmsg("unrecognized statistics kind \"%s\"",
334  type)));
335  }
336 
337  /*
338  * If no statistic type was specified, build them all (but only when the
339  * statistics is defined on more than one column/expression).
340  */
341  if ((!requested_type) && (numcols >= 2))
342  {
343  build_ndistinct = true;
344  build_dependencies = true;
345  build_mcv = true;
346  }
347 
348  /*
349  * When there are non-trivial expressions, build the expression stats
350  * automatically. This allows calculating good estimates for stats that
351  * consider per-clause estimates (e.g. functional dependencies).
352  */
353  build_expressions = (list_length(stxexprs) > 0);
354 
355  /*
356  * Check that at least two columns were specified in the statement, or
357  * that we're building statistics on a single expression.
358  */
359  if ((numcols < 2) && (list_length(stxexprs) != 1))
360  ereport(ERROR,
361  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
362  errmsg("extended statistics require at least 2 columns")));
363 
364  /*
365  * Sort the attnums, which makes detecting duplicates somewhat easier, and
366  * it does not hurt (it does not matter for the contents, unlike for
367  * indexes, for example).
368  */
369  qsort(attnums, nattnums, sizeof(int16), compare_int16);
370 
371  /*
372  * Check for duplicates in the list of columns. The attnums are sorted so
373  * just check consecutive elements.
374  */
375  for (i = 1; i < nattnums; i++)
376  {
377  if (attnums[i] == attnums[i - 1])
378  ereport(ERROR,
379  (errcode(ERRCODE_DUPLICATE_COLUMN),
380  errmsg("duplicate column name in statistics definition")));
381  }
382 
383  /*
384  * Check for duplicate expressions. We do two loops, counting the
385  * occurrences of each expression. This is O(N^2) but we only allow small
386  * number of expressions and it's not executed often.
387  *
388  * XXX We don't cross-check attributes and expressions, because it does
389  * not seem worth it. In principle we could check that expressions don't
390  * contain trivial attribute references like "(a)", but the reasoning is
391  * similar to why we don't bother with extracting columns from
392  * expressions. It's either expensive or very easy to defeat for
393  * determined user, and there's no risk if we allow such statistics (the
394  * statistics is useless, but harmless).
395  */
396  foreach(cell, stxexprs)
397  {
398  Node *expr1 = (Node *) lfirst(cell);
399  int cnt = 0;
400 
401  foreach(cell2, stxexprs)
402  {
403  Node *expr2 = (Node *) lfirst(cell2);
404 
405  if (equal(expr1, expr2))
406  cnt += 1;
407  }
408 
409  /* every expression should find at least itself */
410  Assert(cnt >= 1);
411 
412  if (cnt > 1)
413  ereport(ERROR,
414  (errcode(ERRCODE_DUPLICATE_COLUMN),
415  errmsg("duplicate expression in statistics definition")));
416  }
417 
418  /* Form an int2vector representation of the sorted column list */
419  stxkeys = buildint2vector(attnums, nattnums);
420 
421  /* construct the char array of enabled statistic types */
422  ntypes = 0;
423  if (build_ndistinct)
424  types[ntypes++] = CharGetDatum(STATS_EXT_NDISTINCT);
425  if (build_dependencies)
426  types[ntypes++] = CharGetDatum(STATS_EXT_DEPENDENCIES);
427  if (build_mcv)
428  types[ntypes++] = CharGetDatum(STATS_EXT_MCV);
429  if (build_expressions)
430  types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
431  Assert(ntypes > 0 && ntypes <= lengthof(types));
432  stxkind = construct_array(types, ntypes, CHAROID, 1, true, TYPALIGN_CHAR);
433 
434  /* convert the expressions (if any) to a text datum */
435  if (stxexprs != NIL)
436  {
437  char *exprsString;
438 
439  exprsString = nodeToString(stxexprs);
440  exprsDatum = CStringGetTextDatum(exprsString);
441  pfree(exprsString);
442  }
443  else
444  exprsDatum = (Datum) 0;
445 
446  statrel = table_open(StatisticExtRelationId, RowExclusiveLock);
447 
448  /*
449  * Everything seems fine, so let's build the pg_statistic_ext tuple.
450  */
451  memset(values, 0, sizeof(values));
452  memset(nulls, false, sizeof(nulls));
453 
454  statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
455  Anum_pg_statistic_ext_oid);
456  values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
457  values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
458  values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
459  values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
460  values[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(-1);
461  values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
462  values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
463  values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
464 
465  values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
466  if (exprsDatum == (Datum) 0)
467  nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
468 
469  /* insert it into pg_statistic_ext */
470  htup = heap_form_tuple(statrel->rd_att, values, nulls);
471  CatalogTupleInsert(statrel, htup);
472  heap_freetuple(htup);
473 
475 
476  /*
477  * Also build the pg_statistic_ext_data tuple, to hold the actual
478  * statistics data.
479  */
480  datarel = table_open(StatisticExtDataRelationId, RowExclusiveLock);
481 
482  memset(datavalues, 0, sizeof(datavalues));
483  memset(datanulls, false, sizeof(datanulls));
484 
485  datavalues[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statoid);
486 
487  /* no statistics built yet */
488  datanulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
489  datanulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
490  datanulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
491  datanulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
492 
493  /* insert it into pg_statistic_ext_data */
494  htup = heap_form_tuple(datarel->rd_att, datavalues, datanulls);
495  CatalogTupleInsert(datarel, htup);
496  heap_freetuple(htup);
497 
499 
500  InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
501 
502  /*
503  * Invalidate relcache so that others see the new statistics object.
504  */
506 
507  relation_close(rel, NoLock);
508 
509  /*
510  * Add an AUTO dependency on each column used in the stats, so that the
511  * stats object goes away if any or all of them get dropped.
512  */
513  ObjectAddressSet(myself, StatisticExtRelationId, statoid);
514 
515  /* add dependencies for plain column references */
516  for (i = 0; i < nattnums; i++)
517  {
518  ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
519  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
520  }
521 
522  /*
523  * If there are no dependencies on a column, give the statistics an auto
524  * dependency on the whole table. In most cases, this will be redundant,
525  * but it might not be if the statistics expressions contain no Vars
526  * (which might seem strange but possible). This is consistent with what
527  * we do for indexes in index_create.
528  *
529  * XXX We intentionally don't consider the expressions before adding this
530  * dependency, because recordDependencyOnSingleRelExpr may not create any
531  * dependencies for whole-row Vars.
532  */
533  if (!nattnums)
534  {
535  ObjectAddressSet(parentobject, RelationRelationId, relid);
536  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
537  }
538 
539  /*
540  * Store dependencies on anything mentioned in statistics expressions,
541  * just like we do for index expressions.
542  */
543  if (stxexprs)
545  (Node *) stxexprs,
546  relid,
548  DEPENDENCY_AUTO, false);
549 
550  /*
551  * Also add dependencies on namespace and owner. These are required
552  * because the stats object might have a different namespace and/or owner
553  * than the underlying table(s).
554  */
555  ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
556  recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
557 
558  recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
559 
560  /*
561  * XXX probably there should be a recordDependencyOnCurrentExtension call
562  * here too, but we'd have to add support for ALTER EXTENSION ADD/DROP
563  * STATISTICS, which is more work than it seems worth.
564  */
565 
566  /* Add any requested comment */
567  if (stmt->stxcomment != NULL)
568  CreateComments(statoid, StatisticExtRelationId, 0,
569  stmt->stxcomment);
570 
571  /* Return stats object's address */
572  return myself;
573 }
signed short int16
Definition: c.h:428
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
#define NIL
Definition: pg_list.h:65
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:381
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:143
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#define NameGetDatum(X)
Definition: postgres.h:639
static char * ChooseExtendedStatisticName(const char *name1, const char *name2, const char *label, Oid namespaceid)
Definition: statscmds.c:747
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:151
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
char * name
Definition: parsenodes.h:2930
struct typedefs * types
Definition: ecpg.c:29
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3122
Oid GetUserId(void)
Definition: miscinit.c:478
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:3040
#define PointerGetDatum(X)
Definition: postgres.h:600
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:114
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3318
Definition: nodes.h:539
#define strVal(v)
Definition: value.h:54
int errcode(int sqlerrcode)
Definition: elog.c:698
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:163
#define lengthof(array)
Definition: c.h:734
Form_pg_class rd_rel
Definition: rel.h:109
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3308
void pfree(void *pointer)
Definition: mcxt.c:1169
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
NameData attname
Definition: pg_attribute.h:41
Node * expr
Definition: parsenodes.h:2931
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Definition: c.h:675
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:622
#define RelationGetRelationName(relation)
Definition: rel.h:511
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:207
static int compare_int16(const void *a, const void *b)
Definition: statscmds.c:51
List * lappend(List *list, void *datum)
Definition: list.c:336
Definition: c.h:649
uintptr_t Datum
Definition: postgres.h:411
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
Relation relation_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: relation.c:138
TupleDesc rd_att
Definition: rel.h:110
bool allowSystemTableMods
Definition: globals.c:123
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:339
#define InvalidOid
Definition: postgres_ext.h:36
#define ereport(elevel,...)
Definition: elog.h:157
#define NOTICE
Definition: elog.h:37
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:206
#define Assert(condition)
Definition: c.h:804
#define lfirst(lc)
Definition: pg_list.h:169
Definition: value.h:42
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4818
static char * ChooseExtendedStatisticNameAddition(List *exprs)
Definition: statscmds.c:789
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1611
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:41
static int list_length(const List *l)
Definition: pg_list.h:149
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:186
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:1268
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Definition: objectaddress.h:33
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define CharGetDatum(X)
Definition: postgres.h:460
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1279
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define Int32GetDatum(X)
Definition: postgres.h:523
#define STATS_MAX_DIMENSIONS
Definition: statistics.h:19
const ObjectAddress InvalidObjectAddress
int errmsg(const char *fmt,...)
Definition: elog.c:909
int i
ObjectType get_relkind_objtype(char relkind)
#define TYPECACHE_LT_OPR
Definition: typcache.h:137
#define CStringGetTextDatum(s)
Definition: builtins.h:82
char * nodeToString(const void *obj)
Definition: outfuncs.c:4543
#define qsort(a, b, c, d)
Definition: port.h:504
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: pg_list.h:50
#define RelationGetRelid(relation)
Definition: rel.h:477
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
#define RelationGetNamespace(relation)
Definition: rel.h:518

◆ RemoveStatisticsById()

void RemoveStatisticsById ( Oid  statsOid)

Definition at line 683 of file statscmds.c.

References CacheInvalidateRelcacheByRelid(), CatalogTupleDelete(), elog, ERROR, GETSTRUCT, HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), STATEXTDATASTXOID, STATEXTOID, HeapTupleData::t_self, table_close(), and table_open().

Referenced by doDeletion().

684 {
685  Relation relation;
686  HeapTuple tup;
687  Form_pg_statistic_ext statext;
688  Oid relid;
689 
690  /*
691  * First delete the pg_statistic_ext_data tuple holding the actual
692  * statistical data.
693  */
694  relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
695 
697 
698  if (!HeapTupleIsValid(tup)) /* should not happen */
699  elog(ERROR, "cache lookup failed for statistics data %u", statsOid);
700 
701  CatalogTupleDelete(relation, &tup->t_self);
702 
703  ReleaseSysCache(tup);
704 
705  table_close(relation, RowExclusiveLock);
706 
707  /*
708  * Delete the pg_statistic_ext tuple. Also send out a cache inval on the
709  * associated table, so that dependent plans will be rebuilt.
710  */
711  relation = table_open(StatisticExtRelationId, RowExclusiveLock);
712 
713  tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
714 
715  if (!HeapTupleIsValid(tup)) /* should not happen */
716  elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
717 
718  statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
719  relid = statext->stxrelid;
720 
722 
723  CatalogTupleDelete(relation, &tup->t_self);
724 
725  ReleaseSysCache(tup);
726 
727  table_close(relation, RowExclusiveLock);
728 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
unsigned int Oid
Definition: postgres_ext.h:31
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1338
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1127
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define elog(elevel,...)
Definition: elog.h:232
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
FormData_pg_statistic_ext * Form_pg_statistic_ext

◆ StatisticsGetRelation()

Oid StatisticsGetRelation ( Oid  statId,
bool  missing_ok 
)

Definition at line 836 of file statscmds.c.

References Assert, elog, ERROR, GETSTRUCT, HeapTupleIsValid, InvalidOid, ObjectIdGetDatum, ReleaseSysCache(), SearchSysCache1(), and STATEXTOID.

Referenced by ATPostAlterTypeCleanup().

837 {
838  HeapTuple tuple;
840  Oid result;
841 
842  tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statId));
843  if (!HeapTupleIsValid(tuple))
844  {
845  if (missing_ok)
846  return InvalidOid;
847  elog(ERROR, "cache lookup failed for statistics object %u", statId);
848  }
849  stx = (Form_pg_statistic_ext) GETSTRUCT(tuple);
850  Assert(stx->oid == statId);
851 
852  result = stx->stxrelid;
853  ReleaseSysCache(tuple);
854  return result;
855 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
unsigned int Oid
Definition: postgres_ext.h:31
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define ERROR
Definition: elog.h:46
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1127
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
#define InvalidOid
Definition: postgres_ext.h:36
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define Assert(condition)
Definition: c.h:804
#define elog(elevel,...)
Definition: elog.h:232
FormData_pg_statistic_ext * Form_pg_statistic_ext