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/fmgroids.h"
37 #include "utils/inval.h"
38 #include "utils/memutils.h"
39 #include "utils/rel.h"
40 #include "utils/syscache.h"
41 #include "utils/typcache.h"
42 
43 
44 static char *ChooseExtendedStatisticName(const char *name1, const char *name2,
45  const char *label, Oid namespaceid);
46 static char *ChooseExtendedStatisticNameAddition(List *exprs);
47 
48 
49 /* qsort comparator for the attnums in CreateStatistics */
50 static int
51 compare_int16(const void *a, const void *b)
52 {
53  int av = *(const int16 *) a;
54  int bv = *(const int16 *) b;
55 
56  /* this can't overflow if int is wider than int16 */
57  return (av - bv);
58 }
59 
60 /*
61  * CREATE STATISTICS
62  */
65 {
66  int16 attnums[STATS_MAX_DIMENSIONS];
67  int nattnums = 0;
68  int numcols;
69  char *namestr;
70  NameData stxname;
71  Oid statoid;
72  Oid namespaceId;
73  Oid stxowner = GetUserId();
74  HeapTuple htup;
75  Datum values[Natts_pg_statistic_ext];
76  bool nulls[Natts_pg_statistic_ext];
77  Datum datavalues[Natts_pg_statistic_ext_data];
78  bool datanulls[Natts_pg_statistic_ext_data];
79  int2vector *stxkeys;
80  List *stxexprs = NIL;
81  Datum exprsDatum;
82  Relation statrel;
83  Relation datarel;
84  Relation rel = NULL;
85  Oid relid;
86  ObjectAddress parentobject,
87  myself;
88  Datum types[4]; /* one for each possible type of statistic */
89  int ntypes;
90  ArrayType *stxkind;
91  bool build_ndistinct;
92  bool build_dependencies;
93  bool build_mcv;
94  bool build_expressions;
95  bool requested_type = false;
96  int i;
97  ListCell *cell;
98  ListCell *cell2;
99 
100  Assert(IsA(stmt, CreateStatsStmt));
101 
102  /*
103  * Examine the FROM clause. Currently, we only allow it to be a single
104  * simple table, but later we'll probably allow multiple tables and JOIN
105  * syntax. The grammar is already prepared for that, so we have to check
106  * here that what we got is what we can support.
107  */
108  if (list_length(stmt->relations) != 1)
109  ereport(ERROR,
110  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
111  errmsg("only a single relation is allowed in CREATE STATISTICS")));
112 
113  foreach(cell, stmt->relations)
114  {
115  Node *rln = (Node *) lfirst(cell);
116 
117  if (!IsA(rln, RangeVar))
118  ereport(ERROR,
119  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
120  errmsg("only a single relation is allowed in CREATE STATISTICS")));
121 
122  /*
123  * CREATE STATISTICS will influence future execution plans but does
124  * not interfere with currently executing plans. So it should be
125  * enough to take only ShareUpdateExclusiveLock on relation,
126  * conflicting with ANALYZE and other DDL that sets statistical
127  * information, but not with normal queries.
128  */
130 
131  /* Restrict to allowed relation types */
132  if (rel->rd_rel->relkind != RELKIND_RELATION &&
133  rel->rd_rel->relkind != RELKIND_MATVIEW &&
134  rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
135  rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
136  ereport(ERROR,
137  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
138  errmsg("relation \"%s\" is not a table, foreign table, or materialized view",
139  RelationGetRelationName(rel))));
140 
141  /* You must own the relation to create stats on it */
142  if (!pg_class_ownercheck(RelationGetRelid(rel), stxowner))
145 
146  /* Creating statistics on system catalogs is not allowed */
148  ereport(ERROR,
149  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
150  errmsg("permission denied: \"%s\" is a system catalog",
151  RelationGetRelationName(rel))));
152  }
153 
154  Assert(rel);
155  relid = RelationGetRelid(rel);
156 
157  /*
158  * If the node has a name, split it up and determine creation namespace.
159  * If not (a possibility not considered by the grammar, but one which can
160  * occur via the "CREATE TABLE ... (LIKE)" command), then we put the
161  * object in the same namespace as the relation, and cons up a name for
162  * it.
163  */
164  if (stmt->defnames)
165  namespaceId = QualifiedNameGetCreationNamespace(stmt->defnames,
166  &namestr);
167  else
168  {
169  namespaceId = RelationGetNamespace(rel);
172  "stat",
173  namespaceId);
174  }
175  namestrcpy(&stxname, namestr);
176 
177  /*
178  * Deal with the possibility that the statistics object already exists.
179  */
181  CStringGetDatum(namestr),
182  ObjectIdGetDatum(namespaceId)))
183  {
184  if (stmt->if_not_exists)
185  {
186  ereport(NOTICE,
188  errmsg("statistics object \"%s\" already exists, skipping",
189  namestr)));
190  relation_close(rel, NoLock);
191  return InvalidObjectAddress;
192  }
193 
194  ereport(ERROR,
196  errmsg("statistics object \"%s\" already exists", namestr)));
197  }
198 
199  /*
200  * Make sure no more than STATS_MAX_DIMENSIONS columns are used. There
201  * might be duplicates and so on, but we'll deal with those later.
202  */
203  numcols = list_length(stmt->exprs);
204  if (numcols > STATS_MAX_DIMENSIONS)
205  ereport(ERROR,
206  (errcode(ERRCODE_TOO_MANY_COLUMNS),
207  errmsg("cannot have more than %d columns in statistics",
209 
210  /*
211  * Convert the expression list to a simple array of attnums, but also keep
212  * a list of more complex expressions. While at it, enforce some
213  * constraints.
214  *
215  * XXX We do only the bare minimum to separate simple attribute and
216  * complex expressions - for example "(a)" will be treated as a complex
217  * expression. No matter how elaborate the check is, there'll always be a
218  * way around it, if the user is determined (consider e.g. "(a+0)"), so
219  * it's not worth protecting against it.
220  */
221  foreach(cell, stmt->exprs)
222  {
223  Node *expr = (Node *) lfirst(cell);
224  StatsElem *selem;
225  HeapTuple atttuple;
226  Form_pg_attribute attForm;
228 
229  /*
230  * We should not get anything else than StatsElem, given the grammar.
231  * But let's keep it as a safety.
232  */
233  if (!IsA(expr, StatsElem))
234  ereport(ERROR,
235  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
236  errmsg("only simple column references and expressions are allowed in CREATE STATISTICS")));
237 
238  selem = (StatsElem *) expr;
239 
240  if (selem->name) /* column reference */
241  {
242  char *attname;
243 
244  attname = selem->name;
245 
246  atttuple = SearchSysCacheAttName(relid, attname);
247  if (!HeapTupleIsValid(atttuple))
248  ereport(ERROR,
249  (errcode(ERRCODE_UNDEFINED_COLUMN),
250  errmsg("column \"%s\" does not exist",
251  attname)));
252  attForm = (Form_pg_attribute) GETSTRUCT(atttuple);
253 
254  /* Disallow use of system attributes in extended stats */
255  if (attForm->attnum <= 0)
256  ereport(ERROR,
257  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
258  errmsg("statistics creation on system columns is not supported")));
259 
260  /* Disallow data types without a less-than operator */
261  type = lookup_type_cache(attForm->atttypid, TYPECACHE_LT_OPR);
262  if (type->lt_opr == InvalidOid)
263  ereport(ERROR,
264  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
265  errmsg("column \"%s\" cannot be used in statistics because its type %s has no default btree operator class",
266  attname, format_type_be(attForm->atttypid))));
267 
268  attnums[nattnums] = attForm->attnum;
269  nattnums++;
270  ReleaseSysCache(atttuple);
271  }
272  else /* expression */
273  {
274  Node *expr = selem->expr;
275  Oid atttype;
276 
277  Assert(expr != NULL);
278 
279  /*
280  * Disallow data types without a less-than operator.
281  *
282  * We ignore this for statistics on a single expression, in which
283  * case we'll build the regular statistics only (and that code can
284  * deal with such data types).
285  */
286  if (list_length(stmt->exprs) > 1)
287  {
288  atttype = exprType(expr);
289  type = lookup_type_cache(atttype, TYPECACHE_LT_OPR);
290  if (type->lt_opr == InvalidOid)
291  ereport(ERROR,
292  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
293  errmsg("expression cannot be used in multivariate statistics because its type %s has no default btree operator class",
294  format_type_be(atttype))));
295  }
296 
297  stxexprs = lappend(stxexprs, expr);
298  }
299  }
300 
301  /*
302  * Parse the statistics kinds.
303  *
304  * First check that if this is the case with a single expression, there
305  * are no statistics kinds specified (we don't allow that for the simple
306  * CREATE STATISTICS form).
307  */
308  if ((list_length(stmt->exprs) == 1) && (list_length(stxexprs) == 1))
309  {
310  /* statistics kinds not specified */
311  if (list_length(stmt->stat_types) > 0)
312  ereport(ERROR,
313  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
314  errmsg("when building statistics on a single expression, statistics kinds may not be specified")));
315  }
316 
317  /* OK, let's check that we recognize the statistics kinds. */
318  build_ndistinct = false;
319  build_dependencies = false;
320  build_mcv = false;
321  foreach(cell, stmt->stat_types)
322  {
323  char *type = strVal((Value *) lfirst(cell));
324 
325  if (strcmp(type, "ndistinct") == 0)
326  {
327  build_ndistinct = true;
328  requested_type = true;
329  }
330  else if (strcmp(type, "dependencies") == 0)
331  {
332  build_dependencies = true;
333  requested_type = true;
334  }
335  else if (strcmp(type, "mcv") == 0)
336  {
337  build_mcv = true;
338  requested_type = true;
339  }
340  else
341  ereport(ERROR,
342  (errcode(ERRCODE_SYNTAX_ERROR),
343  errmsg("unrecognized statistics kind \"%s\"",
344  type)));
345  }
346 
347  /*
348  * If no statistic type was specified, build them all (but only when the
349  * statistics is defined on more than one column/expression).
350  */
351  if ((!requested_type) && (numcols >= 2))
352  {
353  build_ndistinct = true;
354  build_dependencies = true;
355  build_mcv = true;
356  }
357 
358  /*
359  * When there are non-trivial expressions, build the expression stats
360  * automatically. This allows calculating good estimates for stats that
361  * consider per-clause estimates (e.g. functional dependencies).
362  */
363  build_expressions = (list_length(stxexprs) > 0);
364 
365  /*
366  * Check that at least two columns were specified in the statement, or
367  * that we're building statistics on a single expression.
368  */
369  if ((numcols < 2) && (list_length(stxexprs) != 1))
370  ereport(ERROR,
371  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
372  errmsg("extended statistics require at least 2 columns")));
373 
374  /*
375  * Sort the attnums, which makes detecting duplicates somewhat easier, and
376  * it does not hurt (it does not matter for the contents, unlike for
377  * indexes, for example).
378  */
379  qsort(attnums, nattnums, sizeof(int16), compare_int16);
380 
381  /*
382  * Check for duplicates in the list of columns. The attnums are sorted so
383  * just check consecutive elements.
384  */
385  for (i = 1; i < nattnums; i++)
386  {
387  if (attnums[i] == attnums[i - 1])
388  ereport(ERROR,
389  (errcode(ERRCODE_DUPLICATE_COLUMN),
390  errmsg("duplicate column name in statistics definition")));
391  }
392 
393  /*
394  * Check for duplicate expressions. We do two loops, counting the
395  * occurrences of each expression. This is O(N^2) but we only allow small
396  * number of expressions and it's not executed often.
397  *
398  * XXX We don't cross-check attributes and expressions, because it does
399  * not seem worth it. In principle we could check that expressions don't
400  * contain trivial attribute references like "(a)", but the reasoning is
401  * similar to why we don't bother with extracting columns from
402  * expressions. It's either expensive or very easy to defeat for
403  * determined user, and there's no risk if we allow such statistics (the
404  * statistics is useless, but harmless).
405  */
406  foreach(cell, stxexprs)
407  {
408  Node *expr1 = (Node *) lfirst(cell);
409  int cnt = 0;
410 
411  foreach(cell2, stxexprs)
412  {
413  Node *expr2 = (Node *) lfirst(cell2);
414 
415  if (equal(expr1, expr2))
416  cnt += 1;
417  }
418 
419  /* every expression should find at least itself */
420  Assert(cnt >= 1);
421 
422  if (cnt > 1)
423  ereport(ERROR,
424  (errcode(ERRCODE_DUPLICATE_COLUMN),
425  errmsg("duplicate expression in statistics definition")));
426  }
427 
428  /* Form an int2vector representation of the sorted column list */
429  stxkeys = buildint2vector(attnums, nattnums);
430 
431  /* construct the char array of enabled statistic types */
432  ntypes = 0;
433  if (build_ndistinct)
434  types[ntypes++] = CharGetDatum(STATS_EXT_NDISTINCT);
435  if (build_dependencies)
436  types[ntypes++] = CharGetDatum(STATS_EXT_DEPENDENCIES);
437  if (build_mcv)
438  types[ntypes++] = CharGetDatum(STATS_EXT_MCV);
439  if (build_expressions)
440  types[ntypes++] = CharGetDatum(STATS_EXT_EXPRESSIONS);
441  Assert(ntypes > 0 && ntypes <= lengthof(types));
442  stxkind = construct_array(types, ntypes, CHAROID, 1, true, TYPALIGN_CHAR);
443 
444  /* convert the expressions (if any) to a text datum */
445  if (stxexprs != NIL)
446  {
447  char *exprsString;
448 
449  exprsString = nodeToString(stxexprs);
450  exprsDatum = CStringGetTextDatum(exprsString);
451  pfree(exprsString);
452  }
453  else
454  exprsDatum = (Datum) 0;
455 
456  statrel = table_open(StatisticExtRelationId, RowExclusiveLock);
457 
458  /*
459  * Everything seems fine, so let's build the pg_statistic_ext tuple.
460  */
461  memset(values, 0, sizeof(values));
462  memset(nulls, false, sizeof(nulls));
463 
464  statoid = GetNewOidWithIndex(statrel, StatisticExtOidIndexId,
465  Anum_pg_statistic_ext_oid);
466  values[Anum_pg_statistic_ext_oid - 1] = ObjectIdGetDatum(statoid);
467  values[Anum_pg_statistic_ext_stxrelid - 1] = ObjectIdGetDatum(relid);
468  values[Anum_pg_statistic_ext_stxname - 1] = NameGetDatum(&stxname);
469  values[Anum_pg_statistic_ext_stxnamespace - 1] = ObjectIdGetDatum(namespaceId);
470  values[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(-1);
471  values[Anum_pg_statistic_ext_stxowner - 1] = ObjectIdGetDatum(stxowner);
472  values[Anum_pg_statistic_ext_stxkeys - 1] = PointerGetDatum(stxkeys);
473  values[Anum_pg_statistic_ext_stxkind - 1] = PointerGetDatum(stxkind);
474 
475  values[Anum_pg_statistic_ext_stxexprs - 1] = exprsDatum;
476  if (exprsDatum == (Datum) 0)
477  nulls[Anum_pg_statistic_ext_stxexprs - 1] = true;
478 
479  /* insert it into pg_statistic_ext */
480  htup = heap_form_tuple(statrel->rd_att, values, nulls);
481  CatalogTupleInsert(statrel, htup);
482  heap_freetuple(htup);
483 
485 
486  /*
487  * Also build the pg_statistic_ext_data tuple, to hold the actual
488  * statistics data.
489  */
490  datarel = table_open(StatisticExtDataRelationId, RowExclusiveLock);
491 
492  memset(datavalues, 0, sizeof(datavalues));
493  memset(datanulls, false, sizeof(datanulls));
494 
495  datavalues[Anum_pg_statistic_ext_data_stxoid - 1] = ObjectIdGetDatum(statoid);
496 
497  /* no statistics built yet */
498  datanulls[Anum_pg_statistic_ext_data_stxdndistinct - 1] = true;
499  datanulls[Anum_pg_statistic_ext_data_stxddependencies - 1] = true;
500  datanulls[Anum_pg_statistic_ext_data_stxdmcv - 1] = true;
501  datanulls[Anum_pg_statistic_ext_data_stxdexpr - 1] = true;
502 
503  /* insert it into pg_statistic_ext_data */
504  htup = heap_form_tuple(datarel->rd_att, datavalues, datanulls);
505  CatalogTupleInsert(datarel, htup);
506  heap_freetuple(htup);
507 
509 
510  InvokeObjectPostCreateHook(StatisticExtRelationId, statoid, 0);
511 
512  /*
513  * Invalidate relcache so that others see the new statistics object.
514  */
516 
517  relation_close(rel, NoLock);
518 
519  /*
520  * Add an AUTO dependency on each column used in the stats, so that the
521  * stats object goes away if any or all of them get dropped.
522  */
523  ObjectAddressSet(myself, StatisticExtRelationId, statoid);
524 
525  /* add dependencies for plain column references */
526  for (i = 0; i < nattnums; i++)
527  {
528  ObjectAddressSubSet(parentobject, RelationRelationId, relid, attnums[i]);
529  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
530  }
531 
532  /*
533  * If there are no dependencies on a column, give the statistics an auto
534  * dependency on the whole table. In most cases, this will be redundant,
535  * but it might not be if the statistics expressions contain no Vars
536  * (which might seem strange but possible). This is consistent with what
537  * we do for indexes in index_create.
538  *
539  * XXX We intentionally don't consider the expressions before adding this
540  * dependency, because recordDependencyOnSingleRelExpr may not create any
541  * dependencies for whole-row Vars.
542  */
543  if (!nattnums)
544  {
545  ObjectAddressSet(parentobject, RelationRelationId, relid);
546  recordDependencyOn(&myself, &parentobject, DEPENDENCY_AUTO);
547  }
548 
549  /*
550  * Store dependencies on anything mentioned in statistics expressions,
551  * just like we do for index expressions.
552  */
553  if (stxexprs)
555  (Node *) stxexprs,
556  relid,
558  DEPENDENCY_AUTO, false);
559 
560  /*
561  * Also add dependencies on namespace and owner. These are required
562  * because the stats object might have a different namespace and/or owner
563  * than the underlying table(s).
564  */
565  ObjectAddressSet(parentobject, NamespaceRelationId, namespaceId);
566  recordDependencyOn(&myself, &parentobject, DEPENDENCY_NORMAL);
567 
568  recordDependencyOnOwner(StatisticExtRelationId, statoid, stxowner);
569 
570  /*
571  * XXX probably there should be a recordDependencyOnCurrentExtension call
572  * here too, but we'd have to add support for ALTER EXTENSION ADD/DROP
573  * STATISTICS, which is more work than it seems worth.
574  */
575 
576  /* Add any requested comment */
577  if (stmt->stxcomment != NULL)
578  CreateComments(statoid, StatisticExtRelationId, 0,
579  stmt->stxcomment);
580 
581  /* Return stats object's address */
582  return myself;
583 }
584 
585 /*
586  * ALTER STATISTICS
587  */
590 {
591  Relation rel;
592  Oid stxoid;
593  HeapTuple oldtup;
594  HeapTuple newtup;
595  Datum repl_val[Natts_pg_statistic_ext];
596  bool repl_null[Natts_pg_statistic_ext];
597  bool repl_repl[Natts_pg_statistic_ext];
598  ObjectAddress address;
599  int newtarget = stmt->stxstattarget;
600 
601  /* Limit statistics target to a sane range */
602  if (newtarget < -1)
603  {
604  ereport(ERROR,
605  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
606  errmsg("statistics target %d is too low",
607  newtarget)));
608  }
609  else if (newtarget > 10000)
610  {
611  newtarget = 10000;
613  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
614  errmsg("lowering statistics target to %d",
615  newtarget)));
616  }
617 
618  /* lookup OID of the statistics object */
619  stxoid = get_statistics_object_oid(stmt->defnames, stmt->missing_ok);
620 
621  /*
622  * If we got here and the OID is not valid, it means the statistics does
623  * not exist, but the command specified IF EXISTS. So report this as a
624  * simple NOTICE and we're done.
625  */
626  if (!OidIsValid(stxoid))
627  {
628  char *schemaname;
629  char *statname;
630 
631  Assert(stmt->missing_ok);
632 
633  DeconstructQualifiedName(stmt->defnames, &schemaname, &statname);
634 
635  if (schemaname)
636  ereport(NOTICE,
637  (errmsg("statistics object \"%s.%s\" does not exist, skipping",
638  schemaname, statname)));
639  else
640  ereport(NOTICE,
641  (errmsg("statistics object \"%s\" does not exist, skipping",
642  statname)));
643 
644  return InvalidObjectAddress;
645  }
646 
647  /* Search pg_statistic_ext */
648  rel = table_open(StatisticExtRelationId, RowExclusiveLock);
649 
650  oldtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxoid));
651 
652  /* Must be owner of the existing statistics object */
655  NameListToString(stmt->defnames));
656 
657  /* Build new tuple. */
658  memset(repl_val, 0, sizeof(repl_val));
659  memset(repl_null, false, sizeof(repl_null));
660  memset(repl_repl, false, sizeof(repl_repl));
661 
662  /* replace the stxstattarget column */
663  repl_repl[Anum_pg_statistic_ext_stxstattarget - 1] = true;
664  repl_val[Anum_pg_statistic_ext_stxstattarget - 1] = Int32GetDatum(newtarget);
665 
666  newtup = heap_modify_tuple(oldtup, RelationGetDescr(rel),
667  repl_val, repl_null, repl_repl);
668 
669  /* Update system catalog. */
670  CatalogTupleUpdate(rel, &newtup->t_self, newtup);
671 
672  InvokeObjectPostAlterHook(StatisticExtRelationId, stxoid, 0);
673 
674  ObjectAddressSet(address, StatisticExtRelationId, stxoid);
675 
676  /*
677  * NOTE: because we only support altering the statistics target, not the
678  * other fields, there is no need to update dependencies.
679  */
680 
681  heap_freetuple(newtup);
682  ReleaseSysCache(oldtup);
683 
685 
686  return address;
687 }
688 
689 /*
690  * Guts of statistics object deletion.
691  */
692 void
694 {
695  Relation relation;
696  HeapTuple tup;
697  Form_pg_statistic_ext statext;
698  Oid relid;
699 
700  /*
701  * First delete the pg_statistic_ext_data tuple holding the actual
702  * statistical data.
703  */
704  relation = table_open(StatisticExtDataRelationId, RowExclusiveLock);
705 
707 
708  if (!HeapTupleIsValid(tup)) /* should not happen */
709  elog(ERROR, "cache lookup failed for statistics data %u", statsOid);
710 
711  CatalogTupleDelete(relation, &tup->t_self);
712 
713  ReleaseSysCache(tup);
714 
715  table_close(relation, RowExclusiveLock);
716 
717  /*
718  * Delete the pg_statistic_ext tuple. Also send out a cache inval on the
719  * associated table, so that dependent plans will be rebuilt.
720  */
721  relation = table_open(StatisticExtRelationId, RowExclusiveLock);
722 
723  tup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statsOid));
724 
725  if (!HeapTupleIsValid(tup)) /* should not happen */
726  elog(ERROR, "cache lookup failed for statistics object %u", statsOid);
727 
728  statext = (Form_pg_statistic_ext) GETSTRUCT(tup);
729  relid = statext->stxrelid;
730 
732 
733  CatalogTupleDelete(relation, &tup->t_self);
734 
735  ReleaseSysCache(tup);
736 
737  table_close(relation, RowExclusiveLock);
738 }
739 
740 /*
741  * Select a nonconflicting name for a new statistics.
742  *
743  * name1, name2, and label are used the same way as for makeObjectName(),
744  * except that the label can't be NULL; digits will be appended to the label
745  * if needed to create a name that is unique within the specified namespace.
746  *
747  * Returns a palloc'd string.
748  *
749  * Note: it is theoretically possible to get a collision anyway, if someone
750  * else chooses the same name concurrently. This is fairly unlikely to be
751  * a problem in practice, especially if one is holding a share update
752  * exclusive lock on the relation identified by name1. However, if choosing
753  * multiple names within a single command, you'd better create the new object
754  * and do CommandCounterIncrement before choosing the next one!
755  */
756 static char *
757 ChooseExtendedStatisticName(const char *name1, const char *name2,
758  const char *label, Oid namespaceid)
759 {
760  int pass = 0;
761  char *stxname = NULL;
762  char modlabel[NAMEDATALEN];
763 
764  /* try the unmodified label first */
765  strlcpy(modlabel, label, sizeof(modlabel));
766 
767  for (;;)
768  {
769  Oid existingstats;
770 
771  stxname = makeObjectName(name1, name2, modlabel);
772 
773  existingstats = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
774  PointerGetDatum(stxname),
775  ObjectIdGetDatum(namespaceid));
776  if (!OidIsValid(existingstats))
777  break;
778 
779  /* found a conflict, so try a new name component */
780  pfree(stxname);
781  snprintf(modlabel, sizeof(modlabel), "%s%d", label, ++pass);
782  }
783 
784  return stxname;
785 }
786 
787 /*
788  * Generate "name2" for a new statistics given the list of column names for it
789  * This will be passed to ChooseExtendedStatisticName along with the parent
790  * table name and a suitable label.
791  *
792  * We know that less than NAMEDATALEN characters will actually be used,
793  * so we can truncate the result once we've generated that many.
794  *
795  * XXX see also ChooseForeignKeyConstraintNameAddition and
796  * ChooseIndexNameAddition.
797  */
798 static char *
800 {
801  char buf[NAMEDATALEN * 2];
802  int buflen = 0;
803  ListCell *lc;
804 
805  buf[0] = '\0';
806  foreach(lc, exprs)
807  {
808  StatsElem *selem = (StatsElem *) lfirst(lc);
809  const char *name;
810 
811  /* It should be one of these, but just skip if it happens not to be */
812  if (!IsA(selem, StatsElem))
813  continue;
814 
815  name = selem->name;
816 
817  if (buflen > 0)
818  buf[buflen++] = '_'; /* insert _ between names */
819 
820  /*
821  * We use fixed 'expr' for expressions, which have empty column names.
822  * For indexes this is handled in ChooseIndexColumnNames, but we have
823  * no such function for stats and it does not seem worth adding. If a
824  * better name is needed, the user can specify it explicitly.
825  */
826  if (!name)
827  name = "expr";
828 
829  /*
830  * At this point we have buflen <= NAMEDATALEN. name should be less
831  * than NAMEDATALEN already, but use strlcpy for paranoia.
832  */
833  strlcpy(buf + buflen, name, NAMEDATALEN);
834  buflen += strlen(buf + buflen);
835  if (buflen >= NAMEDATALEN)
836  break;
837  }
838  return pstrdup(buf);
839 }
840 
841 /*
842  * StatisticsGetRelation: given a statistics's relation OID, get the OID of
843  * the relation it is an statistics on. Uses the system cache.
844  */
845 Oid
846 StatisticsGetRelation(Oid statId, bool missing_ok)
847 {
848  HeapTuple tuple;
850  Oid result;
851 
852  tuple = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statId));
853  if (!HeapTupleIsValid(tuple))
854  {
855  if (missing_ok)
856  return InvalidOid;
857  elog(ERROR, "cache lookup failed for statistics object %u", statId);
858  }
859  stx = (Form_pg_statistic_ext) GETSTRUCT(tuple);
860  Assert(stx->oid == statId);
861 
862  result = stx->stxrelid;
863  ReleaseSysCache(tuple);
864  return result;
865 }
signed short int16
Definition: c.h:428
#define NIL
Definition: pg_list.h:65
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:322
void CreateComments(Oid oid, Oid classoid, int32 subid, const char *comment)
Definition: comment.c:142
#define IsA(nodeptr, _type_)
Definition: nodes.h:590
#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:757
#define GETSTRUCT(TUP)
Definition: htup_details.h:654
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:151
void RemoveStatisticsById(Oid statsOid)
Definition: statscmds.c:693
bool IsSystemRelation(Relation relation)
Definition: catalog.c:73
char * name
Definition: parsenodes.h:2918
struct typedefs * types
Definition: ecpg.c:29
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:3113
#define RelationGetDescr(relation)
Definition: rel.h:495
Oid GetUserId(void)
Definition: miscinit.c:478
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid QualifiedNameGetCreationNamespace(List *names, char **objname_p)
Definition: namespace.c:2994
#define PointerGetDatum(X)
Definition: postgres.h:600
#define StatisticExtOidIndexId
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]
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3318
Definition: nodes.h:539
#define strVal(v)
Definition: value.h:54
int errcode(int sqlerrcode)
Definition: elog.c:698
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:846
char * format_type_be(Oid type_oid)
Definition: format_type.c:339
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:43
void DeconstructQualifiedName(List *names, char **nspname_p, char **objname_p)
Definition: namespace.c:2808
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:165
#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
#define OidIsValid(objectId)
Definition: c.h:710
char * makeObjectName(const char *name1, const char *name2, const char *label)
Definition: indexcmds.c:2208
#define NAMEDATALEN
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:3313
ObjectAddress AlterStatistics(AlterStatsStmt *stmt)
Definition: statscmds.c:589
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:2919
ItemPointerData t_self
Definition: htup.h:65
Oid get_statistics_object_oid(List *names, bool missing_ok)
Definition: namespace.c:2177
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:1338
#define RelationGetRelationName(relation)
Definition: rel.h:503
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:203
static int compare_int16(const void *a, const void *b)
Definition: statscmds.c:51
#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:3101
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:338
#define InvalidOid
Definition: postgres_ext.h:36
#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
Definition: value.h:42
bool pg_class_ownercheck(Oid class_oid, Oid roleid)
Definition: aclchk.c:4823
#define GetSysCacheOid2(cacheId, oidcol, key1, key2)
Definition: syscache.h:195
static char * ChooseExtendedStatisticNameAddition(List *exprs)
Definition: statscmds.c:799
void recordDependencyOnSingleRelExpr(const ObjectAddress *depender, Node *expr, Oid relId, DependencyType behavior, DependencyType self_behavior, bool reverse_self)
Definition: dependency.c:1621
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:515
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
#define CharGetDatum(X)
Definition: postgres.h:460
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1279
static Datum values[MAXATTR]
Definition: bootstrap.c:166
#define Int32GetDatum(X)
Definition: postgres.h:523
#define STATS_MAX_DIMENSIONS
Definition: statistics.h:19
const ObjectAddress InvalidObjectAddress
int errmsg(const char *fmt,...)
Definition: elog.c:909
#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:82
char * nodeToString(const void *obj)
Definition: outfuncs.c:4527
#define qsort(a, b, c, d)
Definition: port.h:504
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:216
#define RelationGetRelid(relation)
Definition: rel.h:469
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
FormData_pg_statistic_ext * Form_pg_statistic_ext
#define RelationGetNamespace(relation)
Definition: rel.h:510
bool pg_statistics_object_ownercheck(Oid stat_oid, Oid roleid)
Definition: aclchk.c:5413
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:64