PostgreSQL Source Code: src/backend/commands/statscmds.c Source File

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

AltStyle によって変換されたページ (->オリジナル) /