PostgreSQL Source Code  git master
statscmds.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * statscmds.c
4  * Commands for creating and altering extended statistics objects
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  * src/backend/commands/statscmds.c
12  *
13  *-------------------------------------------------------------------------
14  */
15 #include "postgres.h"
16 
17 #include "access/heapam.h"
18 #include "access/relation.h"
19 #include "access/relscan.h"
20 #include "access/table.h"
21 #include "catalog/catalog.h"
22 #include "catalog/dependency.h"
23 #include "catalog/indexing.h"
24 #include "catalog/namespace.h"
25 #include "catalog/objectaccess.h"
26 #include "catalog/pg_namespace.h"
29 #include "commands/comment.h"
30 #include "commands/defrem.h"
31 #include "miscadmin.h"
32 #include "nodes/nodeFuncs.h"
33 #include "optimizer/optimizer.h"
34 #include "statistics/statistics.h"
35 #include "utils/builtins.h"
36 #include "utils/lsyscache.h"
37 #include "utils/fmgroids.h"
38 #include "utils/inval.h"
39 #include "utils/memutils.h"
40 #include "utils/rel.h"
41 #include "utils/syscache.h"
42 #include "utils/typcache.h"
43 
44 
45 static char *ChooseExtendedStatisticName(const char *name1, const char *name2,
46  const char *label, Oid namespaceid);
47 static char *ChooseExtendedStatisticNameAddition(List *exprs);
48 
49 
50 /* qsort comparator for the attnums in CreateStatistics */
51 static int
52 compare_int16(const void *a, const void *b)
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 }
60 
61 /*
62  * CREATE STATISTICS
63  */
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  Datum datavalues[Natts_pg_statistic_ext_data];
79  bool datanulls[Natts_pg_statistic_ext_data];
80  int2vector *stxkeys;
81  List *stxexprs = NIL;
82  Datum exprsDatum;
83  Relation statrel;
84  Relation datarel;
85  Relation rel = NULL;
86  Oid relid;
87  ObjectAddress parentobject,
88  myself;
89  Datum types[4]; /* one for each possible type of statistic */
90  int ntypes;
91  ArrayType *stxkind;
92  bool build_ndistinct;
93  bool build_dependencies;
94  bool build_mcv;
95  bool build_expressions;
96  bool requested_type = false;
97  int i;
98  ListCell *cell;
99  ListCell *cell2;
100 
101  Assert(IsA(stmt, CreateStatsStmt));
102 
103  /*
104  * Examine the FROM clause. Currently, we only allow it to be a single
105  * simple table, but later we'll probably allow multiple tables and JOIN
106  * syntax. The grammar is already prepared for that, so we have to check
107  * here that what we got is what we can support.
108  */
109  if (list_length(stmt->relations) != 1)
110  ereport(ERROR,
111  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
112  errmsg("only a single relation is allowed in CREATE STATISTICS")));
113 
114  foreach(cell, stmt->relations)
115  {
116  Node *rln = (Node *) lfirst(cell);
117 
118  if (!IsA(rln, RangeVar))
119  ereport(ERROR,
120  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
121  errmsg("only a single relation is allowed in CREATE STATISTICS")));
122 
123  /*
124  * CREATE STATISTICS will influence future execution plans but does
125  * not interfere with currently executing plans. So it should be
126  * enough to take only ShareUpdateExclusiveLock on relation,
127  * conflicting with ANALYZE and other DDL that sets statistical
128  * information, but not with normal queries.
129  */
131 
132  /* Restrict to allowed relation types */
133  if (rel->rd_rel->relkind != RELKIND_RELATION &&
134  rel->rd_rel->relkind != RELKIND_MATVIEW &&
135  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
136  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
137  ereport(ERROR,
138  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
139  errmsg("cannot define statistics for relation \"%s\"",
141  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
142 
143  /* You must own the relation to create stats on it */
144  if (!pg_class_ownercheck(RelationGetRelid(rel), stxowner))
147 
148  /* Creating statistics on system catalogs is not allowed */
150  ereport(ERROR,
151  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
152  errmsg("permission denied: \"%s\" is a system catalog",
153  RelationGetRelationName(rel))));
154  }
155 
156  Assert(rel);
157  relid = RelationGetRelid(rel);
158 
159  /*
160  * If the node has a name, split it up and determine creation namespace.
161  * If not (a possibility not considered by the grammar, but one which can
162  * occur via the "CREATE TABLE ... (LIKE)" command), then we put the
163  * object in the same namespace as the relation, and cons up a name for
164  * it.
165  */
166  if (stmt->defnames)
167  namespaceId = QualifiedNameGetCreationNamespace(stmt->defnames,
168  &namestr);
169  else
170  {
171  namespaceId = RelationGetNamespace(rel);
174  "stat",
175  namespaceId);
176  }
177  namestrcpy(&stxname, namestr);
178 
179  /*
180  * Deal with the possibility that the statistics object already exists.
181  */
183  CStringGetDatum(namestr),
184  ObjectIdGetDatum(namespaceId)))
185  {
186  if (stmt->if_not_exists)
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 */
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  if (attnum <= 0)
304  ereport(ERROR,
305  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
306  errmsg("statistics creation on system columns is not supported")));
307  }
308 
309  /*
310  * Disallow data types without a less-than operator.
311  *
312  * We ignore this for statistics on a single expression, in which
313  * case we'll build the regular statistics only (and that code can
314  * deal with such data types).
315  */
316  if (list_length(stmt->exprs) > 1)
317  {
318  atttype = exprType(expr);
319  type = lookup_type_cache(atttype, TYPECACHE_LT_OPR);
320  if (type->lt_opr == InvalidOid)
321  ereport(ERROR,
322  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
323  errmsg("expression cannot be used in multivariate statistics because its type %s has no default btree operator class",
324  format_type_be(atttype))));
325  }
326 
327  stxexprs = lappend(stxexprs, expr);
328  }
329  }
330 
331  /*
332  * Parse the statistics kinds.
333  *
334  * First check that if this is the case with a single expression, there
335  * are no statistics kinds specified (we don't allow that for the simple
336  * CREATE STATISTICS form).
337  */
338  if ((list_length(stmt->exprs) == 1) && (list_length(stxexprs) == 1))
339  {
340  /* statistics kinds not specified */
341  if (list_length(stmt->stat_types) > 0)
342  ereport(ERROR,
343  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
344  errmsg("when building statistics on a single expression, statistics kinds may not be specified")));
345  }
346 
347  /* OK, let's check that we recognize the statistics kinds. */
348  build_ndistinct = false;
349  build_dependencies = false;
350  build_mcv = false;
351  foreach(cell, stmt->stat_types)
352  {
353  char *type = strVal(lfirst(cell));
354 
355  if (strcmp(type, "ndistinct") == 0)
356  {
357  build_ndistinct = true;
358  requested_type = true;
359  }
360  else if (strcmp(type, "dependencies") == 0)
361  {
362  build_dependencies = true;
363  requested_type = true;
364  }
365  else if (strcmp(type, "mcv") == 0)
366  {
367  build_mcv = true;
368  requested_type = true;
369  }
370  else
371  ereport(ERROR,
372  (errcode(ERRCODE_SYNTAX_ERROR),
373  errmsg("unrecognized statistics kind \"%s\"",
374  type)));
375  }
376 
377  /*
378  * If no statistic type was specified, build them all (but only when the
379  * statistics is defined on more than one column/expression).
380  */
381  if ((!requested_type) && (numcols >= 2))
382  {
383  build_ndistinct = true;
384  build_dependencies = true;
385  build_mcv = true;
386  }
387 
388  /*
389  * When there are non-trivial expressions, build the expression stats
390  * automatically. This allows calculating good estimates for stats that
391  * consider per-clause estimates (e.g. functional dependencies).
392  */
393  build_expressions = (list_length(stxexprs) > 0);
394 
395  /*
396  * Check that at least two columns were specified in the statement, or
397  * that we're building statistics on a single expression.
398  */
399  if ((numcols < 2) && (list_length(stxexprs) != 1))
400  ereport(ERROR,
401  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
402  errmsg("extended statistics require at least 2 columns")));
403 
404  /*
405  * Sort the attnums, which makes detecting duplicates somewhat easier, and
406  * it does not hurt (it does not matter for the contents, unlike for
407  * indexes, for example).
408  */
409  qsort(attnums, nattnums, sizeof(int16), compare_int16);
410 
411  /*
412  * Check for duplicates in the list of columns. The attnums are sorted so
413  * just check consecutive elements.
414  */
415  for (i = 1; i < nattnums; i++)
416  {
417  if (attnums[i] == attnums[i - 1])
418  ereport(ERROR,
419  (errcode(ERRCODE_DUPLICATE_COLUMN),
420  errmsg("duplicate column name in statistics definition")));
421  }
422 
423  /*
424  * Check for duplicate expressions. We do two loops, counting the
425  * occurrences of each expression. This is O(N^2) but we only allow small
426  * number of expressions and it's not executed often.
427  *
428  * XXX We don't cross-check attributes and expressions, because it does
429  * not seem worth it. In principle we could check that expressions don't
430  * contain trivial attribute references like "(a)", but the reasoning is
431  * similar to why we don't bother with extracting columns from
432  * expressions. It's either expensive or very easy to defeat for
433  * determined user, and there's no risk if we allow such statistics (the
434  * statistics is useless, but harmless).
435  */
436  foreach(cell, stxexprs)
437  {
438  Node *expr1 = (Node *) lfirst(cell);
439  int cnt = 0;
440 
441  foreach(cell2, stxexprs)
442  {
443  Node *expr2 = (Node *) lfirst(cell2);
444 
445  if (equal(expr1, expr2))
446  cnt += 1;
447  }
448 
449  /* every expression should find at least itself */
450  Assert(cnt >= 1);
451 
452  if (cnt > 1)
453  ereport(ERROR,
454  (errcode(ERRCODE_DUPLICATE_COLUMN),
455  errmsg("duplicate expression in statistics definition")));
456  }
457 
458  /* Form an int2vector representation of the sorted column list */
459  stxkeys = buildint2vector(attnums, nattnums);
460 
461  /* construct the char array of enabled statistic types */
462  ntypes = 0;
463  if (build_ndistinct)
464  types[ntypes++] = CharGetDatum(STATS_EXT_NDISTINCT);
465  if (build_dependencies)
466  types[ntypes++] = CharGetDatum(STATS_EXT_DEPENDENCIES);
467  if (build_mcv)
468  types[ntypes++] = CharGetDatum(STATS_EXT_MCV);
469  if (build_expressions)
470  types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
471  Assert(ntypes > 0 && ntypes <= lengthof(types));
472  stxkind = construct_array(types, ntypes, CHAROID, 1, true, TYPALIGN_CHAR);
473 
474  /* convert the expressions (if any) to a text datum */
475  if (stxexprs != NIL)
476  {
477  char *exprsString;
478 
479  exprsString = nodeToString(stxexprs);
480  exprsDatum = CStringGetTextDatum(exprsString);
481  pfree(exprsString);
482  }
483  else
484  exprsDatum = (Datum) 0;
485 
486  statrel = table_open(StatisticExtRelationId, RowExclusiveLock);
487 
488  /*
489  * Everything seems fine, so let's build the pg_statistic_ext tuple.
490  */
491  memset(values, 0, sizeof(values));
492  memset(nulls, false, sizeof(nulls));
493 
494  statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
495  Anum_pg_statistic_ext_oid);
496  values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
497  values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
498  values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
499  values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
500  values[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(-1);
501  values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
502  values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
503  values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
504 
505  values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
506  if (exprsDatum == (Datum) 0)
507  nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
508 
509  /* insert it into pg_statistic_ext */
510  htup = heap_form_tuple(statrel->rd_att, values, nulls);
511  CatalogTupleInsert(statrel, htup);
512  heap_freetuple(htup);
513 
515 
516  /*
517  * Also build the pg_statistic_ext_data tuple, to hold the actual
518  * statistics data.
519  */
520  datarel = table_open(StatisticExtDataRelationId, RowExclusiveLock);
521 
522  memset(datavalues, 0, sizeof(datavalues));
523  memset(datanulls, false, sizeof(datanulls));
524 
525  datavalues[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statoid);
526 
527  /* no statistics built yet */
528  datanulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
529  datanulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
530  datanulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
531  datanulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
532 
533  /* insert it into pg_statistic_ext_data */
534  htup = heap_form_tuple(datarel->rd_att, datavalues, datanulls);
535  CatalogTupleInsert(datarel, htup);
536  heap_freetuple(htup);
537 
539 
540  InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
541 
542  /*
543  * Invalidate relcache so that others see the new statistics object.
544  */
546 
547  relation_close(rel, NoLock);
548 
549  /*
550  * Add an AUTO dependency on each column used in the stats, so that the
551  * stats object goes away if any or all of them get dropped.
552  */
553  ObjectAddressSet(myself, StatisticExtRelationId, statoid);
554 
555  /* add dependencies for plain column references */
556  for (i = 0; i < nattnums; i++)
557  {
558  ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
559  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
560  }
561 
562  /*
563  * If there are no dependencies on a column, give the statistics object an
564  * auto dependency on the whole table. In most cases, this will be
565  * redundant, but it might not be if the statistics expressions contain no
566  * Vars (which might seem strange but possible). This is consistent with
567  * what we do for indexes in index_create.
568  *
569  * XXX We intentionally don't consider the expressions before adding this
570  * dependency, because recordDependencyOnSingleRelExpr may not create any
571  * dependencies for whole-row Vars.
572  */
573  if (!nattnums)
574  {
575  ObjectAddressSet(parentobject, RelationRelationId, relid);
576  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
577  }
578 
579  /*
580  * Store dependencies on anything mentioned in statistics expressions,
581  * just like we do for index expressions.
582  */
583  if (stxexprs)
585  (Node *) stxexprs,
586  relid,
588  DEPENDENCY_AUTO, false);
589 
590  /*
591  * Also add dependencies on namespace and owner. These are required
592  * because the stats object might have a different namespace and/or owner
593  * than the underlying table(s).
594  */
595  ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
596  recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
597 
598  recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
599 
600  /*
601  * XXX probably there should be a recordDependencyOnCurrentExtension call
602  * here too, but we'd have to add support for ALTER EXTENSION ADD/DROP
603  * STATISTICS, which is more work than it seems worth.
604  */
605 
606  /* Add any requested comment */
607  if (stmt->stxcomment != NULL)
608  CreateComments(statoid, StatisticExtRelationId, 0,
609  stmt->stxcomment);
610 
611  /* Return stats object's address */
612  return myself;
613 }
614 
615 /*
616  * ALTER STATISTICS
617  */
620 {
621  Relation rel;
622  Oid stxoid;
623  HeapTuple oldtup;
624  HeapTuple newtup;
625  Datum repl_val[Natts_pg_statistic_ext];
626  bool repl_null[Natts_pg_statistic_ext];
627  bool repl_repl[Natts_pg_statistic_ext];
628  ObjectAddress address;
629  int newtarget = stmt->stxstattarget;
630 
631  /* Limit statistics target to a sane range */
632  if (newtarget < -1)
633  {
634  ereport(ERROR,
635  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
636  errmsg("statistics target %d is too low",
637  newtarget)));
638  }
639  else if (newtarget > 10000)
640  {
641  newtarget = 10000;
643  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
644  errmsg("lowering statistics target to %d",
645  newtarget)));
646  }
647 
648  /* lookup OID of the statistics object */
649  stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
650 
651  /*
652  * If we got here and the OID is not valid, it means the statistics object
653  * does not exist, but the command specified IF EXISTS. So report this as
654  * a simple NOTICE and we're done.
655  */
656  if (!OidIsValid(stxoid))
657  {
658  char *schemaname;
659  char *statname;
660 
661  Assert(stmt->missing_ok);
662 
663  DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
664 
665  if (schemaname)
666  ereport(NOTICE,
667  (errmsg("statistics object \"%s.%s\" does not exist, skipping",
668  schemaname, statname)));
669  else
670  ereport(NOTICE,
671  (errmsg("statistics object \"%s\" does not exist, skipping",
672  statname)));
673 
674  return InvalidObjectAddress;
675  }
676 
677  /* Search pg_statistic_ext */
678  rel = table_open(StatisticExtRelationId, RowExclusiveLock);
679 
680  oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
681 
682  /* Must be owner of the existing statistics object */
685  NameListToString(stmt->defnames));
686 
687  /* Build new tuple. */
688  memset(repl_val, 0, sizeof(repl_val));
689  memset(repl_null, false, sizeof(repl_null));
690  memset(repl_repl, false, sizeof(repl_repl));
691 
692  /* replace the stxstattarget column */
693  repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
694  repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(newtarget);
695 
696  newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
697  repl_val, repl_null, repl_repl);
698 
699  /* Update system catalog. */
700  CatalogTupleUpdate(rel, &newtup->t_self, newtup);
701 
702  InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
703 
704  ObjectAddressSet(address, StatisticExtRelationId, stxoid);
705 
706  /*
707  * NOTE: because we only support altering the statistics target, not the
708  * other fields, there is no need to update dependencies.
709  */
710 
711  heap_freetuple(newtup);
712  ReleaseSysCache(oldtup);
713 
715 
716  return address;
717 }
718 
719 /*
720  * Guts of statistics object deletion.
721  */
722 void
724 {
725  Relation relation;
726  HeapTuple tup;
727  Form_pg_statistic_ext statext;
728  Oid relid;
729 
730  /*
731  * First delete the pg_statistic_ext_data tuple holding the actual
732  * statistical data.
733  */
734  relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
735 
737 
738  if (!HeapTupleIsValid(tup)) /* should not happen */
739  elog(ERROR, "cache lookup failed for statistics data %u", statsOid);
740 
741  CatalogTupleDelete(relation, &tup->t_self);
742 
743  ReleaseSysCache(tup);
744 
745  table_close(relation, RowExclusiveLock);
746 
747  /*
748  * Delete the pg_statistic_ext tuple. Also send out a cache inval on the
749  * associated table, so that dependent plans will be rebuilt.
750  */
751  relation = table_open(StatisticExtRelationId, RowExclusiveLock);
752 
753  tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
754 
755  if (!HeapTupleIsValid(tup)) /* should not happen */
756  elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
757 
758  statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
759  relid = statext->stxrelid;
760 
762 
763  CatalogTupleDelete(relation, &tup->t_self);
764 
765  ReleaseSysCache(tup);
766 
767  table_close(relation, RowExclusiveLock);
768 }
769 
770 /*
771  * Select a nonconflicting name for a new statistics object.
772  *
773  * name1, name2, and label are used the same way as for makeObjectName(),
774  * except that the label can't be NULL; digits will be appended to the label
775  * if needed to create a name that is unique within the specified namespace.
776  *
777  * Returns a palloc'd string.
778  *
779  * Note: it is theoretically possible to get a collision anyway, if someone
780  * else chooses the same name concurrently. This is fairly unlikely to be
781  * a problem in practice, especially if one is holding a share update
782  * exclusive lock on the relation identified by name1. However, if choosing
783  * multiple names within a single command, you'd better create the new object
784  * and do CommandCounterIncrement before choosing the next one!
785  */
786 static char *
787 ChooseExtendedStatisticName(const char *name1, const char *name2,
788  const char *label, Oid namespaceid)
789 {
790  int pass = 0;
791  char *stxname = NULL;
792  char modlabel[NAMEDATALEN];
793 
794  /* try the unmodified label first */
795  strlcpy(modlabel, label, sizeof(modlabel));
796 
797  for (;;)
798  {
799  Oid existingstats;
800 
801  stxname = makeObjectName(name1, name2, modlabel);
802 
803  existingstats = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
804  PointerGetDatum(stxname),
805  ObjectIdGetDatum(namespaceid));
806  if (!OidIsValid(existingstats))
807  break;
808 
809  /* found a conflict, so try a new name component */
810  pfree(stxname);
811  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
812  }
813 
814  return stxname;
815 }
816 
817 /*
818  * Generate "name2" for a new statistics object given the list of column
819  * names for it. This will be passed to ChooseExtendedStatisticName along
820  * with the parent table name and a suitable label.
821  *
822  * We know that less than NAMEDATALEN characters will actually be used,
823  * so we can truncate the result once we've generated that many.
824  *
825  * XXX see also ChooseForeignKeyConstraintNameAddition and
826  * ChooseIndexNameAddition.
827  */
828 static char *
830 {
831  char buf[NAMEDATALEN * 2];
832  int buflen = 0;
833  ListCell *lc;
834 
835  buf[0] = '\0';
836  foreach(lc, exprs)
837  {
838  StatsElem *selem = (StatsElem *) lfirst(lc);
839  const char *name;
840 
841  /* It should be one of these, but just skip if it happens not to be */
842  if (!IsA(selem, StatsElem))
843  continue;
844 
845  name = selem->name;
846 
847  if (buflen > 0)
848  buf[buflen++] = '_'; /* insert _ between names */
849 
850  /*
851  * We use fixed 'expr' for expressions, which have empty column names.
852  * For indexes this is handled in ChooseIndexColumnNames, but we have
853  * no such function for stats and it does not seem worth adding. If a
854  * better name is needed, the user can specify it explicitly.
855  */
856  if (!name)
857  name = "expr";
858 
859  /*
860  * At this point we have buflen <= NAMEDATALEN. name should be less
861  * than NAMEDATALEN already, but use strlcpy for paranoia.
862  */
863  strlcpy(buf + buflen, name, NAMEDATALEN);
864  buflen += strlen(buf + buflen);
865  if (buflen >= NAMEDATALEN)
866  break;
867  }
868  return pstrdup(buf);
869 }
870 
871 /*
872  * StatisticsGetRelation: given a statistics object's OID, get the OID of
873  * the relation it is defined on. Uses the system cache.
874  */
875 Oid
876 StatisticsGetRelation(Oid statId, bool missing_ok)
877 {
878  HeapTuple tuple;
880  Oid result;
881 
882  tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statId));
883  if (!HeapTupleIsValid(tuple))
884  {
885  if (missing_ok)
886  return InvalidOid;
887  elog(ERROR, "cache lookup failed for statistics object %u", statId);
888  }
889  stx = (Form_pg_statistic_ext) GETSTRUCT(tuple);
890  Assert(stx->oid == statId);
891 
892  result = stx->stxrelid;
893  ReleaseSysCache(tuple);
894  return result;
895 }
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:587
#define NameGetDatum(X)
Definition: postgres.h:639
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:167
static char * ChooseExtendedStatisticName(const char *name1, const char *name2, const char *label, Oid namespaceid)
Definition: statscmds.c:787
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:151
void RemoveStatisticsById(Oid statsOid)
Definition: statscmds.c:723
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
char * name
Definition: parsenodes.h:2942
struct typedefs * types
Definition: ecpg.c:29
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3149
#define RelationGetDescr(relation)
Definition: rel.h:503
Oid GetUserId(void)
Definition: miscinit.c:495
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
char * pstrdup(const char *in)
Definition: mcxt.c:1299
int2vector * buildint2vector(const int16 *int2s, int n)
Definition: int.c:114
struct @11::@12 av[32]
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1043
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3319
Definition: nodes.h:536
#define strVal(v)
Definition: value.h:65
int errcode(int sqlerrcode)
Definition: elog.c:698
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:876
AttrNumber varattno
Definition: primnodes.h:191
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:44
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2854
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
Definition: primnodes.h:186
#define OidIsValid(objectId)
Definition: c.h:710
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition: var.c:281
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2198
#define NAMEDATALEN
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3308
ObjectAddress AlterStatistics(AlterStatsStmt *stmt)
Definition: statscmds.c:619
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:2943
Oid vartype
Definition: primnodes.h:193
ItemPointerData t_self
Definition: htup.h:65
Oid get_statistics_object_oid(List *names, bool missing_ok)
Definition: namespace.c:2223
#define lfirst_node(type, lc)
Definition: pg_list.h:172
Definition: c.h:675
#define NoLock
Definition: lockdefs.h:34
static char * buf
Definition: pg_test_fsync.c:68
#define RowExclusiveLock
Definition: lockdefs.h:38
#define CStringGetDatum(X)
Definition: postgres.h:622
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1401
#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:52
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:175
List * lappend(List *list, void *datum)
Definition: list.c:336
#define WARNING
Definition: elog.h:40
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:1127
char * NameListToString(List *names)
Definition: namespace.c:3147
Definition: c.h:649
uintptr_t Datum
Definition: postgres.h:411
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1175
static char * label
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
int16 attnum
Definition: pg_attribute.h:83
#define ereport(elevel,...)
Definition: elog.h:157
#define NOTICE
Definition: elog.h:37
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
#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
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4818
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition: syscache.h:195
static char * ChooseExtendedStatisticNameAddition(List *exprs)
Definition: statscmds.c:829
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1614
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301
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
const char * name
Definition: encode.c:561
#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:1342
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#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
#define elog(elevel,...)
Definition: elog.h:232
int i
ObjectType get_relkind_objtype(char relkind)
#define TYPECACHE_LT_OPR
Definition: typcache.h:137
#define CStringGetTextDatum(s)
Definition: builtins.h:86
char * nodeToString(const void *obj)
Definition: outfuncs.c:4544
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:825
#define qsort(a, b, c, d)
Definition: port.h:505
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
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: pg_list.h:50
#define snprintf
Definition: port.h:217
int16 AttrNumber
Definition: attnum.h:21
#define RelationGetRelid(relation)
Definition: rel.h:477
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
FormData_pg_statistic_ext * Form_pg_statistic_ext
#define RelationGetNamespace(relation)
Definition: rel.h:518
bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
Definition: aclchk.c:5408
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:65