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/lsyscache.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 RemoveStatisticsDataById (Oid statsOid, bool inh)
 
void RemoveStatisticsById (Oid statsOid)
 
Oid StatisticsGetRelation (Oid statId, bool missing_ok)
 

Function Documentation

◆ AlterStatistics()

ObjectAddress AlterStatistics ( AlterStatsStmt stmt)

Definition at line 598 of file statscmds.c.

599 {
600  Relation rel;
601  Oid stxoid;
602  HeapTuple oldtup;
603  HeapTuple newtup;
604  Datum repl_val[Natts_pg_statistic_ext];
605  bool repl_null[Natts_pg_statistic_ext];
606  bool repl_repl[Natts_pg_statistic_ext];
607  ObjectAddress address;
608  int newtarget = stmt->stxstattarget;
609 
610  /* Limit statistics target to a sane range */
611  if (newtarget < -1)
612  {
613  ereport(ERROR,
614  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
615  errmsg("statistics target %d is too low",
616  newtarget)));
617  }
618  else if (newtarget > 10000)
619  {
620  newtarget = 10000;
622  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
623  errmsg("lowering statistics target to %d",
624  newtarget)));
625  }
626 
627  /* lookup OID of the statistics object */
628  stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
629 
630  /*
631  * If we got here and the OID is not valid, it means the statistics object
632  * does not exist, but the command specified IF EXISTS. So report this as
633  * a simple NOTICE and we're done.
634  */
635  if (!OidIsValid(stxoid))
636  {
637  char *schemaname;
638  char *statname;
639 
640  Assert(stmt->missing_ok);
641 
642  DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
643 
644  if (schemaname)
645  ereport(NOTICE,
646  (errmsg("statistics object \"%s.%s\" does not exist, skipping",
647  schemaname, statname)));
648  else
649  ereport(NOTICE,
650  (errmsg("statistics object \"%s\" does not exist, skipping",
651  statname)));
652 
653  return InvalidObjectAddress;
654  }
655 
656  /* Search pg_statistic_ext */
657  rel = table_open(StatisticExtRelationId, RowExclusiveLock);
658 
659  oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
660 
661  /* Must be owner of the existing statistics object */
664  NameListToString(stmt->defnames));
665 
666  /* Build new tuple. */
667  memset(repl_val, 0, sizeof(repl_val));
668  memset(repl_null, false, sizeof(repl_null));
669  memset(repl_repl, false, sizeof(repl_repl));
670 
671  /* replace the stxstattarget column */
672  repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
673  repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(newtarget);
674 
675  newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
676  repl_val, repl_null, repl_repl);
677 
678  /* Update system catalog. */
679  CatalogTupleUpdate(rel, &newtup->t_self, newtup);
680 
681  InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
682 
683  ObjectAddressSet(address, StatisticExtRelationId, stxoid);
684 
685  /*
686  * NOTE: because we only support altering the statistics target, not the
687  * other fields, there is no need to update dependencies.
688  */
689 
690  heap_freetuple(newtup);
691  ReleaseSysCache(oldtup);
692 
694 
695  return address;
696 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:181
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3308
bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
Definition: aclchk.c:5410
#define OidIsValid(objectId)
Definition: c.h:710
int errcode(int sqlerrcode)
Definition: elog.c:693
int errmsg(const char *fmt,...)
Definition: elog.c:904
#define WARNING
Definition: elog.h:30
#define ERROR
Definition: elog.h:33
#define NOTICE
Definition: elog.h:29
#define ereport(elevel,...)
Definition: elog.h:143
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1113
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
Assert(fmt[strlen(fmt) - 1] !='\n')
#define RowExclusiveLock
Definition: lockdefs.h:38
Oid GetUserId(void)
Definition: miscinit.c:495
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2854
Oid get_statistics_object_oid(List *names, bool missing_ok)
Definition: namespace.c:2223
char * NameListToString(List *names)
Definition: namespace.c:3147
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_STATISTIC_EXT
Definition: parsenodes.h:1827
uintptr_t Datum
Definition: postgres.h:411
#define ObjectIdGetDatum(X)
Definition: postgres.h:551
#define Int32GetDatum(X)
Definition: postgres.h:523
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:504
ItemPointerData t_self
Definition: htup.h:65
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1198
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1150
@ STATEXTOID
Definition: syscache.h:94
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39

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().

◆ ChooseExtendedStatisticName()

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

Definition at line 783 of file statscmds.c.

785 {
786  int pass = 0;
787  char *stxname = NULL;
788  char modlabel[NAMEDATALEN];
789 
790  /* try the unmodified label first */
791  strlcpy(modlabel, label, sizeof(modlabel));
792 
793  for (;;)
794  {
795  Oid existingstats;
796 
797  stxname = makeObjectName(name1, name2, modlabel);
798 
799  existingstats = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
800  PointerGetDatum(stxname),
801  ObjectIdGetDatum(namespaceid));
802  if (!OidIsValid(existingstats))
803  break;
804 
805  /* found a conflict, so try a new name component */
806  pfree(stxname);
807  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
808  }
809 
810  return stxname;
811 }
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2198
void pfree(void *pointer)
Definition: mcxt.c:1169
static char * label
#define NAMEDATALEN
#define snprintf
Definition: port.h:225
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#define PointerGetDatum(X)
Definition: postgres.h:600
@ STATEXTNAMENSP
Definition: syscache.h:93
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition: syscache.h:197

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

Referenced by CreateStatistics().

◆ ChooseExtendedStatisticNameAddition()

static char * ChooseExtendedStatisticNameAddition ( List exprs)
static

Definition at line 825 of file statscmds.c.

