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 602 of file statscmds.c.

603 {
604  Relation rel;
605  Oid stxoid;
606  HeapTuple oldtup;
607  HeapTuple newtup;
608  Datum repl_val[Natts_pg_statistic_ext];
609  bool repl_null[Natts_pg_statistic_ext];
610  bool repl_repl[Natts_pg_statistic_ext];
611  ObjectAddress address;
612  int newtarget = stmt->stxstattarget;
613 
614  /* Limit statistics target to a sane range */
615  if (newtarget < -1)
616  {
617  ereport(ERROR,
618  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
619  errmsg("statistics target %d is too low",
620  newtarget)));
621  }
622  else if (newtarget > MAX_STATISTICS_TARGET)
623  {
624  newtarget = MAX_STATISTICS_TARGET;
626  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
627  errmsg("lowering statistics target to %d",
628  newtarget)));
629  }
630 
631  /* lookup OID of the statistics object */
632  stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
633 
634  /*
635  * If we got here and the OID is not valid, it means the statistics object
636  * does not exist, but the command specified IF EXISTS. So report this as
637  * a simple NOTICE and we're done.
638  */
639  if (!OidIsValid(stxoid))
640  {
641  char *schemaname;
642  char *statname;
643 
644  Assert(stmt->missing_ok);
645 
646  DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
647 
648  if (schemaname)
649  ereport(NOTICE,
650  (errmsg("statistics object \"%s.%s\" does not exist, skipping",
651  schemaname, statname)));
652  else
653  ereport(NOTICE,
654  (errmsg("statistics object \"%s\" does not exist, skipping",
655  statname)));
656 
657  return InvalidObjectAddress;
658  }
659 
660  /* Search pg_statistic_ext */
661  rel = table_open(StatisticExtRelationId, RowExclusiveLock);
662 
663  oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
664  if (!HeapTupleIsValid(oldtup))
665  elog(ERROR, "cache lookup failed for extended statistics object %u", stxoid);
666 
667  /* Must be owner of the existing statistics object */
668  if (!object_ownercheck(StatisticExtRelationId, stxoid, GetUserId()))
670  NameListToString(stmt->defnames));
671 
672  /* Build new tuple. */
673  memset(repl_val, 0, sizeof(repl_val));
674  memset(repl_null, false, sizeof(repl_null));
675  memset(repl_repl, false, sizeof(repl_repl));
676 
677  /* replace the stxstattarget column */
678  repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
679  repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int16GetDatum(newtarget);
680 
681  newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
682  repl_val, repl_null, repl_repl);
683 
684  /* Update system catalog. */
685  CatalogTupleUpdate(rel, &newtup->t_self, newtup);
686 
687  InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
688 
689  ObjectAddressSet(address, StatisticExtRelationId, stxoid);
690 
691  /*
692  * NOTE: because we only support altering the statistics target, not the
693  * other fields, there is no need to update dependencies.
694  */
695 
696  heap_freetuple(newtup);
697  ReleaseSysCache(oldtup);
698 
700 
701  return address;
702 }
@ ACLCHECK_NOT_OWNER
Definition: acl.h:184
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2669
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:3961
#define OidIsValid(objectId)
Definition: c.h:764
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define WARNING
Definition: elog.h:36
#define ERROR
Definition: elog.h:39
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, Datum *replValues, bool *replIsnull, bool *doReplace)
Definition: heaptuple.c:1201
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1426
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define stmt
Definition: indent_codes.h:59
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
Assert(fmt[strlen(fmt) - 1] !='\n')
#define RowExclusiveLock
Definition: lockdefs.h:38
Oid GetUserId(void)
Definition: miscinit.c:509
Oid get_statistics_object_oid(List *names, bool missing_ok)
Definition: namespace.c:2203
void DeconstructQualifiedName(const List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2834
char * NameListToString(const List *names)
Definition: namespace.c:3127
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
const ObjectAddress InvalidObjectAddress
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
@ OBJECT_STATISTIC_EXT
Definition: parsenodes.h:2159
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:530
ItemPointerData t_self
Definition: htup.h:65
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:868
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:820
@ STATEXTOID
Definition: syscache.h:96
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:308

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert(), CatalogTupleUpdate(), DeconstructQualifiedName(), elog(), ereport, errcode(), errmsg(), ERROR, get_statistics_object_oid(), GetUserId(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, Int16GetDatum(), InvalidObjectAddress, InvokeObjectPostAlterHook, MAX_STATISTICS_TARGET, NameListToString(), NOTICE, object_ownercheck(), OBJECT_STATISTIC_EXT, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, RelationGetDescr, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), STATEXTOID, stmt, 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 789 of file statscmds.c.

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

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 831 of file statscmds.c.

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

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:482
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 
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 (!object_ownercheck(RelationRelationId, 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, put the object in the same namespace as the relation, and cons
159  * up a name for it. (This can happen either via "CREATE STATISTICS ..."
160  * or via "CREATE TABLE ... (LIKE)".)
161  */
162  if (stmt->defnames)
163  namespaceId = QualifiedNameGetCreationNamespace(stmt->defnames,
164  &namestr);
165  else
166  {
167  namespaceId = RelationGetNamespace(rel);
170  "stat",
171  namespaceId);
172  }
173  namestrcpy(&stxname, namestr);
174 
175  /*
176  * Deal with the possibility that the statistics object already exists.
177  */
179  CStringGetDatum(namestr),
180  ObjectIdGetDatum(namespaceId)))
181  {
182  if (stmt->if_not_exists)
183  {
184  /*
185  * Since stats objects aren't members of extensions (see comments
186  * below), no need for checkMembershipInCurrentExtension here.
187  */
188  ereport(NOTICE,
190  errmsg("statistics object \"%s\" already exists, skipping",
191  namestr)));
192  relation_close(rel, NoLock);
193  return InvalidObjectAddress;
194  }
195 
196  ereport(ERROR,
198  errmsg("statistics object \"%s\" already exists", namestr)));
199  }
200 
201  /*
202  * Make sure no more than STATS_MAX_DIMENSIONS columns are used. There
203  * might be duplicates and so on, but we'll deal with those later.
204  */
205  numcols = list_length(stmt->exprs);
206  if (numcols > STATS_MAX_DIMENSIONS)
207  ereport(ERROR,
208  (errcode(ERRCODE_TOO_MANY_COLUMNS),
209  errmsg("cannot have more than %d columns in statistics",
211 
212  /*
213  * Convert the expression list to a simple array of attnums, but also keep
214  * a list of more complex expressions. While at it, enforce some
215  * constraints - we don't allow extended statistics on system attributes,
216  * and we require the data type to have a less-than operator.
217  *
218  * There are many ways to "mask" a simple attribute reference as an
219  * expression, for example "(a+0)" etc. We can't possibly detect all of
220  * them, but we handle at least the simple case with the attribute in
221  * parens. There'll always be a way around this, if the user is determined
222  * (like the "(a+0)" example), but this makes it somewhat consistent with
223  * how indexes treat attributes/expressions.
224  */
225  foreach(cell, stmt->exprs)
226  {
227  StatsElem *selem = lfirst_node(StatsElem, cell);
228 
229  if (selem->name) /* column reference */
230  {
231  char *attname;
232  HeapTuple atttuple;
233  Form_pg_attribute attForm;
235 
236  attname = selem->name;
237 
238  atttuple = SearchSysCacheAttName(relid, attname);
239  if (!HeapTupleIsValid(atttuple))
240  ereport(ERROR,
241  (errcode(ERRCODE_UNDEFINED_COLUMN),
242  errmsg("column \"%s\" does not exist",
243  attname)));
244  attForm = (Form_pg_attribute) GETSTRUCT(atttuple);
245 
246  /* Disallow use of system attributes in extended stats */
247  if (attForm->attnum <= 0)
248  ereport(ERROR,
249  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
250  errmsg("statistics creation on system columns is not supported")));
251 
252  /* Disallow data types without a less-than operator */
253  type = lookup_type_cache(attForm->atttypid, TYPECACHE_LT_OPR);
254  if (type->lt_opr == InvalidOid)
255  ereport(ERROR,
256  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
257  errmsg("column \"%s\" cannot be used in statistics because its type %s has no default btree operator class",
258  attname, format_type_be(attForm->atttypid))));
259 
260  attnums[nattnums] = attForm->attnum;
261  nattnums++;
262  ReleaseSysCache(atttuple);
263  }
264  else if (IsA(selem->expr, Var)) /* column reference in parens */
265  {
266  Var *var = (Var *) selem->expr;
268 
269  /* Disallow use of system attributes in extended stats */
270  if (var->varattno <= 0)
271  ereport(ERROR,
272  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
273  errmsg("statistics creation on system columns is not supported")));
274 
275  /* Disallow data types without a less-than operator */
276  type = lookup_type_cache(var->vartype, TYPECACHE_LT_OPR);
277  if (type->lt_opr == InvalidOid)
278  ereport(ERROR,
279  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
280  errmsg("column \"%s\" cannot be used in statistics because its type %s has no default btree operator class",
281  get_attname(relid, var->varattno, false), format_type_be(var->vartype))));
282 
283  attnums[nattnums] = var->varattno;
284  nattnums++;
285  }
286  else /* expression */
287  {
288  Node *expr = selem->expr;
289  Oid atttype;
291  Bitmapset *attnums = NULL;
292  int k;
293 
294  Assert(expr != NULL);
295 
296  /* Disallow expressions referencing system attributes. */
297  pull_varattnos(expr, 1, &attnums);
298 
299  k = -1;
300  while ((k = bms_next_member(attnums, k)) >= 0)
301  {
303 
304  if (attnum <= 0)
305  ereport(ERROR,
306  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
307  errmsg("statistics creation on system columns is not supported")));
308  }
309 
310  /*
311  * Disallow data types without a less-than operator.
312  *
313  * We ignore this for statistics on a single expression, in which
314  * case we'll build the regular statistics only (and that code can
315  * deal with such data types).
316  */
317  if (list_length(stmt->exprs) > 1)
318  {
319  atttype = exprType(expr);
321  if (type->lt_opr == InvalidOid)
322  ereport(ERROR,
323  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
324  errmsg("expression cannot be used in multivariate statistics because its type %s has no default btree operator class",
325  format_type_be(atttype))));
326  }
327 
328  stxexprs = lappend(stxexprs, expr);
329  }
330  }
331 
332  /*
333  * Parse the statistics kinds.
334  *
335  * First check that if this is the case with a single expression, there
336  * are no statistics kinds specified (we don't allow that for the simple
337  * CREATE STATISTICS form).
338  */
339  if ((list_length(stmt->exprs) == 1) && (list_length(stxexprs) == 1))
340  {
341  /* statistics kinds not specified */
342  if (stmt->stat_types != NIL)
343  ereport(ERROR,
344  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
345  errmsg("when building statistics on a single expression, statistics kinds may not be specified")));
346  }
347 
348  /* OK, let's check that we recognize the statistics kinds. */
349  build_ndistinct = false;
350  build_dependencies = false;
351  build_mcv = false;
352  foreach(cell, stmt->stat_types)
353  {
354  char *type = strVal(lfirst(cell));
355 
356  if (strcmp(type, "ndistinct") == 0)
357  {
358  build_ndistinct = true;
359  requested_type = true;
360  }
361  else if (strcmp(type, "dependencies") == 0)
362  {
363  build_dependencies = true;
364  requested_type = true;
365  }
366  else if (strcmp(type, "mcv") == 0)
367  {
368  build_mcv = true;
369  requested_type = true;
370  }
371  else
372  ereport(ERROR,
373  (errcode(ERRCODE_SYNTAX_ERROR),
374  errmsg("unrecognized statistics kind \"%s\"",
375  type)));
376  }
377 
378  /*
379  * If no statistic type was specified, build them all (but only when the
380  * statistics is defined on more than one column/expression).
381  */
382  if ((!requested_type) && (numcols >= 2))
383  {
384  build_ndistinct = true;
385  build_dependencies = true;
386  build_mcv = true;
387  }
388 
389  /*
390  * When there are non-trivial expressions, build the expression stats
391  * automatically. This allows calculating good estimates for stats that
392  * consider per-clause estimates (e.g. functional dependencies).
393  */
394  build_expressions = (stxexprs != NIL);
395 
396  /*
397  * Check that at least two columns were specified in the statement, or
398  * that we're building statistics on a single expression.
399  */
400  if ((numcols < 2) && (list_length(stxexprs) != 1))
401  ereport(ERROR,
402  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
403  errmsg("extended statistics require at least 2 columns")));
404 
405  /*
406  * Sort the attnums, which makes detecting duplicates somewhat easier, and
407  * it does not hurt (it does not matter for the contents, unlike for
408  * indexes, for example).
409  */
410  qsort(attnums, nattnums, sizeof(int16), compare_int16);
411 
412  /*
413  * Check for duplicates in the list of columns. The attnums are sorted so
414  * just check consecutive elements.
415  */
416  for (i = 1; i < nattnums; i++)
417  {
418  if (attnums[i] == attnums[i - 1])
419  ereport(ERROR,
420  (errcode(ERRCODE_DUPLICATE_COLUMN),
421  errmsg("duplicate column name in statistics definition")));
422  }
423 
424  /*
425  * Check for duplicate expressions. We do two loops, counting the
426  * occurrences of each expression. This is O(N^2) but we only allow small
427  * number of expressions and it's not executed often.
428  *
429  * XXX We don't cross-check attributes and expressions, because it does
430  * not seem worth it. In principle we could check that expressions don't
431  * contain trivial attribute references like "(a)", but the reasoning is
432  * similar to why we don't bother with extracting columns from
433  * expressions. It's either expensive or very easy to defeat for
434  * determined user, and there's no risk if we allow such statistics (the
435  * statistics is useless, but harmless).
436  */
437  foreach(cell, stxexprs)
438  {
439  Node *expr1 = (Node *) lfirst(cell);
440  int cnt = 0;
441 
442  foreach(cell2, stxexprs)
443  {
444  Node *expr2 = (Node *) lfirst(cell2);
445 
446  if (equal(expr1, expr2))
447  cnt += 1;
448  }
449 
450  /* every expression should find at least itself */
451  Assert(cnt >= 1);
452 
453  if (cnt > 1)
454  ereport(ERROR,
455  (errcode(ERRCODE_DUPLICATE_COLUMN),
456  errmsg("duplicate expression in statistics definition")));
457  }
458 
459  /* Form an int2vector representation of the sorted column list */
460  stxkeys = buildint2vector(attnums, nattnums);
461 
462  /* construct the char array of enabled statistic types */
463  ntypes = 0;
464  if (build_ndistinct)
465  types[ntypes++] = CharGetDatum(STATS_EXT_NDISTINCT);
466  if (build_dependencies)
467  types[ntypes++] = CharGetDatum(STATS_EXT_DEPENDENCIES);
468  if (build_mcv)
469  types[ntypes++] = CharGetDatum(STATS_EXT_MCV);
470  if (build_expressions)
471  types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
472  Assert(ntypes > 0 && ntypes <= lengthof(types));
473  stxkind = construct_array_builtin(types, ntypes, CHAROID);
474 
475  /* convert the expressions (if any) to a text datum */
476  if (stxexprs != NIL)
477  {
478  char *exprsString;
479 
480  exprsString = nodeToString(stxexprs);
481  exprsDatum = CStringGetTextDatum(exprsString);
482  pfree(exprsString);
483  }
484  else
485  exprsDatum = (Datum) 0;
486 
487  statrel = table_open(StatisticExtRelationId, RowExclusiveLock);
488 
489  /*
490  * Everything seems fine, so let's build the pg_statistic_ext tuple.
491  */
492  memset(values, 0, sizeof(values));
493  memset(nulls, false, sizeof(nulls));
494 
495  statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
496  Anum_pg_statistic_ext_oid);
497  values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
498  values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
499  values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
500  values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
501  values[Anum_pg_statistic_ext_stxstattarget - 1] = Int16GetDatum(-1);
502  values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
503  values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
504  values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
505 
506  values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
507  if (exprsDatum == (Datum) 0)
508  nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
509 
510  /* insert it into pg_statistic_ext */
511  htup = heap_form_tuple(statrel->rd_att, values, nulls);
512  CatalogTupleInsert(statrel, htup);
513  heap_freetuple(htup);
514 
516 
517  /*
518  * We used to create the pg_statistic_ext_data tuple too, but it's not
519  * clear what value should the stxdinherit flag have (it depends on
520  * whether the rel is partitioned, contains data, etc.)
521  */
522 
523  InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
524 
525  /*
526  * Invalidate relcache so that others see the new statistics object.
527  */
529 
530  relation_close(rel, NoLock);
531 
532  /*
533  * Add an AUTO dependency on each column used in the stats, so that the
534  * stats object goes away if any or all of them get dropped.
535  */
536  ObjectAddressSet(myself, StatisticExtRelationId, statoid);
537 
538  /* add dependencies for plain column references */
539  for (i = 0; i < nattnums; i++)
540  {
541  ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
542  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
543  }
544 
545  /*
546  * If there are no dependencies on a column, give the statistics object an
547  * auto dependency on the whole table. In most cases, this will be
548  * redundant, but it might not be if the statistics expressions contain no
549  * Vars (which might seem strange but possible). This is consistent with
550  * what we do for indexes in index_create.
551  *
552  * XXX We intentionally don't consider the expressions before adding this
553  * dependency, because recordDependencyOnSingleRelExpr may not create any
554  * dependencies for whole-row Vars.
555  */
556  if (!nattnums)
557  {
558  ObjectAddressSet(parentobject, RelationRelationId, relid);
559  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
560  }
561 
562  /*
563  * Store dependencies on anything mentioned in statistics expressions,
564  * just like we do for index expressions.
565  */
566  if (stxexprs)
568  (Node *) stxexprs,
569  relid,
571  DEPENDENCY_AUTO, false);
572 
573  /*
574  * Also add dependencies on namespace and owner. These are required
575  * because the stats object might have a different namespace and/or owner
576  * than the underlying table(s).
577  */
578  ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
579  recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
580 
581  recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
582 
583  /*
584  * XXX probably there should be a recordDependencyOnCurrentExtension call
585  * here too, but we'd have to add support for ALTER EXTENSION ADD/DROP
586  * STATISTICS, which is more work than it seems worth.
587  */
588 
589  /* Add any requested comment */
590  if (stmt->stxcomment != NULL)
591  CreateComments(statoid, StatisticExtRelationId, 0,
592  stmt->stxcomment);
593 
594  /* Return stats object's address */
595  return myself;
596 }
ArrayType * construct_array_builtin(Datum *elems, int nelems, Oid elmtype)
Definition: arrayfuncs.c:3340
int16 AttrNumber
Definition: attnum.h:21
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1106
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define CStringGetTextDatum(s)
Definition: builtins.h:94
#define lengthof(array)
Definition: c.h:777
bool IsSystemRelation(Relation relation)
Definition: catalog.c:75
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
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:1645
@ 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:223
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
bool allowSystemTableMods
Definition: globals.c:124
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1108
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:114
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1363
int i
Definition: isn.c:73
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
List * lappend(List *list, void *datum)
Definition: list.c:338
#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:826
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid QualifiedNameGetCreationNamespace(const List *names, char **objname_p)
Definition: namespace.c:3020
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:43
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
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:883
NameData attname
Definition: pg_attribute.h:41
int16 attnum
Definition: pg_attribute.h:74
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
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:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
void recordDependencyOnOwner(Oid classId, Oid objectId, Oid owner)
Definition: pg_shdepend.c:165
#define qsort(a, b, c, d)
Definition: port.h:445
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
static Datum CharGetDatum(char X)
Definition: postgres.h:122
#define InvalidOid
Definition: postgres_ext.h:36
#define RelationGetRelid(relation)
Definition: rel.h:504
#define RelationGetRelationName(relation)
Definition: rel.h:538
#define RelationGetNamespace(relation)
Definition: rel.h:545
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:831
static char * ChooseExtendedStatisticName(const char *name1, const char *name2, const char *label, Oid namespaceid)
Definition: statscmds.c:789
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:54
Definition: nodes.h:129
TupleDesc rd_att
Definition: rel.h:112
Form_pg_class rd_rel
Definition: rel.h:111
Node * expr
Definition: parsenodes.h:3281
Definition: primnodes.h:226
AttrNumber varattno
Definition: primnodes.h:238
Definition: c.h:704
Definition: c.h:730
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:961
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:193
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:344
#define TYPECACHE_LT_OPR
Definition: typcache.h:137
#define strVal(v)
Definition: value.h:82
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:291
const char * type

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, Assert(), attname, attnum, bms_next_member(), buildint2vector(), CacheInvalidateRelcache(), CatalogTupleInsert(), CharGetDatum(), ChooseExtendedStatisticName(), ChooseExtendedStatisticNameAddition(), compare_int16(), construct_array_builtin(), CreateComments(), CStringGetDatum(), CStringGetTextDatum, DEPENDENCY_AUTO, DEPENDENCY_NORMAL, equal(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail_relkind_not_supported(), errmsg(), ERROR, StatsElem::expr, exprType(), FirstLowInvalidHeapAttributeNumber, format_type_be(), get_attname(), get_relkind_objtype(), GetNewOidWithIndex(), GETSTRUCT, GetUserId(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, if(), Int16GetDatum(), InvalidObjectAddress, InvalidOid, InvokeObjectPostCreateHook, IsA, IsSystemRelation(), lappend(), lengthof, lfirst, lfirst_node, list_length(), lookup_type_cache(), StatsElem::name, NameGetDatum(), namestrcpy(), NIL, nodeToString(), NoLock, NOTICE, object_ownercheck(), ObjectAddressSet, ObjectAddressSubSet, ObjectIdGetDatum(), pfree(), PointerGetDatum(), pull_varattnos(), qsort, QualifiedNameGetCreationNamespace(), RelationData::rd_att, RelationData::rd_rel, recordDependencyOn(), recordDependencyOnOwner(), recordDependencyOnSingleRelExpr(), relation_close(), relation_openrv(), RelationGetNamespace, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheExists2, ShareUpdateExclusiveLock, STATEXTNAMENSP, STATS_MAX_DIMENSIONS, stmt, strVal, table_open(), type, TYPECACHE_LT_OPR, types, values, and Var::varattno.

Referenced by ATExecAddStatistics(), and ProcessUtilitySlow().

◆ RemoveStatisticsById()

void RemoveStatisticsById ( Oid  statsOid)

Definition at line 734 of file statscmds.c.

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

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 709 of file statscmds.c.

710 {
711  Relation relation;
712  HeapTuple tup;
713 
714  relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
715 
717  BoolGetDatum(inh));
718 
719  /* We don't know if the data row for inh value exists. */
720  if (HeapTupleIsValid(tup))
721  {
722  CatalogTupleDelete(relation, &tup->t_self);
723 
724  ReleaseSysCache(tup);
725  }
726 
727  table_close(relation, RowExclusiveLock);
728 }
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:831
@ STATEXTDATASTXOID
Definition: syscache.h:94

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 878 of file statscmds.c.

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

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