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