826 {
827  char buf[NAMEDATALEN * 2];
828  int buflen = 0;
829  ListCell *lc;
830 
831  buf[0] = '\0';
832  foreach(lc, exprs)
833  {
834  StatsElem *selem = (StatsElem *) lfirst(lc);
835  const char *name;
836 
837  /* It should be one of these, but just skip if it happens not to be */
838  if (!IsA(selem, StatsElem))
839  continue;
840 
841  name = selem->name;
842 
843  if (buflen > 0)
844  buf[buflen++] = '_'; /* insert _ between names */
845 
846  /*
847  * We use fixed 'expr' for expressions, which have empty column names.
848  * For indexes this is handled in ChooseIndexColumnNames, but we have
849  * no such function for stats and it does not seem worth adding. If a
850  * better name is needed, the user can specify it explicitly.
851  */
852  if (!name)
853  name = "expr";
854 
855  /*
856  * At this point we have buflen <= NAMEDATALEN. name should be less
857  * than NAMEDATALEN already, but use strlcpy for paranoia.
858  */
859  strlcpy(buf + buflen, name, NAMEDATALEN);
860  buflen += strlen(buf + buflen);
861  if (buflen >= NAMEDATALEN)
862  break;
863  }
864  return pstrdup(buf);
865 }
const char * name
Definition: encode.c:561
char * pstrdup(const char *in)
Definition: mcxt.c:1299
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#define lfirst(lc)
Definition: pg_list.h:169
static char * buf
Definition: pg_test_fsync.c:70
char * name
Definition: parsenodes.h:2944

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

Referenced by CreateStatistics().

◆ compare_int16()

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

Definition at line 52 of file statscmds.c.

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

References a, av, and b.

Referenced by CreateStatistics().

◆ CreateStatistics()

ObjectAddress CreateStatistics ( CreateStatsStmt stmt)

Definition at line 65 of file statscmds.c.

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, Assert(), attname, attnum, bms_next_member(), 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(), FirstLowInvalidHeapAttributeNumber, format_type_be(), get_attname(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, if(), CreateStatsStmt::if_not_exists, Int32GetDatum, InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHook, IsA, IsSystemRelation(), lappend(), lengthof, lfirst, lfirst_node, list_length(), lookup_type_cache(), StatsElem::name, NameGetDatum, namestrcpy(), NIL, nodeToString(), NoLock, NOTICE, ObjectAddressSet, ObjectAddressSubSet, ObjectIdGetDatum, pfree(), pg_class_ownercheck(), PointerGetDatum, pull_varattnos(), 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, values, Var::varattno, and Var::vartype.

Referenced by ATExecAddStatistics(), and ProcessUtilitySlow().

◆ RemoveStatisticsById()

void RemoveStatisticsById ( Oid  statsOid)

Definition at line 728 of file statscmds.c.

729 {
730  Relation relation;
731  HeapTuple tup;
732  Form_pg_statistic_ext statext;
733  Oid relid;
734 
735  /*
736  * First delete the pg_statistic_ext_data tuples holding the actual
737  * statistical data. There might be data with/without inheritance, so
738  * attempt deleting both.
739  */
740  RemoveStatisticsDataById(statsOid, true);
741  RemoveStatisticsDataById(statsOid, false);
742 
743  /*
744  * Delete the pg_statistic_ext tuple. Also send out a cache inval on the
745  * associated table, so that dependent plans will be rebuilt.
746  */
747  relation = table_open(StatisticExtRelationId, RowExclusiveLock);
748 
749  tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
750 
751  if (!HeapTupleIsValid(tup)) /* should not happen */
752  elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
753 
754  statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
755  relid = statext->stxrelid;
756 
758 
759  CatalogTupleDelete(relation, &tup->t_self);
760 
761  ReleaseSysCache(tup);
762 
763  table_close(relation, RowExclusiveLock);
764 }
#define elog(elevel,...)
Definition: elog.h:218
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1421
FormData_pg_statistic_ext * Form_pg_statistic_ext
void RemoveStatisticsDataById(Oid statsOid, bool inh)
Definition: statscmds.c:703

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

Referenced by doDeletion().

◆ RemoveStatisticsDataById()

void RemoveStatisticsDataById ( Oid  statsOid,
bool  inh 
)

Definition at line 703 of file statscmds.c.

704 {
705  Relation relation;
706  HeapTuple tup;
707 
708  relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
709 
711  BoolGetDatum(inh));
712 
713  /* We don't know if the data row for inh value exists. */
714  if (HeapTupleIsValid(tup))
715  {
716  CatalogTupleDelete(relation, &tup->t_self);
717 
718  ReleaseSysCache(tup);
719  }
720 
721  table_close(relation, RowExclusiveLock);
722 }
#define BoolGetDatum(X)
Definition: postgres.h:446
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:1161
@ STATEXTDATASTXOID
Definition: syscache.h:92

References BoolGetDatum, CatalogTupleDelete(), HeapTupleIsValid, ObjectIdGetDatum, ReleaseSysCache(), RowExclusiveLock, SearchSysCache2(), STATEXTDATASTXOID, HeapTupleData::t_self, table_close(), and table_open().

Referenced by RemoveStatisticsById(), and statext_store().

◆ StatisticsGetRelation()

Oid StatisticsGetRelation ( Oid  statId,
bool  missing_ok 
)

Definition at line 872 of file statscmds.c.

873 {
874  HeapTuple tuple;
876  Oid result;
877 
878  tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statId));
879  if (!HeapTupleIsValid(tuple))
880  {
881  if (missing_ok)
882  return InvalidOid;
883  elog(ERROR, "cache lookup failed for statistics object %u", statId);
884  }
885  stx = (Form_pg_statistic_ext) GETSTRUCT(tuple);
886  Assert(stx->oid == statId);
887 
888  result = stx->stxrelid;
889  ReleaseSysCache(tuple);
890  return result;
891 }

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