PostgreSQL Source Code git master
tablecmds.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
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/tablecmds.c
12 *
13 *-------------------------------------------------------------------------
14 */
15#include "postgres.h"
16
17#include "access/attmap.h"
18#include "access/genam.h"
19#include "access/gist.h"
20#include "access/heapam.h"
21#include "access/heapam_xlog.h"
22#include "access/multixact.h"
23#include "access/reloptions.h"
24#include "access/relscan.h"
25#include "access/sysattr.h"
26#include "access/tableam.h"
28#include "access/xact.h"
29#include "access/xlog.h"
30#include "access/xloginsert.h"
31#include "catalog/catalog.h"
32#include "catalog/heap.h"
33#include "catalog/index.h"
34#include "catalog/namespace.h"
36#include "catalog/partition.h"
37#include "catalog/pg_am.h"
38#include "catalog/pg_attrdef.h"
41#include "catalog/pg_depend.h"
43#include "catalog/pg_inherits.h"
46#include "catalog/pg_opclass.h"
47#include "catalog/pg_policy.h"
48#include "catalog/pg_proc.h"
50#include "catalog/pg_rewrite.h"
53#include "catalog/pg_trigger.h"
54#include "catalog/pg_type.h"
55#include "catalog/storage.h"
57#include "catalog/toasting.h"
58#include "commands/cluster.h"
59#include "commands/comment.h"
60#include "commands/defrem.h"
62#include "commands/sequence.h"
63#include "commands/tablecmds.h"
64#include "commands/tablespace.h"
65#include "commands/trigger.h"
66#include "commands/typecmds.h"
67#include "commands/user.h"
68#include "commands/vacuum.h"
69#include "common/int.h"
70#include "executor/executor.h"
71#include "foreign/fdwapi.h"
72#include "foreign/foreign.h"
73#include "miscadmin.h"
74#include "nodes/makefuncs.h"
75#include "nodes/nodeFuncs.h"
76#include "nodes/parsenodes.h"
77#include "optimizer/optimizer.h"
78#include "parser/parse_coerce.h"
80#include "parser/parse_expr.h"
82#include "parser/parse_type.h"
84#include "parser/parser.h"
87#include "pgstat.h"
91#include "storage/bufmgr.h"
92#include "storage/lmgr.h"
93#include "storage/lock.h"
94#include "storage/predicate.h"
95#include "storage/smgr.h"
96#include "tcop/utility.h"
97#include "utils/acl.h"
98#include "utils/builtins.h"
99#include "utils/fmgroids.h"
100#include "utils/inval.h"
101#include "utils/lsyscache.h"
102#include "utils/memutils.h"
103#include "utils/partcache.h"
104#include "utils/relcache.h"
105#include "utils/ruleutils.h"
106#include "utils/snapmgr.h"
107#include "utils/syscache.h"
108#include "utils/timestamp.h"
109#include "utils/typcache.h"
110#include "utils/usercontext.h"
111
112/*
113 * ON COMMIT action list
114 */
115typedef struct OnCommitItem
116{
117 Oid relid; /* relid of relation */
118 OnCommitAction oncommit; /* what to do at end of xact */
119
120 /*
121 * If this entry was created during the current transaction,
122 * creating_subid is the ID of the creating subxact; if created in a prior
123 * transaction, creating_subid is zero. If deleted during the current
124 * transaction, deleting_subid is the ID of the deleting subxact; if no
125 * deletion request is pending, deleting_subid is zero.
126 */
130
132
133
134/*
135 * State information for ALTER TABLE
136 *
137 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
138 * structs, one for each table modified by the operation (the named table
139 * plus any child tables that are affected). We save lists of subcommands
140 * to apply to this table (possibly modified by parse transformation steps);
141 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
142 * necessary information is stored in the constraints and newvals lists.
143 *
144 * Phase 2 is divided into multiple passes; subcommands are executed in
145 * a pass determined by subcommand type.
146 */
147
148typedef enum AlterTablePass
149{
150 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
151 AT_PASS_DROP, /* DROP (all flavors) */
152 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
153 AT_PASS_ADD_COL, /* ADD COLUMN */
154 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
155 AT_PASS_OLD_INDEX, /* re-add existing indexes */
156 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
157 /* We could support a RENAME COLUMN pass here, but not currently used */
158 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
159 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
160 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
161 AT_PASS_ADD_INDEX, /* ADD indexes */
162 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
163 AT_PASS_MISC, /* other stuff */
165
166#define AT_NUM_PASSES (AT_PASS_MISC + 1)
167
168typedef struct AlteredTableInfo
169{
170 /* Information saved before any work commences: */
171 Oid relid; /* Relation to work on */
172 char relkind; /* Its relkind */
173 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174
175 /*
176 * Transiently set during Phase 2, normally set to NULL.
177 *
178 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
179 * returns control. This can be exploited by ATExecCmd subroutines to
180 * close/reopen across transaction boundaries.
181 */
183
184 /* Information saved by Phase 1 for Phase 2: */
185 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
186 /* Information saved by Phases 1/2 for Phase 3: */
187 List *constraints; /* List of NewConstraint */
188 List *newvals; /* List of NewColumnValue */
189 List *afterStmts; /* List of utility command parsetrees */
190 bool verify_new_notnull; /* T if we should recheck NOT NULL */
191 int rewrite; /* Reason for forced rewrite, if any */
192 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
193 Oid newAccessMethod; /* new access method; 0 means no change,
194 * if above is true */
195 Oid newTableSpace; /* new tablespace; 0 means no change */
196 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
197 char newrelpersistence; /* if above is true */
198 Expr *partition_constraint; /* for attach partition validation */
199 /* true, if validating default due to some other attach/detach */
201 /* Objects to rebuild after completing ALTER TYPE operations */
202 List *changedConstraintOids; /* OIDs of constraints to rebuild */
203 List *changedConstraintDefs; /* string definitions of same */
204 List *changedIndexOids; /* OIDs of indexes to rebuild */
205 List *changedIndexDefs; /* string definitions of same */
206 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
207 char *clusterOnIndex; /* index to use for CLUSTER */
208 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
209 List *changedStatisticsDefs; /* string definitions of same */
211
212/* Struct describing one new constraint to check in Phase 3 scan */
213/* Note: new not-null constraints are handled elsewhere */
214typedef struct NewConstraint
215{
216 char *name; /* Constraint name, or NULL if none */
217 ConstrType contype; /* CHECK or FOREIGN */
218 Oid refrelid; /* PK rel, if FOREIGN */
219 Oid refindid; /* OID of PK's index, if FOREIGN */
220 bool conwithperiod; /* Whether the new FOREIGN KEY uses PERIOD */
221 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
222 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
223 ExprState *qualstate; /* Execution state for CHECK expr */
225
226/*
227 * Struct describing one new column value that needs to be computed during
228 * Phase 3 copy (this could be either a new column with a non-null default, or
229 * a column that we're changing the type of). Columns without such an entry
230 * are just copied from the old table during ATRewriteTable. Note that the
231 * expr is an expression over *old* table values, except when is_generated
232 * is true; then it is an expression over columns of the *new* tuple.
233 */
234typedef struct NewColumnValue
235{
236 AttrNumber attnum; /* which column */
237 Expr *expr; /* expression to compute */
238 ExprState *exprstate; /* execution state */
239 bool is_generated; /* is it a GENERATED expression? */
241
242/*
243 * Error-reporting support for RemoveRelations
244 */
246{
247 char kind;
249 const char *nonexistent_msg;
250 const char *skipping_msg;
251 const char *nota_msg;
252 const char *drophint_msg;
253};
254
255static const struct dropmsgstrings dropmsgstringarray[] = {
256 {RELKIND_RELATION,
258 gettext_noop("table \"%s\" does not exist"),
259 gettext_noop("table \"%s\" does not exist, skipping"),
260 gettext_noop("\"%s\" is not a table"),
261 gettext_noop("Use DROP TABLE to remove a table.")},
262 {RELKIND_SEQUENCE,
264 gettext_noop("sequence \"%s\" does not exist"),
265 gettext_noop("sequence \"%s\" does not exist, skipping"),
266 gettext_noop("\"%s\" is not a sequence"),
267 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
268 {RELKIND_VIEW,
270 gettext_noop("view \"%s\" does not exist"),
271 gettext_noop("view \"%s\" does not exist, skipping"),
272 gettext_noop("\"%s\" is not a view"),
273 gettext_noop("Use DROP VIEW to remove a view.")},
274 {RELKIND_MATVIEW,
276 gettext_noop("materialized view \"%s\" does not exist"),
277 gettext_noop("materialized view \"%s\" does not exist, skipping"),
278 gettext_noop("\"%s\" is not a materialized view"),
279 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
280 {RELKIND_INDEX,
281 ERRCODE_UNDEFINED_OBJECT,
282 gettext_noop("index \"%s\" does not exist"),
283 gettext_noop("index \"%s\" does not exist, skipping"),
284 gettext_noop("\"%s\" is not an index"),
285 gettext_noop("Use DROP INDEX to remove an index.")},
286 {RELKIND_COMPOSITE_TYPE,
287 ERRCODE_UNDEFINED_OBJECT,
288 gettext_noop("type \"%s\" does not exist"),
289 gettext_noop("type \"%s\" does not exist, skipping"),
290 gettext_noop("\"%s\" is not a type"),
291 gettext_noop("Use DROP TYPE to remove a type.")},
292 {RELKIND_FOREIGN_TABLE,
293 ERRCODE_UNDEFINED_OBJECT,
294 gettext_noop("foreign table \"%s\" does not exist"),
295 gettext_noop("foreign table \"%s\" does not exist, skipping"),
296 gettext_noop("\"%s\" is not a foreign table"),
297 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
298 {RELKIND_PARTITIONED_TABLE,
300 gettext_noop("table \"%s\" does not exist"),
301 gettext_noop("table \"%s\" does not exist, skipping"),
302 gettext_noop("\"%s\" is not a table"),
303 gettext_noop("Use DROP TABLE to remove a table.")},
304 {RELKIND_PARTITIONED_INDEX,
305 ERRCODE_UNDEFINED_OBJECT,
306 gettext_noop("index \"%s\" does not exist"),
307 gettext_noop("index \"%s\" does not exist, skipping"),
308 gettext_noop("\"%s\" is not an index"),
309 gettext_noop("Use DROP INDEX to remove an index.")},
310 {'\0', 0, NULL, NULL, NULL, NULL}
311};
312
313/* communication between RemoveRelations and RangeVarCallbackForDropRelation */
315{
316 /* These fields are set by RemoveRelations: */
319 /* These fields are state to track which subsidiary locks are held: */
322 /* These fields are passed back by RangeVarCallbackForDropRelation: */
325};
326
327/* Alter table target-type flags for ATSimplePermissions */
328#define ATT_TABLE 0x0001
329#define ATT_VIEW 0x0002
330#define ATT_MATVIEW 0x0004
331#define ATT_INDEX 0x0008
332#define ATT_COMPOSITE_TYPE 0x0010
333#define ATT_FOREIGN_TABLE 0x0020
334#define ATT_PARTITIONED_INDEX 0x0040
335#define ATT_SEQUENCE 0x0080
336#define ATT_PARTITIONED_TABLE 0x0100
337
338/*
339 * ForeignTruncateInfo
340 *
341 * Information related to truncation of foreign tables. This is used for
342 * the elements in a hash table. It uses the server OID as lookup key,
343 * and includes a per-server list of all foreign tables involved in the
344 * truncation.
345 */
347{
351
352/* Partial or complete FK creation in addFkConstraint() */
354{
359
360/*
361 * Partition tables are expected to be dropped when the parent partitioned
362 * table gets dropped. Hence for partitioning we use AUTO dependency.
363 * Otherwise, for regular inheritance use NORMAL dependency.
364 */
365#define child_dependency_type(child_is_partition) \
366 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
367
368static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370static void truncate_check_activity(Relation rel);
371static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 Oid relId, Oid oldRelId, void *arg);
373static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 bool is_partition, List **supconstr,
375 List **supnotnulls);
376static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381static void StoreCatalogInheritance(Oid relationId, List *supers,
382 bool child_is_partition);
383static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 int32 seqNumber, Relation inhRelation,
385 bool child_is_partition);
386static int findAttrByName(const char *attributeName, const List *columns);
387static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 LOCKMODE lockmode);
393 ATAlterConstraint *cmdcon,
394 bool recurse, LOCKMODE lockmode);
395static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel,
396 Relation tgrel, Relation rel, HeapTuple contuple,
397 bool recurse, LOCKMODE lockmode);
398static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon,
399 Relation conrel, Relation tgrel,
400 Oid fkrelid, Oid pkrelid,
401 HeapTuple contuple, LOCKMODE lockmode,
402 Oid ReferencedParentDelTrigger,
403 Oid ReferencedParentUpdTrigger,
404 Oid ReferencingParentInsTrigger,
405 Oid ReferencingParentUpdTrigger);
406static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon,
407 Relation conrel, Relation tgrel, Relation rel,
408 HeapTuple contuple, bool recurse,
409 List **otherrelids, LOCKMODE lockmode);
410static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon,
411 Relation conrel, Relation rel,
412 HeapTuple contuple, LOCKMODE lockmode);
413static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel,
414 bool deferrable, bool initdeferred,
415 List **otherrelids);
416static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
417 Relation conrel, Relation tgrel,
418 Oid fkrelid, Oid pkrelid,
419 HeapTuple contuple, LOCKMODE lockmode,
420 Oid ReferencedParentDelTrigger,
421 Oid ReferencedParentUpdTrigger,
422 Oid ReferencingParentInsTrigger,
423 Oid ReferencingParentUpdTrigger);
424static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon,
425 Relation conrel, Relation tgrel, Relation rel,
426 HeapTuple contuple, bool recurse,
427 List **otherrelids, LOCKMODE lockmode);
429 HeapTuple contuple);
431 Relation rel, char *constrName,
432 bool recurse, bool recursing, LOCKMODE lockmode);
433static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel,
434 HeapTuple contuple, LOCKMODE lockmode);
435static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel,
436 char *constrName, HeapTuple contuple,
437 bool recurse, bool recursing, LOCKMODE lockmode);
438static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel,
439 HeapTuple contuple, bool recurse, bool recursing,
440 LOCKMODE lockmode);
441static int transformColumnNameList(Oid relId, List *colList,
442 int16 *attnums, Oid *atttypids, Oid *attcollids);
443static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
444 List **attnamelist,
445 int16 *attnums, Oid *atttypids, Oid *attcollids,
446 Oid *opclasses, bool *pk_has_without_overlaps);
448 int numattrs, int16 *attnums,
449 bool with_period, Oid *opclasses,
450 bool *pk_has_without_overlaps);
451static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
452static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
453 Oid *funcid);
454static void validateForeignKeyConstraint(char *conname,
455 Relation rel, Relation pkrel,
456 Oid pkindOid, Oid constraintOid, bool hasperiod);
457static void CheckAlterTableIsSafe(Relation rel);
458static void ATController(AlterTableStmt *parsetree,
459 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
460 AlterTableUtilityContext *context);
461static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
462 bool recurse, bool recursing, LOCKMODE lockmode,
463 AlterTableUtilityContext *context);
464static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
465 AlterTableUtilityContext *context);
466static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
467 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
468 AlterTableUtilityContext *context);
470 Relation rel, AlterTableCmd *cmd,
471 bool recurse, LOCKMODE lockmode,
472 AlterTablePass cur_pass,
473 AlterTableUtilityContext *context);
474static void ATRewriteTables(AlterTableStmt *parsetree,
475 List **wqueue, LOCKMODE lockmode,
476 AlterTableUtilityContext *context);
477static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
478static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
479static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
480static void ATSimpleRecursion(List **wqueue, Relation rel,
481 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
482 AlterTableUtilityContext *context);
483static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
484static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
485 LOCKMODE lockmode,
486 AlterTableUtilityContext *context);
487static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
488 DropBehavior behavior);
489static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
490 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
491 AlterTableUtilityContext *context);
493 Relation rel, AlterTableCmd **cmd,
494 bool recurse, bool recursing,
495 LOCKMODE lockmode, AlterTablePass cur_pass,
496 AlterTableUtilityContext *context);
497static bool check_for_column_name_collision(Relation rel, const char *colname,
498 bool if_not_exists);
499static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
501static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
502 LOCKMODE lockmode);
503static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
504 bool is_valid, bool queue_validation);
505static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
506 char *conName, char *colName,
507 bool recurse, bool recursing,
508 LOCKMODE lockmode);
511 List *testConstraint, List *provenConstraint);
512static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
513 Node *newDefault, LOCKMODE lockmode);
515 Node *newDefault);
516static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
517 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
518static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
519 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
520static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
521 bool recurse, bool recursing);
522static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
523 Node *newExpr, LOCKMODE lockmode);
524static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
525static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
526static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
527 Node *newValue, LOCKMODE lockmode);
528static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
529 Node *options, bool isReset, LOCKMODE lockmode);
530static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
531 Node *newValue, LOCKMODE lockmode);
532static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
533 AlterTableCmd *cmd, LOCKMODE lockmode,
534 AlterTableUtilityContext *context);
535static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
536 DropBehavior behavior,
537 bool recurse, bool recursing,
538 bool missing_ok, LOCKMODE lockmode,
539 ObjectAddresses *addrs);
540static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
541 bool recurse, LOCKMODE lockmode,
542 AlterTableUtilityContext *context);
543static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname);
545 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
547 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
549 AlteredTableInfo *tab, Relation rel,
550 Constraint *newConstraint, bool recurse, bool is_readd,
551 LOCKMODE lockmode);
552static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
554 IndexStmt *stmt, LOCKMODE lockmode);
556 AlteredTableInfo *tab, Relation rel,
557 Constraint *constr,
558 bool recurse, bool recursing, bool is_readd,
559 LOCKMODE lockmode);
561 Relation rel, Constraint *fkconstraint,
562 bool recurse, bool recursing,
563 LOCKMODE lockmode);
564static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
565 int numfksetcols, int16 *fksetcolsattnums,
566 List *fksetcols);
568 char *constraintname,
569 Constraint *fkconstraint, Relation rel,
570 Relation pkrel, Oid indexOid,
571 Oid parentConstr,
572 int numfks, int16 *pkattnum, int16 *fkattnum,
573 Oid *pfeqoperators, Oid *ppeqoperators,
574 Oid *ffeqoperators, int numfkdelsetcols,
575 int16 *fkdelsetcols, bool is_internal,
576 bool with_period);
577static void addFkRecurseReferenced(Constraint *fkconstraint,
578 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
579 int numfks, int16 *pkattnum, int16 *fkattnum,
580 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
581 int numfkdelsetcols, int16 *fkdelsetcols,
582 bool old_check_ok,
583 Oid parentDelTrigger, Oid parentUpdTrigger,
584 bool with_period);
585static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
586 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
587 int numfks, int16 *pkattnum, int16 *fkattnum,
588 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
589 int numfkdelsetcols, int16 *fkdelsetcols,
590 bool old_check_ok, LOCKMODE lockmode,
591 Oid parentInsTrigger, Oid parentUpdTrigger,
592 bool with_period);
593static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
594 Relation partitionRel);
595static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
596static void CloneFkReferencing(List **wqueue, Relation parentRel,
597 Relation partRel);
598static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
599 Constraint *fkconstraint, Oid constraintOid,
600 Oid indexOid,
601 Oid parentInsTrigger, Oid parentUpdTrigger,
602 Oid *insertTrigOid, Oid *updateTrigOid);
603static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid,
604 Constraint *fkconstraint, Oid constraintOid,
605 Oid indexOid,
606 Oid parentDelTrigger, Oid parentUpdTrigger,
607 Oid *deleteTrigOid, Oid *updateTrigOid);
608static bool tryAttachPartitionForeignKey(List **wqueue,
610 Relation partition,
611 Oid parentConstrOid, int numfks,
612 AttrNumber *mapped_conkey, AttrNumber *confkey,
613 Oid *conpfeqop,
614 Oid parentInsTrigger,
615 Oid parentUpdTrigger,
616 Relation trigrel);
617static void AttachPartitionForeignKey(List **wqueue, Relation partition,
618 Oid partConstrOid, Oid parentConstrOid,
619 Oid parentInsTrigger, Oid parentUpdTrigger,
620 Relation trigrel);
621static void RemoveInheritedConstraint(Relation conrel, Relation trigrel,
622 Oid conoid, Oid conrelid);
623static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid,
624 Oid confrelid, Oid conrelid);
625static void GetForeignKeyActionTriggers(Relation trigrel,
626 Oid conoid, Oid confrelid, Oid conrelid,
627 Oid *deleteTriggerOid,
628 Oid *updateTriggerOid);
629static void GetForeignKeyCheckTriggers(Relation trigrel,
630 Oid conoid, Oid confrelid, Oid conrelid,
631 Oid *insertTriggerOid,
632 Oid *updateTriggerOid);
633static void ATExecDropConstraint(Relation rel, const char *constrName,
634 DropBehavior behavior, bool recurse,
635 bool missing_ok, LOCKMODE lockmode);
637 HeapTuple constraintTup, DropBehavior behavior,
638 bool recurse, bool recursing,
639 bool missing_ok, LOCKMODE lockmode);
640static void ATPrepAlterColumnType(List **wqueue,
641 AlteredTableInfo *tab, Relation rel,
642 bool recurse, bool recursing,
643 AlterTableCmd *cmd, LOCKMODE lockmode,
644 AlterTableUtilityContext *context);
645static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
647 AlterTableCmd *cmd, LOCKMODE lockmode);
649 Relation rel, AttrNumber attnum, const char *colName);
651static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
653static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
654 LOCKMODE lockmode);
655static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
656 char *cmd, List **wqueue, LOCKMODE lockmode,
657 bool rewrite);
659 Oid objid, Relation rel, List *domname,
660 const char *conname);
661static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
662static void TryReuseForeignKey(Oid oldId, Constraint *con);
663static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
664 List *options, LOCKMODE lockmode);
665static void change_owner_fix_column_acls(Oid relationOid,
666 Oid oldOwnerId, Oid newOwnerId);
667static void change_owner_recurse_to_sequences(Oid relationOid,
668 Oid newOwnerId, LOCKMODE lockmode);
669static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
670 LOCKMODE lockmode);
671static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
672static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
673static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
675 bool toLogged);
676static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
677 const char *tablespacename, LOCKMODE lockmode);
678static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
679static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
680static void ATExecSetRelOptions(Relation rel, List *defList,
681 AlterTableType operation,
682 LOCKMODE lockmode);
683static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
684 char fires_when, bool skip_system, bool recurse,
685 LOCKMODE lockmode);
686static void ATExecEnableDisableRule(Relation rel, const char *rulename,
687 char fires_when, LOCKMODE lockmode);
688static void ATPrepAddInherit(Relation child_rel);
689static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
690static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
691static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
692 DependencyType deptype);
693static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
694static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
696static void ATExecGenericOptions(Relation rel, List *options);
697static void ATExecSetRowSecurity(Relation rel, bool rls);
698static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
700 const char *column, Node *newValue, LOCKMODE lockmode);
701
702static void index_copy_data(Relation rel, RelFileLocator newrlocator);
703static const char *storage_name(char c);
704
705static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
706 Oid oldRelOid, void *arg);
707static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
708 Oid oldrelid, void *arg);
710static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
711 List **partexprs, Oid *partopclass, Oid *partcollation,
712 PartitionStrategy strategy);
713static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
714static void RemoveInheritance(Relation child_rel, Relation parent_rel,
715 bool expect_detached);
717 PartitionCmd *cmd,
718 AlterTableUtilityContext *context);
719static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
720static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
721 List *partConstraint,
722 bool validate_default);
723static void CloneRowTriggersToPartition(Relation parent, Relation partition);
724static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
725static void DropClonedTriggersFromPartition(Oid partitionId);
727 Relation rel, RangeVar *name,
728 bool concurrent);
729static void DetachPartitionFinalize(Relation rel, Relation partRel,
730 bool concurrent, Oid defaultPartOid);
732static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
733 RangeVar *name);
734static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
735static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
736 Relation partitionTbl);
737static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition);
738static List *GetParentedForeignKeyRefs(Relation partition);
739static void ATDetachCheckNoForeignKeyRefs(Relation partition);
740static char GetAttributeCompression(Oid atttypid, const char *compression);
741static char GetAttributeStorage(Oid atttypid, const char *storagemode);
742
743
744/* ----------------------------------------------------------------
745 * DefineRelation
746 * Creates a new relation.
747 *
748 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
749 * The other arguments are used to extend the behavior for other cases:
750 * relkind: relkind to assign to the new relation
751 * ownerId: if not InvalidOid, use this as the new relation's owner.
752 * typaddress: if not null, it's set to the pg_type entry's address.
753 * queryString: for error reporting
754 *
755 * Note that permissions checks are done against current user regardless of
756 * ownerId. A nonzero ownerId is used when someone is creating a relation
757 * "on behalf of" someone else, so we still want to see that the current user
758 * has permissions to do it.
759 *
760 * If successful, returns the address of the new relation.
761 * ----------------------------------------------------------------
762 */
764DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
765 ObjectAddress *typaddress, const char *queryString)
766{
767 char relname[NAMEDATALEN];
768 Oid namespaceId;
769 Oid relationId;
770 Oid tablespaceId;
771 Relation rel;
773 List *inheritOids;
774 List *old_constraints;
775 List *old_notnulls;
776 List *rawDefaults;
777 List *cookedDefaults;
778 List *nncols;
779 Datum reloptions;
780 ListCell *listptr;
782 bool partitioned;
783 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
784 Oid ofTypeId;
785 ObjectAddress address;
786 LOCKMODE parentLockmode;
787 Oid accessMethodId = InvalidOid;
788
789 /*
790 * Truncate relname to appropriate length (probably a waste of time, as
791 * parser should have done this already).
792 */
793 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
794
795 /*
796 * Check consistency of arguments
797 */
798 if (stmt->oncommit != ONCOMMIT_NOOP
799 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
801 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
802 errmsg("ON COMMIT can only be used on temporary tables")));
803
804 if (stmt->partspec != NULL)
805 {
806 if (relkind != RELKIND_RELATION)
807 elog(ERROR, "unexpected relkind: %d", (int) relkind);
808
809 relkind = RELKIND_PARTITIONED_TABLE;
810 partitioned = true;
811 }
812 else
813 partitioned = false;
814
815 if (relkind == RELKIND_PARTITIONED_TABLE &&
816 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
818 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
819 errmsg("partitioned tables cannot be unlogged")));
820
821 /*
822 * Look up the namespace in which we are supposed to create the relation,
823 * check we have permission to create there, lock it against concurrent
824 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
825 * namespace is selected.
826 */
827 namespaceId =
829
830 /*
831 * Security check: disallow creating temp tables from security-restricted
832 * code. This is needed because calling code might not expect untrusted
833 * tables to appear in pg_temp at the front of its search path.
834 */
835 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
838 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
839 errmsg("cannot create temporary table within security-restricted operation")));
840
841 /*
842 * Determine the lockmode to use when scanning parents. A self-exclusive
843 * lock is needed here.
844 *
845 * For regular inheritance, if two backends attempt to add children to the
846 * same parent simultaneously, and that parent has no pre-existing
847 * children, then both will attempt to update the parent's relhassubclass
848 * field, leading to a "tuple concurrently updated" error. Also, this
849 * interlocks against a concurrent ANALYZE on the parent table, which
850 * might otherwise be attempting to clear the parent's relhassubclass
851 * field, if its previous children were recently dropped.
852 *
853 * If the child table is a partition, then we instead grab an exclusive
854 * lock on the parent because its partition descriptor will be changed by
855 * addition of the new partition.
856 */
857 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
859
860 /* Determine the list of OIDs of the parents. */
861 inheritOids = NIL;
862 foreach(listptr, stmt->inhRelations)
863 {
864 RangeVar *rv = (RangeVar *) lfirst(listptr);
865 Oid parentOid;
866
867 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
868
869 /*
870 * Reject duplications in the list of parents.
871 */
872 if (list_member_oid(inheritOids, parentOid))
874 (errcode(ERRCODE_DUPLICATE_TABLE),
875 errmsg("relation \"%s\" would be inherited from more than once",
876 get_rel_name(parentOid))));
877
878 inheritOids = lappend_oid(inheritOids, parentOid);
879 }
880
881 /*
882 * Select tablespace to use: an explicitly indicated one, or (in the case
883 * of a partitioned table) the parent's, if it has one.
884 */
885 if (stmt->tablespacename)
886 {
887 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
888
889 if (partitioned && tablespaceId == MyDatabaseTableSpace)
891 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
892 errmsg("cannot specify default tablespace for partitioned relations")));
893 }
894 else if (stmt->partbound)
895 {
896 Assert(list_length(inheritOids) == 1);
897 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
898 }
899 else
900 tablespaceId = InvalidOid;
901
902 /* still nothing? use the default */
903 if (!OidIsValid(tablespaceId))
904 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
905 partitioned);
906
907 /* Check permissions except when using database's default */
908 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
909 {
910 AclResult aclresult;
911
912 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
913 ACL_CREATE);
914 if (aclresult != ACLCHECK_OK)
916 get_tablespace_name(tablespaceId));
917 }
918
919 /* In all cases disallow placing user relations in pg_global */
920 if (tablespaceId == GLOBALTABLESPACE_OID)
922 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
923 errmsg("only shared relations can be placed in pg_global tablespace")));
924
925 /* Identify user ID that will own the table */
926 if (!OidIsValid(ownerId))
927 ownerId = GetUserId();
928
929 /*
930 * Parse and validate reloptions, if any.
931 */
932 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
933 true, false);
934
935 switch (relkind)
936 {
937 case RELKIND_VIEW:
938 (void) view_reloptions(reloptions, true);
939 break;
940 case RELKIND_PARTITIONED_TABLE:
941 (void) partitioned_table_reloptions(reloptions, true);
942 break;
943 default:
944 (void) heap_reloptions(relkind, reloptions, true);
945 }
946
947 if (stmt->ofTypename)
948 {
949 AclResult aclresult;
950
951 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
952
953 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
954 if (aclresult != ACLCHECK_OK)
955 aclcheck_error_type(aclresult, ofTypeId);
956 }
957 else
958 ofTypeId = InvalidOid;
959
960 /*
961 * Look up inheritance ancestors and generate relation schema, including
962 * inherited attributes. (Note that stmt->tableElts is destructively
963 * modified by MergeAttributes.)
964 */
965 stmt->tableElts =
966 MergeAttributes(stmt->tableElts, inheritOids,
967 stmt->relation->relpersistence,
968 stmt->partbound != NULL,
969 &old_constraints, &old_notnulls);
970
971 /*
972 * Create a tuple descriptor from the relation schema. Note that this
973 * deals with column names, types, and in-descriptor NOT NULL flags, but
974 * not default values, NOT NULL or CHECK constraints; we handle those
975 * below.
976 */
978
979 /*
980 * Find columns with default values and prepare for insertion of the
981 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
982 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
983 * while raw defaults go into a list of RawColumnDefault structs that will
984 * be processed by AddRelationNewConstraints. (We can't deal with raw
985 * expressions until we can do transformExpr.)
986 */
987 rawDefaults = NIL;
988 cookedDefaults = NIL;
989 attnum = 0;
990
991 foreach(listptr, stmt->tableElts)
992 {
993 ColumnDef *colDef = lfirst(listptr);
994
995 attnum++;
996 if (colDef->raw_default != NULL)
997 {
998 RawColumnDefault *rawEnt;
999
1000 Assert(colDef->cooked_default == NULL);
1001
1002 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
1003 rawEnt->attnum = attnum;
1004 rawEnt->raw_default = colDef->raw_default;
1005 rawEnt->generated = colDef->generated;
1006 rawDefaults = lappend(rawDefaults, rawEnt);
1007 }
1008 else if (colDef->cooked_default != NULL)
1009 {
1010 CookedConstraint *cooked;
1011
1012 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
1013 cooked->contype = CONSTR_DEFAULT;
1014 cooked->conoid = InvalidOid; /* until created */
1015 cooked->name = NULL;
1016 cooked->attnum = attnum;
1017 cooked->expr = colDef->cooked_default;
1018 cooked->is_enforced = true;
1019 cooked->skip_validation = false;
1020 cooked->is_local = true; /* not used for defaults */
1021 cooked->inhcount = 0; /* ditto */
1022 cooked->is_no_inherit = false;
1023 cookedDefaults = lappend(cookedDefaults, cooked);
1024 }
1025 }
1026
1027 /*
1028 * For relations with table AM and partitioned tables, select access
1029 * method to use: an explicitly indicated one, or (in the case of a
1030 * partitioned table) the parent's, if it has one.
1031 */
1032 if (stmt->accessMethod != NULL)
1033 {
1034 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
1035 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1036 }
1037 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1038 {
1039 if (stmt->partbound)
1040 {
1041 Assert(list_length(inheritOids) == 1);
1042 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1043 }
1044
1045 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1046 accessMethodId = get_table_am_oid(default_table_access_method, false);
1047 }
1048
1049 /*
1050 * Create the relation. Inherited defaults and CHECK constraints are
1051 * passed in for immediate handling --- since they don't need parsing,
1052 * they can be stored immediately.
1053 */
1054 relationId = heap_create_with_catalog(relname,
1055 namespaceId,
1056 tablespaceId,
1057 InvalidOid,
1058 InvalidOid,
1059 ofTypeId,
1060 ownerId,
1061 accessMethodId,
1062 descriptor,
1063 list_concat(cookedDefaults,
1064 old_constraints),
1065 relkind,
1066 stmt->relation->relpersistence,
1067 false,
1068 false,
1069 stmt->oncommit,
1070 reloptions,
1071 true,
1073 false,
1074 InvalidOid,
1075 typaddress);
1076
1077 /*
1078 * We must bump the command counter to make the newly-created relation
1079 * tuple visible for opening.
1080 */
1082
1083 /*
1084 * Open the new relation and acquire exclusive lock on it. This isn't
1085 * really necessary for locking out other backends (since they can't see
1086 * the new rel anyway until we commit), but it keeps the lock manager from
1087 * complaining about deadlock risks.
1088 */
1089 rel = relation_open(relationId, AccessExclusiveLock);
1090
1091 /*
1092 * Now add any newly specified column default and generation expressions
1093 * to the new relation. These are passed to us in the form of raw
1094 * parsetrees; we need to transform them to executable expression trees
1095 * before they can be added. The most convenient way to do that is to
1096 * apply the parser's transformExpr routine, but transformExpr doesn't
1097 * work unless we have a pre-existing relation. So, the transformation has
1098 * to be postponed to this final step of CREATE TABLE.
1099 *
1100 * This needs to be before processing the partitioning clauses because
1101 * those could refer to generated columns.
1102 */
1103 if (rawDefaults)
1104 AddRelationNewConstraints(rel, rawDefaults, NIL,
1105 true, true, false, queryString);
1106
1107 /*
1108 * Make column generation expressions visible for use by partitioning.
1109 */
1111
1112 /* Process and store partition bound, if any. */
1113 if (stmt->partbound)
1114 {
1115 PartitionBoundSpec *bound;
1116 ParseState *pstate;
1117 Oid parentId = linitial_oid(inheritOids),
1118 defaultPartOid;
1119 Relation parent,
1120 defaultRel = NULL;
1121 ParseNamespaceItem *nsitem;
1122
1123 /* Already have strong enough lock on the parent */
1124 parent = table_open(parentId, NoLock);
1125
1126 /*
1127 * We are going to try to validate the partition bound specification
1128 * against the partition key of parentRel, so it better have one.
1129 */
1130 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1131 ereport(ERROR,
1132 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1133 errmsg("\"%s\" is not partitioned",
1134 RelationGetRelationName(parent))));
1135
1136 /*
1137 * The partition constraint of the default partition depends on the
1138 * partition bounds of every other partition. It is possible that
1139 * another backend might be about to execute a query on the default
1140 * partition table, and that the query relies on previously cached
1141 * default partition constraints. We must therefore take a table lock
1142 * strong enough to prevent all queries on the default partition from
1143 * proceeding until we commit and send out a shared-cache-inval notice
1144 * that will make them update their index lists.
1145 *
1146 * Order of locking: The relation being added won't be visible to
1147 * other backends until it is committed, hence here in
1148 * DefineRelation() the order of locking the default partition and the
1149 * relation being added does not matter. But at all other places we
1150 * need to lock the default relation before we lock the relation being
1151 * added or removed i.e. we should take the lock in same order at all
1152 * the places such that lock parent, lock default partition and then
1153 * lock the partition so as to avoid a deadlock.
1154 */
1155 defaultPartOid =
1157 true));
1158 if (OidIsValid(defaultPartOid))
1159 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1160
1161 /* Transform the bound values */
1162 pstate = make_parsestate(NULL);
1163 pstate->p_sourcetext = queryString;
1164
1165 /*
1166 * Add an nsitem containing this relation, so that transformExpr
1167 * called on partition bound expressions is able to report errors
1168 * using a proper context.
1169 */
1170 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1171 NULL, false, false);
1172 addNSItemToQuery(pstate, nsitem, false, true, true);
1173
1174 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1175
1176 /*
1177 * Check first that the new partition's bound is valid and does not
1178 * overlap with any of existing partitions of the parent.
1179 */
1180 check_new_partition_bound(relname, parent, bound, pstate);
1181
1182 /*
1183 * If the default partition exists, its partition constraints will
1184 * change after the addition of this new partition such that it won't
1185 * allow any row that qualifies for this new partition. So, check that
1186 * the existing data in the default partition satisfies the constraint
1187 * as it will exist after adding this partition.
1188 */
1189 if (OidIsValid(defaultPartOid))
1190 {
1191 check_default_partition_contents(parent, defaultRel, bound);
1192 /* Keep the lock until commit. */
1193 table_close(defaultRel, NoLock);
1194 }
1195
1196 /* Update the pg_class entry. */
1197 StorePartitionBound(rel, parent, bound);
1198
1199 table_close(parent, NoLock);
1200 }
1201
1202 /* Store inheritance information for new rel. */
1203 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1204
1205 /*
1206 * Process the partitioning specification (if any) and store the partition
1207 * key information into the catalog.
1208 */
1209 if (partitioned)
1210 {
1211 ParseState *pstate;
1212 int partnatts;
1213 AttrNumber partattrs[PARTITION_MAX_KEYS];
1214 Oid partopclass[PARTITION_MAX_KEYS];
1215 Oid partcollation[PARTITION_MAX_KEYS];
1216 List *partexprs = NIL;
1217
1218 pstate = make_parsestate(NULL);
1219 pstate->p_sourcetext = queryString;
1220
1221 partnatts = list_length(stmt->partspec->partParams);
1222
1223 /* Protect fixed-size arrays here and in executor */
1224 if (partnatts > PARTITION_MAX_KEYS)
1225 ereport(ERROR,
1226 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1227 errmsg("cannot partition using more than %d columns",
1229
1230 /*
1231 * We need to transform the raw parsetrees corresponding to partition
1232 * expressions into executable expression trees. Like column defaults
1233 * and CHECK constraints, we could not have done the transformation
1234 * earlier.
1235 */
1236 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1237
1238 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1239 partattrs, &partexprs, partopclass,
1240 partcollation, stmt->partspec->strategy);
1241
1242 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1243 partexprs,
1244 partopclass, partcollation);
1245
1246 /* make it all visible */
1248 }
1249
1250 /*
1251 * If we're creating a partition, create now all the indexes, triggers,
1252 * FKs defined in the parent.
1253 *
1254 * We can't do it earlier, because DefineIndex wants to know the partition
1255 * key which we just stored.
1256 */
1257 if (stmt->partbound)
1258 {
1259 Oid parentId = linitial_oid(inheritOids);
1260 Relation parent;
1261 List *idxlist;
1262 ListCell *cell;
1263
1264 /* Already have strong enough lock on the parent */
1265 parent = table_open(parentId, NoLock);
1266 idxlist = RelationGetIndexList(parent);
1267
1268 /*
1269 * For each index in the parent table, create one in the partition
1270 */
1271 foreach(cell, idxlist)
1272 {
1274 AttrMap *attmap;
1275 IndexStmt *idxstmt;
1276 Oid constraintOid;
1277
1278 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1279 {
1280 if (idxRel->rd_index->indisunique)
1281 ereport(ERROR,
1282 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1283 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1284 RelationGetRelationName(parent)),
1285 errdetail("Table \"%s\" contains indexes that are unique.",
1286 RelationGetRelationName(parent))));
1287 else
1288 {
1290 continue;
1291 }
1292 }
1293
1295 RelationGetDescr(parent),
1296 false);
1297 idxstmt =
1298 generateClonedIndexStmt(NULL, idxRel,
1299 attmap, &constraintOid);
1301 idxstmt,
1302 InvalidOid,
1303 RelationGetRelid(idxRel),
1304 constraintOid,
1305 -1,
1306 false, false, false, false, false);
1307
1309 }
1310
1311 list_free(idxlist);
1312
1313 /*
1314 * If there are any row-level triggers, clone them to the new
1315 * partition.
1316 */
1317 if (parent->trigdesc != NULL)
1318 CloneRowTriggersToPartition(parent, rel);
1319
1320 /*
1321 * And foreign keys too. Note that because we're freshly creating the
1322 * table, there is no need to verify these new constraints.
1323 */
1324 CloneForeignKeyConstraints(NULL, parent, rel);
1325
1326 table_close(parent, NoLock);
1327 }
1328
1329 /*
1330 * Now add any newly specified CHECK constraints to the new relation. Same
1331 * as for defaults above, but these need to come after partitioning is set
1332 * up.
1333 */
1334 if (stmt->constraints)
1335 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1336 true, true, false, queryString);
1337
1338 /*
1339 * Finally, merge the not-null constraints that are declared directly with
1340 * those that come from parent relations (making sure to count inheritance
1341 * appropriately for each), create them, and set the attnotnull flag on
1342 * columns that don't yet have it.
1343 */
1344 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1345 old_notnulls);
1346 foreach_int(attrnum, nncols)
1347 set_attnotnull(NULL, rel, attrnum, true, false);
1348
1349 ObjectAddressSet(address, RelationRelationId, relationId);
1350
1351 /*
1352 * Clean up. We keep lock on new relation (although it shouldn't be
1353 * visible to anyone else anyway, until commit).
1354 */
1355 relation_close(rel, NoLock);
1356
1357 return address;
1358}
1359
1360/*
1361 * BuildDescForRelation
1362 *
1363 * Given a list of ColumnDef nodes, build a TupleDesc.
1364 *
1365 * Note: This is only for the limited purpose of table and view creation. Not
1366 * everything is filled in. A real tuple descriptor should be obtained from
1367 * the relcache.
1368 */
1371{
1372 int natts;
1374 ListCell *l;
1375 TupleDesc desc;
1376 char *attname;
1377 Oid atttypid;
1378 int32 atttypmod;
1379 Oid attcollation;
1380 int attdim;
1381
1382 /*
1383 * allocate a new tuple descriptor
1384 */
1385 natts = list_length(columns);
1386 desc = CreateTemplateTupleDesc(natts);
1387
1388 attnum = 0;
1389
1390 foreach(l, columns)
1391 {
1392 ColumnDef *entry = lfirst(l);
1393 AclResult aclresult;
1395
1396 /*
1397 * for each entry in the list, get the name and type information from
1398 * the list and have TupleDescInitEntry fill in the attribute
1399 * information we need.
1400 */
1401 attnum++;
1402
1403 attname = entry->colname;
1404 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1405
1406 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1407 if (aclresult != ACLCHECK_OK)
1408 aclcheck_error_type(aclresult, atttypid);
1409
1410 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1411 attdim = list_length(entry->typeName->arrayBounds);
1412 if (attdim > PG_INT16_MAX)
1413 ereport(ERROR,
1414 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1415 errmsg("too many array dimensions"));
1416
1417 if (entry->typeName->setof)
1418 ereport(ERROR,
1419 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1420 errmsg("column \"%s\" cannot be declared SETOF",
1421 attname)));
1422
1424 atttypid, atttypmod, attdim);
1425 att = TupleDescAttr(desc, attnum - 1);
1426
1427 /* Override TupleDescInitEntry's settings as requested */
1428 TupleDescInitEntryCollation(desc, attnum, attcollation);
1429
1430 /* Fill in additional stuff not handled by TupleDescInitEntry */
1431 att->attnotnull = entry->is_not_null;
1432 att->attislocal = entry->is_local;
1433 att->attinhcount = entry->inhcount;
1434 att->attidentity = entry->identity;
1435 att->attgenerated = entry->generated;
1436 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1437 if (entry->storage)
1438 att->attstorage = entry->storage;
1439 else if (entry->storage_name)
1440 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1441
1443 }
1444
1445 return desc;
1446}
1447
1448/*
1449 * Emit the right error or warning message for a "DROP" command issued on a
1450 * non-existent relation
1451 */
1452static void
1453DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1454{
1455 const struct dropmsgstrings *rentry;
1456
1457 if (rel->schemaname != NULL &&
1459 {
1460 if (!missing_ok)
1461 {
1462 ereport(ERROR,
1463 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1464 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1465 }
1466 else
1467 {
1469 (errmsg("schema \"%s\" does not exist, skipping",
1470 rel->schemaname)));
1471 }
1472 return;
1473 }
1474
1475 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1476 {
1477 if (rentry->kind == rightkind)
1478 {
1479 if (!missing_ok)
1480 {
1481 ereport(ERROR,
1482 (errcode(rentry->nonexistent_code),
1483 errmsg(rentry->nonexistent_msg, rel->relname)));
1484 }
1485 else
1486 {
1487 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1488 break;
1489 }
1490 }
1491 }
1492
1493 Assert(rentry->kind != '\0'); /* Should be impossible */
1494}
1495
1496/*
1497 * Emit the right error message for a "DROP" command issued on a
1498 * relation of the wrong type
1499 */
1500static void
1501DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1502{
1503 const struct dropmsgstrings *rentry;
1504 const struct dropmsgstrings *wentry;
1505
1506 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1507 if (rentry->kind == rightkind)
1508 break;
1509 Assert(rentry->kind != '\0');
1510
1511 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1512 if (wentry->kind == wrongkind)
1513 break;
1514 /* wrongkind could be something we don't have in our table... */
1515
1516 ereport(ERROR,
1517 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1518 errmsg(rentry->nota_msg, relname),
1519 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1520}
1521
1522/*
1523 * RemoveRelations
1524 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1525 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1526 */
1527void
1529{
1530 ObjectAddresses *objects;
1531 char relkind;
1532 ListCell *cell;
1533 int flags = 0;
1534 LOCKMODE lockmode = AccessExclusiveLock;
1535
1536 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1537 if (drop->concurrent)
1538 {
1539 /*
1540 * Note that for temporary relations this lock may get upgraded later
1541 * on, but as no other session can access a temporary relation, this
1542 * is actually fine.
1543 */
1544 lockmode = ShareUpdateExclusiveLock;
1545 Assert(drop->removeType == OBJECT_INDEX);
1546 if (list_length(drop->objects) != 1)
1547 ereport(ERROR,
1548 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1549 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1550 if (drop->behavior == DROP_CASCADE)
1551 ereport(ERROR,
1552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1553 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1554 }
1555
1556 /*
1557 * First we identify all the relations, then we delete them in a single
1558 * performMultipleDeletions() call. This is to avoid unwanted DROP
1559 * RESTRICT errors if one of the relations depends on another.
1560 */
1561
1562 /* Determine required relkind */
1563 switch (drop->removeType)
1564 {
1565 case OBJECT_TABLE:
1566 relkind = RELKIND_RELATION;
1567 break;
1568
1569 case OBJECT_INDEX:
1570 relkind = RELKIND_INDEX;
1571 break;
1572
1573 case OBJECT_SEQUENCE:
1574 relkind = RELKIND_SEQUENCE;
1575 break;
1576
1577 case OBJECT_VIEW:
1578 relkind = RELKIND_VIEW;
1579 break;
1580
1581 case OBJECT_MATVIEW:
1582 relkind = RELKIND_MATVIEW;
1583 break;
1584
1586 relkind = RELKIND_FOREIGN_TABLE;
1587 break;
1588
1589 default:
1590 elog(ERROR, "unrecognized drop object type: %d",
1591 (int) drop->removeType);
1592 relkind = 0; /* keep compiler quiet */
1593 break;
1594 }
1595
1596 /* Lock and validate each relation; build a list of object addresses */
1597 objects = new_object_addresses();
1598
1599 foreach(cell, drop->objects)
1600 {
1601 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1602 Oid relOid;
1603 ObjectAddress obj;
1605
1606 /*
1607 * These next few steps are a great deal like relation_openrv, but we
1608 * don't bother building a relcache entry since we don't need it.
1609 *
1610 * Check for shared-cache-inval messages before trying to access the
1611 * relation. This is needed to cover the case where the name
1612 * identifies a rel that has been dropped and recreated since the
1613 * start of our transaction: if we don't flush the old syscache entry,
1614 * then we'll latch onto that entry and suffer an error later.
1615 */
1617
1618 /* Look up the appropriate relation using namespace search. */
1619 state.expected_relkind = relkind;
1620 state.heap_lockmode = drop->concurrent ?
1622 /* We must initialize these fields to show that no locks are held: */
1623 state.heapOid = InvalidOid;
1624 state.partParentOid = InvalidOid;
1625
1626 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1628 &state);
1629
1630 /* Not there? */
1631 if (!OidIsValid(relOid))
1632 {
1633 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1634 continue;
1635 }
1636
1637 /*
1638 * Decide if concurrent mode needs to be used here or not. The
1639 * callback retrieved the rel's persistence for us.
1640 */
1641 if (drop->concurrent &&
1642 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1643 {
1644 Assert(list_length(drop->objects) == 1 &&
1645 drop->removeType == OBJECT_INDEX);
1647 }
1648
1649 /*
1650 * Concurrent index drop cannot be used with partitioned indexes,
1651 * either.
1652 */
1653 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1654 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1655 ereport(ERROR,
1656 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1657 errmsg("cannot drop partitioned index \"%s\" concurrently",
1658 rel->relname)));
1659
1660 /*
1661 * If we're told to drop a partitioned index, we must acquire lock on
1662 * all the children of its parent partitioned table before proceeding.
1663 * Otherwise we'd try to lock the child index partitions before their
1664 * tables, leading to potential deadlock against other sessions that
1665 * will lock those objects in the other order.
1666 */
1667 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1668 (void) find_all_inheritors(state.heapOid,
1669 state.heap_lockmode,
1670 NULL);
1671
1672 /* OK, we're ready to delete this one */
1673 obj.classId = RelationRelationId;
1674 obj.objectId = relOid;
1675 obj.objectSubId = 0;
1676
1677 add_exact_object_address(&obj, objects);
1678 }
1679
1680 performMultipleDeletions(objects, drop->behavior, flags);
1681
1682 free_object_addresses(objects);
1683}
1684
1685/*
1686 * Before acquiring a table lock, check whether we have sufficient rights.
1687 * In the case of DROP INDEX, also try to lock the table before the index.
1688 * Also, if the table to be dropped is a partition, we try to lock the parent
1689 * first.
1690 */
1691static void
1692RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1693 void *arg)
1694{
1695 HeapTuple tuple;
1697 char expected_relkind;
1698 bool is_partition;
1699 Form_pg_class classform;
1701 bool invalid_system_index = false;
1702
1703 state = (struct DropRelationCallbackState *) arg;
1704 heap_lockmode = state->heap_lockmode;
1705
1706 /*
1707 * If we previously locked some other index's heap, and the name we're
1708 * looking up no longer refers to that relation, release the now-useless
1709 * lock.
1710 */
1711 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1712 {
1714 state->heapOid = InvalidOid;
1715 }
1716
1717 /*
1718 * Similarly, if we previously locked some other partition's heap, and the
1719 * name we're looking up no longer refers to that relation, release the
1720 * now-useless lock.
1721 */
1722 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1723 {
1725 state->partParentOid = InvalidOid;
1726 }
1727
1728 /* Didn't find a relation, so no need for locking or permission checks. */
1729 if (!OidIsValid(relOid))
1730 return;
1731
1732 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1733 if (!HeapTupleIsValid(tuple))
1734 return; /* concurrently dropped, so nothing to do */
1735 classform = (Form_pg_class) GETSTRUCT(tuple);
1736 is_partition = classform->relispartition;
1737
1738 /* Pass back some data to save lookups in RemoveRelations */
1739 state->actual_relkind = classform->relkind;
1740 state->actual_relpersistence = classform->relpersistence;
1741
1742 /*
1743 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1744 * but RemoveRelations() can only pass one relkind for a given relation.
1745 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1746 * That means we must be careful before giving the wrong type error when
1747 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1748 * exists with indexes.
1749 */
1750 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1751 expected_relkind = RELKIND_RELATION;
1752 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1753 expected_relkind = RELKIND_INDEX;
1754 else
1755 expected_relkind = classform->relkind;
1756
1757 if (state->expected_relkind != expected_relkind)
1758 DropErrorMsgWrongType(rel->relname, classform->relkind,
1759 state->expected_relkind);
1760
1761 /* Allow DROP to either table owner or schema owner */
1762 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1763 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1765 get_relkind_objtype(classform->relkind),
1766 rel->relname);
1767
1768 /*
1769 * Check the case of a system index that might have been invalidated by a
1770 * failed concurrent process and allow its drop. For the time being, this
1771 * only concerns indexes of toast relations that became invalid during a
1772 * REINDEX CONCURRENTLY process.
1773 */
1774 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1775 {
1776 HeapTuple locTuple;
1777 Form_pg_index indexform;
1778 bool indisvalid;
1779
1780 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1781 if (!HeapTupleIsValid(locTuple))
1782 {
1783 ReleaseSysCache(tuple);
1784 return;
1785 }
1786
1787 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1788 indisvalid = indexform->indisvalid;
1789 ReleaseSysCache(locTuple);
1790
1791 /* Mark object as being an invalid index of system catalogs */
1792 if (!indisvalid)
1793 invalid_system_index = true;
1794 }
1795
1796 /* In the case of an invalid index, it is fine to bypass this check */
1797 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1798 ereport(ERROR,
1799 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1800 errmsg("permission denied: \"%s\" is a system catalog",
1801 rel->relname)));
1802
1803 ReleaseSysCache(tuple);
1804
1805 /*
1806 * In DROP INDEX, attempt to acquire lock on the parent table before
1807 * locking the index. index_drop() will need this anyway, and since
1808 * regular queries lock tables before their indexes, we risk deadlock if
1809 * we do it the other way around. No error if we don't find a pg_index
1810 * entry, though --- the relation may have been dropped. Note that this
1811 * code will execute for either plain or partitioned indexes.
1812 */
1813 if (expected_relkind == RELKIND_INDEX &&
1814 relOid != oldRelOid)
1815 {
1816 state->heapOid = IndexGetRelation(relOid, true);
1817 if (OidIsValid(state->heapOid))
1819 }
1820
1821 /*
1822 * Similarly, if the relation is a partition, we must acquire lock on its
1823 * parent before locking the partition. That's because queries lock the
1824 * parent before its partitions, so we risk deadlock if we do it the other
1825 * way around.
1826 */
1827 if (is_partition && relOid != oldRelOid)
1828 {
1829 state->partParentOid = get_partition_parent(relOid, true);
1830 if (OidIsValid(state->partParentOid))
1831 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1832 }
1833}
1834
1835/*
1836 * ExecuteTruncate
1837 * Executes a TRUNCATE command.
1838 *
1839 * This is a multi-relation truncate. We first open and grab exclusive
1840 * lock on all relations involved, checking permissions and otherwise
1841 * verifying that the relation is OK for truncation. Note that if relations
1842 * are foreign tables, at this stage, we have not yet checked that their
1843 * foreign data in external data sources are OK for truncation. These are
1844 * checked when foreign data are actually truncated later. In CASCADE mode,
1845 * relations having FK references to the targeted relations are automatically
1846 * added to the group; in RESTRICT mode, we check that all FK references are
1847 * internal to the group that's being truncated. Finally all the relations
1848 * are truncated and reindexed.
1849 */
1850void
1852{
1853 List *rels = NIL;
1854 List *relids = NIL;
1855 List *relids_logged = NIL;
1856 ListCell *cell;
1857
1858 /*
1859 * Open, exclusive-lock, and check all the explicitly-specified relations
1860 */
1861 foreach(cell, stmt->relations)
1862 {
1863 RangeVar *rv = lfirst(cell);
1864 Relation rel;
1865 bool recurse = rv->inh;
1866 Oid myrelid;
1867 LOCKMODE lockmode = AccessExclusiveLock;
1868
1869 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1871 NULL);
1872
1873 /* don't throw error for "TRUNCATE foo, foo" */
1874 if (list_member_oid(relids, myrelid))
1875 continue;
1876
1877 /* open the relation, we already hold a lock on it */
1878 rel = table_open(myrelid, NoLock);
1879
1880 /*
1881 * RangeVarGetRelidExtended() has done most checks with its callback,
1882 * but other checks with the now-opened Relation remain.
1883 */
1885
1886 rels = lappend(rels, rel);
1887 relids = lappend_oid(relids, myrelid);
1888
1889 /* Log this relation only if needed for logical decoding */
1891 relids_logged = lappend_oid(relids_logged, myrelid);
1892
1893 if (recurse)
1894 {
1895 ListCell *child;
1896 List *children;
1897
1898 children = find_all_inheritors(myrelid, lockmode, NULL);
1899
1900 foreach(child, children)
1901 {
1902 Oid childrelid = lfirst_oid(child);
1903
1904 if (list_member_oid(relids, childrelid))
1905 continue;
1906
1907 /* find_all_inheritors already got lock */
1908 rel = table_open(childrelid, NoLock);
1909
1910 /*
1911 * It is possible that the parent table has children that are
1912 * temp tables of other backends. We cannot safely access
1913 * such tables (because of buffering issues), and the best
1914 * thing to do is to silently ignore them. Note that this
1915 * check is the same as one of the checks done in
1916 * truncate_check_activity() called below, still it is kept
1917 * here for simplicity.
1918 */
1919 if (RELATION_IS_OTHER_TEMP(rel))
1920 {
1921 table_close(rel, lockmode);
1922 continue;
1923 }
1924
1925 /*
1926 * Inherited TRUNCATE commands perform access permission
1927 * checks on the parent table only. So we skip checking the
1928 * children's permissions and don't call
1929 * truncate_check_perms() here.
1930 */
1933
1934 rels = lappend(rels, rel);
1935 relids = lappend_oid(relids, childrelid);
1936
1937 /* Log this relation only if needed for logical decoding */
1939 relids_logged = lappend_oid(relids_logged, childrelid);
1940 }
1941 }
1942 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1943 ereport(ERROR,
1944 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1945 errmsg("cannot truncate only a partitioned table"),
1946 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1947 }
1948
1949 ExecuteTruncateGuts(rels, relids, relids_logged,
1950 stmt->behavior, stmt->restart_seqs, false);
1951
1952 /* And close the rels */
1953 foreach(cell, rels)
1954 {
1955 Relation rel = (Relation) lfirst(cell);
1956
1957 table_close(rel, NoLock);
1958 }
1959}
1960
1961/*
1962 * ExecuteTruncateGuts
1963 *
1964 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1965 * command (see above) as well as replication subscribers that execute a
1966 * replicated TRUNCATE action.
1967 *
1968 * explicit_rels is the list of Relations to truncate that the command
1969 * specified. relids is the list of Oids corresponding to explicit_rels.
1970 * relids_logged is the list of Oids (a subset of relids) that require
1971 * WAL-logging. This is all a bit redundant, but the existing callers have
1972 * this information handy in this form.
1973 */
1974void
1976 List *relids,
1977 List *relids_logged,
1978 DropBehavior behavior, bool restart_seqs,
1979 bool run_as_table_owner)
1980{
1981 List *rels;
1982 List *seq_relids = NIL;
1983 HTAB *ft_htab = NULL;
1984 EState *estate;
1985 ResultRelInfo *resultRelInfos;
1986 ResultRelInfo *resultRelInfo;
1987 SubTransactionId mySubid;
1988 ListCell *cell;
1989 Oid *logrelids;
1990
1991 /*
1992 * Check the explicitly-specified relations.
1993 *
1994 * In CASCADE mode, suck in all referencing relations as well. This
1995 * requires multiple iterations to find indirectly-dependent relations. At
1996 * each phase, we need to exclusive-lock new rels before looking for their
1997 * dependencies, else we might miss something. Also, we check each rel as
1998 * soon as we open it, to avoid a faux pas such as holding lock for a long
1999 * time on a rel we have no permissions for.
2000 */
2001 rels = list_copy(explicit_rels);
2002 if (behavior == DROP_CASCADE)
2003 {
2004 for (;;)
2005 {
2006 List *newrelids;
2007
2008 newrelids = heap_truncate_find_FKs(relids);
2009 if (newrelids == NIL)
2010 break; /* nothing else to add */
2011
2012 foreach(cell, newrelids)
2013 {
2014 Oid relid = lfirst_oid(cell);
2015 Relation rel;
2016
2017 rel = table_open(relid, AccessExclusiveLock);
2019 (errmsg("truncate cascades to table \"%s\"",
2021 truncate_check_rel(relid, rel->rd_rel);
2022 truncate_check_perms(relid, rel->rd_rel);
2024 rels = lappend(rels, rel);
2025 relids = lappend_oid(relids, relid);
2026
2027 /* Log this relation only if needed for logical decoding */
2029 relids_logged = lappend_oid(relids_logged, relid);
2030 }
2031 }
2032 }
2033
2034 /*
2035 * Check foreign key references. In CASCADE mode, this should be
2036 * unnecessary since we just pulled in all the references; but as a
2037 * cross-check, do it anyway if in an Assert-enabled build.
2038 */
2039#ifdef USE_ASSERT_CHECKING
2040 heap_truncate_check_FKs(rels, false);
2041#else
2042 if (behavior == DROP_RESTRICT)
2043 heap_truncate_check_FKs(rels, false);
2044#endif
2045
2046 /*
2047 * If we are asked to restart sequences, find all the sequences, lock them
2048 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2049 * We want to do this early since it's pointless to do all the truncation
2050 * work only to fail on sequence permissions.
2051 */
2052 if (restart_seqs)
2053 {
2054 foreach(cell, rels)
2055 {
2056 Relation rel = (Relation) lfirst(cell);
2057 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2058 ListCell *seqcell;
2059
2060 foreach(seqcell, seqlist)
2061 {
2062 Oid seq_relid = lfirst_oid(seqcell);
2063 Relation seq_rel;
2064
2065 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2066
2067 /* This check must match AlterSequence! */
2068 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2070 RelationGetRelationName(seq_rel));
2071
2072 seq_relids = lappend_oid(seq_relids, seq_relid);
2073
2074 relation_close(seq_rel, NoLock);
2075 }
2076 }
2077 }
2078
2079 /* Prepare to catch AFTER triggers. */
2081
2082 /*
2083 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2084 * each relation. We don't need to call ExecOpenIndices, though.
2085 *
2086 * We put the ResultRelInfos in the es_opened_result_relations list, even
2087 * though we don't have a range table and don't populate the
2088 * es_result_relations array. That's a bit bogus, but it's enough to make
2089 * ExecGetTriggerResultRel() find them.
2090 */
2091 estate = CreateExecutorState();
2092 resultRelInfos = (ResultRelInfo *)
2093 palloc(list_length(rels) * sizeof(ResultRelInfo));
2094 resultRelInfo = resultRelInfos;
2095 foreach(cell, rels)
2096 {
2097 Relation rel = (Relation) lfirst(cell);
2098
2099 InitResultRelInfo(resultRelInfo,
2100 rel,
2101 0, /* dummy rangetable index */
2102 NULL,
2103 0);
2105 lappend(estate->es_opened_result_relations, resultRelInfo);
2106 resultRelInfo++;
2107 }
2108
2109 /*
2110 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2111 * truncating (this is because one of them might throw an error). Also, if
2112 * we were to allow them to prevent statement execution, that would need
2113 * to be handled here.
2114 */
2115 resultRelInfo = resultRelInfos;
2116 foreach(cell, rels)
2117 {
2118 UserContext ucxt;
2119
2120 if (run_as_table_owner)
2121 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2122 &ucxt);
2123 ExecBSTruncateTriggers(estate, resultRelInfo);
2124 if (run_as_table_owner)
2125 RestoreUserContext(&ucxt);
2126 resultRelInfo++;
2127 }
2128
2129 /*
2130 * OK, truncate each table.
2131 */
2132 mySubid = GetCurrentSubTransactionId();
2133
2134 foreach(cell, rels)
2135 {
2136 Relation rel = (Relation) lfirst(cell);
2137
2138 /* Skip partitioned tables as there is nothing to do */
2139 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2140 continue;
2141
2142 /*
2143 * Build the lists of foreign tables belonging to each foreign server
2144 * and pass each list to the foreign data wrapper's callback function,
2145 * so that each server can truncate its all foreign tables in bulk.
2146 * Each list is saved as a single entry in a hash table that uses the
2147 * server OID as lookup key.
2148 */
2149 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2150 {
2152 bool found;
2153 ForeignTruncateInfo *ft_info;
2154
2155 /* First time through, initialize hashtable for foreign tables */
2156 if (!ft_htab)
2157 {
2158 HASHCTL hctl;
2159
2160 memset(&hctl, 0, sizeof(HASHCTL));
2161 hctl.keysize = sizeof(Oid);
2162 hctl.entrysize = sizeof(ForeignTruncateInfo);
2164
2165 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2166 32, /* start small and extend */
2167 &hctl,
2169 }
2170
2171 /* Find or create cached entry for the foreign table */
2172 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2173 if (!found)
2174 ft_info->rels = NIL;
2175
2176 /*
2177 * Save the foreign table in the entry of the server that the
2178 * foreign table belongs to.
2179 */
2180 ft_info->rels = lappend(ft_info->rels, rel);
2181 continue;
2182 }
2183
2184 /*
2185 * Normally, we need a transaction-safe truncation here. However, if
2186 * the table was either created in the current (sub)transaction or has
2187 * a new relfilenumber in the current (sub)transaction, then we can
2188 * just truncate it in-place, because a rollback would cause the whole
2189 * table or the current physical file to be thrown away anyway.
2190 */
2191 if (rel->rd_createSubid == mySubid ||
2192 rel->rd_newRelfilelocatorSubid == mySubid)
2193 {
2194 /* Immediate, non-rollbackable truncation is OK */
2196 }
2197 else
2198 {
2199 Oid heap_relid;
2200 Oid toast_relid;
2201 ReindexParams reindex_params = {0};
2202
2203 /*
2204 * This effectively deletes all rows in the table, and may be done
2205 * in a serializable transaction. In that case we must record a
2206 * rw-conflict in to this transaction from each transaction
2207 * holding a predicate lock on the table.
2208 */
2210
2211 /*
2212 * Need the full transaction-safe pushups.
2213 *
2214 * Create a new empty storage file for the relation, and assign it
2215 * as the relfilenumber value. The old storage file is scheduled
2216 * for deletion at commit.
2217 */
2218 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2219
2220 heap_relid = RelationGetRelid(rel);
2221
2222 /*
2223 * The same for the toast table, if any.
2224 */
2225 toast_relid = rel->rd_rel->reltoastrelid;
2226 if (OidIsValid(toast_relid))
2227 {
2228 Relation toastrel = relation_open(toast_relid,
2230
2232 toastrel->rd_rel->relpersistence);
2233 table_close(toastrel, NoLock);
2234 }
2235
2236 /*
2237 * Reconstruct the indexes to match, and we're done.
2238 */
2240 &reindex_params);
2241 }
2242
2244 }
2245
2246 /* Now go through the hash table, and truncate foreign tables */
2247 if (ft_htab)
2248 {
2249 ForeignTruncateInfo *ft_info;
2250 HASH_SEQ_STATUS seq;
2251
2252 hash_seq_init(&seq, ft_htab);
2253
2254 PG_TRY();
2255 {
2256 while ((ft_info = hash_seq_search(&seq)) != NULL)
2257 {
2258 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2259
2260 /* truncate_check_rel() has checked that already */
2261 Assert(routine->ExecForeignTruncate != NULL);
2262
2263 routine->ExecForeignTruncate(ft_info->rels,
2264 behavior,
2265 restart_seqs);
2266 }
2267 }
2268 PG_FINALLY();
2269 {
2270 hash_destroy(ft_htab);
2271 }
2272 PG_END_TRY();
2273 }
2274
2275 /*
2276 * Restart owned sequences if we were asked to.
2277 */
2278 foreach(cell, seq_relids)
2279 {
2280 Oid seq_relid = lfirst_oid(cell);
2281
2282 ResetSequence(seq_relid);
2283 }
2284
2285 /*
2286 * Write a WAL record to allow this set of actions to be logically
2287 * decoded.
2288 *
2289 * Assemble an array of relids so we can write a single WAL record for the
2290 * whole action.
2291 */
2292 if (relids_logged != NIL)
2293 {
2294 xl_heap_truncate xlrec;
2295 int i = 0;
2296
2297 /* should only get here if wal_level >= logical */
2299
2300 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2301 foreach(cell, relids_logged)
2302 logrelids[i++] = lfirst_oid(cell);
2303
2304 xlrec.dbId = MyDatabaseId;
2305 xlrec.nrelids = list_length(relids_logged);
2306 xlrec.flags = 0;
2307 if (behavior == DROP_CASCADE)
2308 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2309 if (restart_seqs)
2311
2314 XLogRegisterData(logrelids, list_length(relids_logged) * sizeof(Oid));
2315
2317
2318 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2319 }
2320
2321 /*
2322 * Process all AFTER STATEMENT TRUNCATE triggers.
2323 */
2324 resultRelInfo = resultRelInfos;
2325 foreach(cell, rels)
2326 {
2327 UserContext ucxt;
2328
2329 if (run_as_table_owner)
2330 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2331 &ucxt);
2332 ExecASTruncateTriggers(estate, resultRelInfo);
2333 if (run_as_table_owner)
2334 RestoreUserContext(&ucxt);
2335 resultRelInfo++;
2336 }
2337
2338 /* Handle queued AFTER triggers */
2339 AfterTriggerEndQuery(estate);
2340
2341 /* We can clean up the EState now */
2342 FreeExecutorState(estate);
2343
2344 /*
2345 * Close any rels opened by CASCADE (can't do this while EState still
2346 * holds refs)
2347 */
2348 rels = list_difference_ptr(rels, explicit_rels);
2349 foreach(cell, rels)
2350 {
2351 Relation rel = (Relation) lfirst(cell);
2352
2353 table_close(rel, NoLock);
2354 }
2355}
2356
2357/*
2358 * Check that a given relation is safe to truncate. Subroutine for
2359 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2360 */
2361static void
2363{
2364 char *relname = NameStr(reltuple->relname);
2365
2366 /*
2367 * Only allow truncate on regular tables, foreign tables using foreign
2368 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2369 * latter are only being included here for the following checks; no
2370 * physical truncation will occur in their case.).
2371 */
2372 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2373 {
2374 Oid serverid = GetForeignServerIdByRelId(relid);
2375 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2376
2377 if (!fdwroutine->ExecForeignTruncate)
2378 ereport(ERROR,
2379 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2380 errmsg("cannot truncate foreign table \"%s\"",
2381 relname)));
2382 }
2383 else if (reltuple->relkind != RELKIND_RELATION &&
2384 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2385 ereport(ERROR,
2386 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2387 errmsg("\"%s\" is not a table", relname)));
2388
2389 /*
2390 * Most system catalogs can't be truncated at all, or at least not unless
2391 * allow_system_table_mods=on. As an exception, however, we allow
2392 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2393 * to change its relfilenode to match the old cluster, and allowing a
2394 * TRUNCATE command to be executed is the easiest way of doing that.
2395 */
2396 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2397 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2398 ereport(ERROR,
2399 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2400 errmsg("permission denied: \"%s\" is a system catalog",
2401 relname)));
2402
2404}
2405
2406/*
2407 * Check that current user has the permission to truncate given relation.
2408 */
2409static void
2411{
2412 char *relname = NameStr(reltuple->relname);
2413 AclResult aclresult;
2414
2415 /* Permissions checks */
2416 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2417 if (aclresult != ACLCHECK_OK)
2418 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2419 relname);
2420}
2421
2422/*
2423 * Set of extra sanity checks to check if a given relation is safe to
2424 * truncate. This is split with truncate_check_rel() as
2425 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2426 */
2427static void
2429{
2430 /*
2431 * Don't allow truncate on temp tables of other backends ... their local
2432 * buffer manager is not going to cope.
2433 */
2434 if (RELATION_IS_OTHER_TEMP(rel))
2435 ereport(ERROR,
2436 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2437 errmsg("cannot truncate temporary tables of other sessions")));
2438
2439 /*
2440 * Also check for active uses of the relation in the current transaction,
2441 * including open scans and pending AFTER trigger events.
2442 */
2443 CheckTableNotInUse(rel, "TRUNCATE");
2444}
2445
2446/*
2447 * storage_name
2448 * returns the name corresponding to a typstorage/attstorage enum value
2449 */
2450static const char *
2452{
2453 switch (c)
2454 {
2455 case TYPSTORAGE_PLAIN:
2456 return "PLAIN";
2457 case TYPSTORAGE_EXTERNAL:
2458 return "EXTERNAL";
2459 case TYPSTORAGE_EXTENDED:
2460 return "EXTENDED";
2461 case TYPSTORAGE_MAIN:
2462 return "MAIN";
2463 default:
2464 return "???";
2465 }
2466}
2467
2468/*----------
2469 * MergeAttributes
2470 * Returns new schema given initial schema and superclasses.
2471 *
2472 * Input arguments:
2473 * 'columns' is the column/attribute definition for the table. (It's a list
2474 * of ColumnDef's.) It is destructively changed.
2475 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2476 * 'relpersistence' is the persistence type of the table.
2477 * 'is_partition' tells if the table is a partition.
2478 *
2479 * Output arguments:
2480 * 'supconstr' receives a list of CookedConstraint representing
2481 * CHECK constraints belonging to parent relations, updated as
2482 * necessary to be valid for the child.
2483 * 'supnotnulls' receives a list of CookedConstraint representing
2484 * not-null constraints based on those from parent relations.
2485 *
2486 * Return value:
2487 * Completed schema list.
2488 *
2489 * Notes:
2490 * The order in which the attributes are inherited is very important.
2491 * Intuitively, the inherited attributes should come first. If a table
2492 * inherits from multiple parents, the order of those attributes are
2493 * according to the order of the parents specified in CREATE TABLE.
2494 *
2495 * Here's an example:
2496 *
2497 * create table person (name text, age int4, location point);
2498 * create table emp (salary int4, manager text) inherits(person);
2499 * create table student (gpa float8) inherits (person);
2500 * create table stud_emp (percent int4) inherits (emp, student);
2501 *
2502 * The order of the attributes of stud_emp is:
2503 *
2504 * person {1:name, 2:age, 3:location}
2505 * / \
2506 * {6:gpa} student emp {4:salary, 5:manager}
2507 * \ /
2508 * stud_emp {7:percent}
2509 *
2510 * If the same attribute name appears multiple times, then it appears
2511 * in the result table in the proper location for its first appearance.
2512 *
2513 * Constraints (including not-null constraints) for the child table
2514 * are the union of all relevant constraints, from both the child schema
2515 * and parent tables. In addition, in legacy inheritance, each column that
2516 * appears in a primary key in any of the parents also gets a NOT NULL
2517 * constraint (partitioning doesn't need this, because the PK itself gets
2518 * inherited.)
2519 *
2520 * The default value for a child column is defined as:
2521 * (1) If the child schema specifies a default, that value is used.
2522 * (2) If neither the child nor any parent specifies a default, then
2523 * the column will not have a default.
2524 * (3) If conflicting defaults are inherited from different parents
2525 * (and not overridden by the child), an error is raised.
2526 * (4) Otherwise the inherited default is used.
2527 *
2528 * Note that the default-value infrastructure is used for generated
2529 * columns' expressions too, so most of the preceding paragraph applies
2530 * to generation expressions too. We insist that a child column be
2531 * generated if and only if its parent(s) are, but it need not have
2532 * the same generation expression.
2533 *----------
2534 */
2535static List *
2536MergeAttributes(List *columns, const List *supers, char relpersistence,
2537 bool is_partition, List **supconstr, List **supnotnulls)
2538{
2539 List *inh_columns = NIL;
2540 List *constraints = NIL;
2541 List *nnconstraints = NIL;
2542 bool have_bogus_defaults = false;
2543 int child_attno;
2544 static Node bogus_marker = {0}; /* marks conflicting defaults */
2545 List *saved_columns = NIL;
2546 ListCell *lc;
2547
2548 /*
2549 * Check for and reject tables with too many columns. We perform this
2550 * check relatively early for two reasons: (a) we don't run the risk of
2551 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2552 * okay if we're processing <= 1600 columns, but could take minutes to
2553 * execute if the user attempts to create a table with hundreds of
2554 * thousands of columns.
2555 *
2556 * Note that we also need to check that we do not exceed this figure after
2557 * including columns from inherited relations.
2558 */
2559 if (list_length(columns) > MaxHeapAttributeNumber)
2560 ereport(ERROR,
2561 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2562 errmsg("tables can have at most %d columns",
2564
2565 /*
2566 * Check for duplicate names in the explicit list of attributes.
2567 *
2568 * Although we might consider merging such entries in the same way that we
2569 * handle name conflicts for inherited attributes, it seems to make more
2570 * sense to assume such conflicts are errors.
2571 *
2572 * We don't use foreach() here because we have two nested loops over the
2573 * columns list, with possible element deletions in the inner one. If we
2574 * used foreach_delete_current() it could only fix up the state of one of
2575 * the loops, so it seems cleaner to use looping over list indexes for
2576 * both loops. Note that any deletion will happen beyond where the outer
2577 * loop is, so its index never needs adjustment.
2578 */
2579 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2580 {
2581 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2582
2583 if (!is_partition && coldef->typeName == NULL)
2584 {
2585 /*
2586 * Typed table column option that does not belong to a column from
2587 * the type. This works because the columns from the type come
2588 * first in the list. (We omit this check for partition column
2589 * lists; those are processed separately below.)
2590 */
2591 ereport(ERROR,
2592 (errcode(ERRCODE_UNDEFINED_COLUMN),
2593 errmsg("column \"%s\" does not exist",
2594 coldef->colname)));
2595 }
2596
2597 /* restpos scans all entries beyond coldef; incr is in loop body */
2598 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2599 {
2600 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2601
2602 if (strcmp(coldef->colname, restdef->colname) == 0)
2603 {
2604 if (coldef->is_from_type)
2605 {
2606 /*
2607 * merge the column options into the column from the type
2608 */
2609 coldef->is_not_null = restdef->is_not_null;
2610 coldef->raw_default = restdef->raw_default;
2611 coldef->cooked_default = restdef->cooked_default;
2612 coldef->constraints = restdef->constraints;
2613 coldef->is_from_type = false;
2614 columns = list_delete_nth_cell(columns, restpos);
2615 }
2616 else
2617 ereport(ERROR,
2618 (errcode(ERRCODE_DUPLICATE_COLUMN),
2619 errmsg("column \"%s\" specified more than once",
2620 coldef->colname)));
2621 }
2622 else
2623 restpos++;
2624 }
2625 }
2626
2627 /*
2628 * In case of a partition, there are no new column definitions, only dummy
2629 * ColumnDefs created for column constraints. Set them aside for now and
2630 * process them at the end.
2631 */
2632 if (is_partition)
2633 {
2634 saved_columns = columns;
2635 columns = NIL;
2636 }
2637
2638 /*
2639 * Scan the parents left-to-right, and merge their attributes to form a
2640 * list of inherited columns (inh_columns).
2641 */
2642 child_attno = 0;
2643 foreach(lc, supers)
2644 {
2645 Oid parent = lfirst_oid(lc);
2646 Relation relation;
2647 TupleDesc tupleDesc;
2648 TupleConstr *constr;
2649 AttrMap *newattmap;
2650 List *inherited_defaults;
2651 List *cols_with_defaults;
2652 List *nnconstrs;
2653 ListCell *lc1;
2654 ListCell *lc2;
2655 Bitmapset *nncols = NULL;
2656
2657 /* caller already got lock */
2658 relation = table_open(parent, NoLock);
2659
2660 /*
2661 * Check for active uses of the parent partitioned table in the
2662 * current transaction, such as being used in some manner by an
2663 * enclosing command.
2664 */
2665 if (is_partition)
2666 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2667
2668 /*
2669 * We do not allow partitioned tables and partitions to participate in
2670 * regular inheritance.
2671 */
2672 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2673 ereport(ERROR,
2674 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2675 errmsg("cannot inherit from partitioned table \"%s\"",
2676 RelationGetRelationName(relation))));
2677 if (relation->rd_rel->relispartition && !is_partition)
2678 ereport(ERROR,
2679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2680 errmsg("cannot inherit from partition \"%s\"",
2681 RelationGetRelationName(relation))));
2682
2683 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2684 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2685 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2686 ereport(ERROR,
2687 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2688 errmsg("inherited relation \"%s\" is not a table or foreign table",
2689 RelationGetRelationName(relation))));
2690
2691 /*
2692 * If the parent is permanent, so must be all of its partitions. Note
2693 * that inheritance allows that case.
2694 */
2695 if (is_partition &&
2696 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2697 relpersistence == RELPERSISTENCE_TEMP)
2698 ereport(ERROR,
2699 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2700 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2701 RelationGetRelationName(relation))));
2702
2703 /* Permanent rels cannot inherit from temporary ones */
2704 if (relpersistence != RELPERSISTENCE_TEMP &&
2705 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2706 ereport(ERROR,
2707 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2708 errmsg(!is_partition
2709 ? "cannot inherit from temporary relation \"%s\""
2710 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2711 RelationGetRelationName(relation))));
2712
2713 /* If existing rel is temp, it must belong to this session */
2714 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2715 !relation->rd_islocaltemp)
2716 ereport(ERROR,
2717 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2718 errmsg(!is_partition
2719 ? "cannot inherit from temporary relation of another session"
2720 : "cannot create as partition of temporary relation of another session")));
2721
2722 /*
2723 * We should have an UNDER permission flag for this, but for now,
2724 * demand that creator of a child table own the parent.
2725 */
2726 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2728 RelationGetRelationName(relation));
2729
2730 tupleDesc = RelationGetDescr(relation);
2731 constr = tupleDesc->constr;
2732
2733 /*
2734 * newattmap->attnums[] will contain the child-table attribute numbers
2735 * for the attributes of this parent table. (They are not the same
2736 * for parents after the first one, nor if we have dropped columns.)
2737 */
2738 newattmap = make_attrmap(tupleDesc->natts);
2739
2740 /* We can't process inherited defaults until newattmap is complete. */
2741 inherited_defaults = cols_with_defaults = NIL;
2742
2743 /*
2744 * Request attnotnull on columns that have a not-null constraint
2745 * that's not marked NO INHERIT (even if not valid).
2746 */
2747 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2748 true, false);
2749 foreach_ptr(CookedConstraint, cc, nnconstrs)
2750 nncols = bms_add_member(nncols, cc->attnum);
2751
2752 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2753 parent_attno++)
2754 {
2755 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2756 parent_attno - 1);
2757 char *attributeName = NameStr(attribute->attname);
2758 int exist_attno;
2759 ColumnDef *newdef;
2760 ColumnDef *mergeddef;
2761
2762 /*
2763 * Ignore dropped columns in the parent.
2764 */
2765 if (attribute->attisdropped)
2766 continue; /* leave newattmap->attnums entry as zero */
2767
2768 /*
2769 * Create new column definition
2770 */
2771 newdef = makeColumnDef(attributeName, attribute->atttypid,
2772 attribute->atttypmod, attribute->attcollation);
2773 newdef->storage = attribute->attstorage;
2774 newdef->generated = attribute->attgenerated;
2775 if (CompressionMethodIsValid(attribute->attcompression))
2776 newdef->compression =
2777 pstrdup(GetCompressionMethodName(attribute->attcompression));
2778
2779 /*
2780 * Regular inheritance children are independent enough not to
2781 * inherit identity columns. But partitions are integral part of
2782 * a partitioned table and inherit identity column.
2783 */
2784 if (is_partition)
2785 newdef->identity = attribute->attidentity;
2786
2787 /*
2788 * Does it match some previously considered column from another
2789 * parent?
2790 */
2791 exist_attno = findAttrByName(attributeName, inh_columns);
2792 if (exist_attno > 0)
2793 {
2794 /*
2795 * Yes, try to merge the two column definitions.
2796 */
2797 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2798
2799 newattmap->attnums[parent_attno - 1] = exist_attno;
2800
2801 /*
2802 * Partitions have only one parent, so conflict should never
2803 * occur.
2804 */
2805 Assert(!is_partition);
2806 }
2807 else
2808 {
2809 /*
2810 * No, create a new inherited column
2811 */
2812 newdef->inhcount = 1;
2813 newdef->is_local = false;
2814 inh_columns = lappend(inh_columns, newdef);
2815
2816 newattmap->attnums[parent_attno - 1] = ++child_attno;
2817 mergeddef = newdef;
2818 }
2819
2820 /*
2821 * mark attnotnull if parent has it
2822 */
2823 if (bms_is_member(parent_attno, nncols))
2824 mergeddef->is_not_null = true;
2825
2826 /*
2827 * Locate default/generation expression if any
2828 */
2829 if (attribute->atthasdef)
2830 {
2831 Node *this_default;
2832
2833 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2834 if (this_default == NULL)
2835 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2836 parent_attno, RelationGetRelationName(relation));
2837
2838 /*
2839 * If it's a GENERATED default, it might contain Vars that
2840 * need to be mapped to the inherited column(s)' new numbers.
2841 * We can't do that till newattmap is ready, so just remember
2842 * all the inherited default expressions for the moment.
2843 */
2844 inherited_defaults = lappend(inherited_defaults, this_default);
2845 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2846 }
2847 }
2848
2849 /*
2850 * Now process any inherited default expressions, adjusting attnos
2851 * using the completed newattmap map.
2852 */
2853 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2854 {
2855 Node *this_default = (Node *) lfirst(lc1);
2856 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2857 bool found_whole_row;
2858
2859 /* Adjust Vars to match new table's column numbering */
2860 this_default = map_variable_attnos(this_default,
2861 1, 0,
2862 newattmap,
2863 InvalidOid, &found_whole_row);
2864
2865 /*
2866 * For the moment we have to reject whole-row variables. We could
2867 * convert them, if we knew the new table's rowtype OID, but that
2868 * hasn't been assigned yet. (A variable could only appear in a
2869 * generation expression, so the error message is correct.)
2870 */
2871 if (found_whole_row)
2872 ereport(ERROR,
2873 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2874 errmsg("cannot convert whole-row table reference"),
2875 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2876 def->colname,
2877 RelationGetRelationName(relation))));
2878
2879 /*
2880 * If we already had a default from some prior parent, check to
2881 * see if they are the same. If so, no problem; if not, mark the
2882 * column as having a bogus default. Below, we will complain if
2883 * the bogus default isn't overridden by the child columns.
2884 */
2885 Assert(def->raw_default == NULL);
2886 if (def->cooked_default == NULL)
2887 def->cooked_default = this_default;
2888 else if (!equal(def->cooked_default, this_default))
2889 {
2890 def->cooked_default = &bogus_marker;
2891 have_bogus_defaults = true;
2892 }
2893 }
2894
2895 /*
2896 * Now copy the CHECK constraints of this parent, adjusting attnos
2897 * using the completed newattmap map. Identically named constraints
2898 * are merged if possible, else we throw error.
2899 */
2900 if (constr && constr->num_check > 0)
2901 {
2902 ConstrCheck *check = constr->check;
2903
2904 for (int i = 0; i < constr->num_check; i++)
2905 {
2906 char *name = check[i].ccname;
2907 Node *expr;
2908 bool found_whole_row;
2909
2910 /* ignore if the constraint is non-inheritable */
2911 if (check[i].ccnoinherit)
2912 continue;
2913
2914 /* Adjust Vars to match new table's column numbering */
2915 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2916 1, 0,
2917 newattmap,
2918 InvalidOid, &found_whole_row);
2919
2920 /*
2921 * For the moment we have to reject whole-row variables. We
2922 * could convert them, if we knew the new table's rowtype OID,
2923 * but that hasn't been assigned yet.
2924 */
2925 if (found_whole_row)
2926 ereport(ERROR,
2927 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2928 errmsg("cannot convert whole-row table reference"),
2929 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2930 name,
2931 RelationGetRelationName(relation))));
2932
2933 constraints = MergeCheckConstraint(constraints, name, expr,
2934 check[i].ccenforced);
2935 }
2936 }
2937
2938 /*
2939 * Also copy the not-null constraints from this parent. The
2940 * attnotnull markings were already installed above.
2941 */
2942 foreach_ptr(CookedConstraint, nn, nnconstrs)
2943 {
2944 Assert(nn->contype == CONSTR_NOTNULL);
2945
2946 nn->attnum = newattmap->attnums[nn->attnum - 1];
2947
2948 nnconstraints = lappend(nnconstraints, nn);
2949 }
2950
2951 free_attrmap(newattmap);
2952
2953 /*
2954 * Close the parent rel, but keep our lock on it until xact commit.
2955 * That will prevent someone else from deleting or ALTERing the parent
2956 * before the child is committed.
2957 */
2958 table_close(relation, NoLock);
2959 }
2960
2961 /*
2962 * If we had no inherited attributes, the result columns are just the
2963 * explicitly declared columns. Otherwise, we need to merge the declared
2964 * columns into the inherited column list. Although, we never have any
2965 * explicitly declared columns if the table is a partition.
2966 */
2967 if (inh_columns != NIL)
2968 {
2969 int newcol_attno = 0;
2970
2971 foreach(lc, columns)
2972 {
2973 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2974 char *attributeName = newdef->colname;
2975 int exist_attno;
2976
2977 /*
2978 * Partitions have only one parent and have no column definitions
2979 * of their own, so conflict should never occur.
2980 */
2981 Assert(!is_partition);
2982
2983 newcol_attno++;
2984
2985 /*
2986 * Does it match some inherited column?
2987 */
2988 exist_attno = findAttrByName(attributeName, inh_columns);
2989 if (exist_attno > 0)
2990 {
2991 /*
2992 * Yes, try to merge the two column definitions.
2993 */
2994 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2995 }
2996 else
2997 {
2998 /*
2999 * No, attach new column unchanged to result columns.
3000 */
3001 inh_columns = lappend(inh_columns, newdef);
3002 }
3003 }
3004
3005 columns = inh_columns;
3006
3007 /*
3008 * Check that we haven't exceeded the legal # of columns after merging
3009 * in inherited columns.
3010 */
3011 if (list_length(columns) > MaxHeapAttributeNumber)
3012 ereport(ERROR,
3013 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3014 errmsg("tables can have at most %d columns",
3016 }
3017
3018 /*
3019 * Now that we have the column definition list for a partition, we can
3020 * check whether the columns referenced in the column constraint specs
3021 * actually exist. Also, merge column defaults.
3022 */
3023 if (is_partition)
3024 {
3025 foreach(lc, saved_columns)
3026 {
3027 ColumnDef *restdef = lfirst(lc);
3028 bool found = false;
3029 ListCell *l;
3030
3031 foreach(l, columns)
3032 {
3033 ColumnDef *coldef = lfirst(l);
3034
3035 if (strcmp(coldef->colname, restdef->colname) == 0)
3036 {
3037 found = true;
3038
3039 /*
3040 * Check for conflicts related to generated columns.
3041 *
3042 * Same rules as above: generated-ness has to match the
3043 * parent, but the contents of the generation expression
3044 * can be different.
3045 */
3046 if (coldef->generated)
3047 {
3048 if (restdef->raw_default && !restdef->generated)
3049 ereport(ERROR,
3050 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3051 errmsg("column \"%s\" inherits from generated column but specifies default",
3052 restdef->colname)));
3053 if (restdef->identity)
3054 ereport(ERROR,
3055 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3056 errmsg("column \"%s\" inherits from generated column but specifies identity",
3057 restdef->colname)));
3058 }
3059 else
3060 {
3061 if (restdef->generated)
3062 ereport(ERROR,
3063 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3064 errmsg("child column \"%s\" specifies generation expression",
3065 restdef->colname),
3066 errhint("A child table column cannot be generated unless its parent column is.")));
3067 }
3068
3069 if (coldef->generated && restdef->generated && coldef->generated != restdef->generated)
3070 ereport(ERROR,
3071 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3072 errmsg("column \"%s\" inherits from generated column of different kind",
3073 restdef->colname),
3074 errdetail("Parent column is %s, child column is %s.",
3075 coldef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3076 restdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3077
3078 /*
3079 * Override the parent's default value for this column
3080 * (coldef->cooked_default) with the partition's local
3081 * definition (restdef->raw_default), if there's one. It
3082 * should be physically impossible to get a cooked default
3083 * in the local definition or a raw default in the
3084 * inherited definition, but make sure they're nulls, for
3085 * future-proofing.
3086 */
3087 Assert(restdef->cooked_default == NULL);
3088 Assert(coldef->raw_default == NULL);
3089 if (restdef->raw_default)
3090 {
3091 coldef->raw_default = restdef->raw_default;
3092 coldef->cooked_default = NULL;
3093 }
3094 }
3095 }
3096
3097 /* complain for constraints on columns not in parent */
3098 if (!found)
3099 ereport(ERROR,
3100 (errcode(ERRCODE_UNDEFINED_COLUMN),
3101 errmsg("column \"%s\" does not exist",
3102 restdef->colname)));
3103 }
3104 }
3105
3106 /*
3107 * If we found any conflicting parent default values, check to make sure
3108 * they were overridden by the child.
3109 */
3110 if (have_bogus_defaults)
3111 {
3112 foreach(lc, columns)
3113 {
3114 ColumnDef *def = lfirst(lc);
3115
3116 if (def->cooked_default == &bogus_marker)
3117 {
3118 if (def->generated)
3119 ereport(ERROR,
3120 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3121 errmsg("column \"%s\" inherits conflicting generation expressions",
3122 def->colname),
3123 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3124 else
3125 ereport(ERROR,
3126 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3127 errmsg("column \"%s\" inherits conflicting default values",
3128 def->colname),
3129 errhint("To resolve the conflict, specify a default explicitly.")));
3130 }
3131 }
3132 }
3133
3134 *supconstr = constraints;
3135 *supnotnulls = nnconstraints;
3136
3137 return columns;
3138}
3139
3140
3141/*
3142 * MergeCheckConstraint
3143 * Try to merge an inherited CHECK constraint with previous ones
3144 *
3145 * If we inherit identically-named constraints from multiple parents, we must
3146 * merge them, or throw an error if they don't have identical definitions.
3147 *
3148 * constraints is a list of CookedConstraint structs for previous constraints.
3149 *
3150 * If the new constraint matches an existing one, then the existing
3151 * constraint's inheritance count is updated. If there is a conflict (same
3152 * name but different expression), throw an error. If the constraint neither
3153 * matches nor conflicts with an existing one, a new constraint is appended to
3154 * the list.
3155 */
3156static List *
3157MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3158{
3159 ListCell *lc;
3160 CookedConstraint *newcon;
3161
3162 foreach(lc, constraints)
3163 {
3165
3166 Assert(ccon->contype == CONSTR_CHECK);
3167
3168 /* Non-matching names never conflict */
3169 if (strcmp(ccon->name, name) != 0)
3170 continue;
3171
3172 if (equal(expr, ccon->expr))
3173 {
3174 /* OK to merge constraint with existing */
3175 if (pg_add_s16_overflow(ccon->inhcount, 1,
3176 &ccon->inhcount))
3177 ereport(ERROR,
3178 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3179 errmsg("too many inheritance parents"));
3180
3181 /*
3182 * When enforceability differs, the merged constraint should be
3183 * marked as ENFORCED because one of the parents is ENFORCED.
3184 */
3185 if (!ccon->is_enforced && is_enforced)
3186 {
3187 ccon->is_enforced = true;
3188 ccon->skip_validation = false;
3189 }
3190
3191 return constraints;
3192 }
3193
3194 ereport(ERROR,
3196 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3197 name)));
3198 }
3199
3200 /*
3201 * Constraint couldn't be merged with an existing one and also didn't
3202 * conflict with an existing one, so add it as a new one to the list.
3203 */
3205 newcon->contype = CONSTR_CHECK;
3206 newcon->name = pstrdup(name);
3207 newcon->expr = expr;
3208 newcon->inhcount = 1;
3209 newcon->is_enforced = is_enforced;
3210 newcon->skip_validation = !is_enforced;
3211 return lappend(constraints, newcon);
3212}
3213
3214/*
3215 * MergeChildAttribute
3216 * Merge given child attribute definition into given inherited attribute.
3217 *
3218 * Input arguments:
3219 * 'inh_columns' is the list of inherited ColumnDefs.
3220 * 'exist_attno' is the number of the inherited attribute in inh_columns
3221 * 'newcol_attno' is the attribute number in child table's schema definition
3222 * 'newdef' is the column/attribute definition from the child table.
3223 *
3224 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3225 * ColumnDef remains unchanged.
3226 *
3227 * Notes:
3228 * - The attribute is merged according to the rules laid out in the prologue
3229 * of MergeAttributes().
3230 * - If matching inherited attribute exists but the child attribute can not be
3231 * merged into it, the function throws respective errors.
3232 * - A partition can not have its own column definitions. Hence this function
3233 * is applicable only to a regular inheritance child.
3234 */
3235static void
3236MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3237{
3238 char *attributeName = newdef->colname;
3239 ColumnDef *inhdef;
3240 Oid inhtypeid,
3241 newtypeid;
3242 int32 inhtypmod,
3243 newtypmod;
3244 Oid inhcollid,
3245 newcollid;
3246
3247 if (exist_attno == newcol_attno)
3249 (errmsg("merging column \"%s\" with inherited definition",
3250 attributeName)));
3251 else
3253 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3254 errdetail("User-specified column moved to the position of the inherited column.")));
3255
3256 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3257
3258 /*
3259 * Must have the same type and typmod
3260 */
3261 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3262 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3263 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3264 ereport(ERROR,
3265 (errcode(ERRCODE_DATATYPE_MISMATCH),
3266 errmsg("column \"%s\" has a type conflict",
3267 attributeName),
3268 errdetail("%s versus %s",
3269 format_type_with_typemod(inhtypeid, inhtypmod),
3270 format_type_with_typemod(newtypeid, newtypmod))));
3271
3272 /*
3273 * Must have the same collation
3274 */
3275 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3276 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3277 if (inhcollid != newcollid)
3278 ereport(ERROR,
3279 (errcode(ERRCODE_COLLATION_MISMATCH),
3280 errmsg("column \"%s\" has a collation conflict",
3281 attributeName),
3282 errdetail("\"%s\" versus \"%s\"",
3283 get_collation_name(inhcollid),
3284 get_collation_name(newcollid))));
3285
3286 /*
3287 * Identity is never inherited by a regular inheritance child. Pick
3288 * child's identity definition if there's one.
3289 */
3290 inhdef->identity = newdef->identity;
3291
3292 /*
3293 * Copy storage parameter
3294 */
3295 if (inhdef->storage == 0)
3296 inhdef->storage = newdef->storage;
3297 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3298 ereport(ERROR,
3299 (errcode(ERRCODE_DATATYPE_MISMATCH),
3300 errmsg("column \"%s\" has a storage parameter conflict",
3301 attributeName),
3302 errdetail("%s versus %s",
3303 storage_name(inhdef->storage),
3304 storage_name(newdef->storage))));
3305
3306 /*
3307 * Copy compression parameter
3308 */
3309 if (inhdef->compression == NULL)
3310 inhdef->compression = newdef->compression;
3311 else if (newdef->compression != NULL)
3312 {
3313 if (strcmp(inhdef->compression, newdef->compression) != 0)
3314 ereport(ERROR,
3315 (errcode(ERRCODE_DATATYPE_MISMATCH),
3316 errmsg("column \"%s\" has a compression method conflict",
3317 attributeName),
3318 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3319 }
3320
3321 /*
3322 * Merge of not-null constraints = OR 'em together
3323 */
3324 inhdef->is_not_null |= newdef->is_not_null;
3325
3326 /*
3327 * Check for conflicts related to generated columns.
3328 *
3329 * If the parent column is generated, the child column will be made a
3330 * generated column if it isn't already. If it is a generated column,
3331 * we'll take its generation expression in preference to the parent's. We
3332 * must check that the child column doesn't specify a default value or
3333 * identity, which matches the rules for a single column in
3334 * parse_utilcmd.c.
3335 *
3336 * Conversely, if the parent column is not generated, the child column
3337 * can't be either. (We used to allow that, but it results in being able
3338 * to override the generation expression via UPDATEs through the parent.)
3339 */
3340 if (inhdef->generated)
3341 {
3342 if (newdef->raw_default && !newdef->generated)
3343 ereport(ERROR,
3344 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3345 errmsg("column \"%s\" inherits from generated column but specifies default",
3346 inhdef->colname)));
3347 if (newdef->identity)
3348 ereport(ERROR,
3349 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3350 errmsg("column \"%s\" inherits from generated column but specifies identity",
3351 inhdef->colname)));
3352 }
3353 else
3354 {
3355 if (newdef->generated)
3356 ereport(ERROR,
3357 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3358 errmsg("child column \"%s\" specifies generation expression",
3359 inhdef->colname),
3360 errhint("A child table column cannot be generated unless its parent column is.")));
3361 }
3362
3363 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3364 ereport(ERROR,
3365 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3366 errmsg("column \"%s\" inherits from generated column of different kind",
3367 inhdef->colname),
3368 errdetail("Parent column is %s, child column is %s.",
3369 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3370 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3371
3372 /*
3373 * If new def has a default, override previous default
3374 */
3375 if (newdef->raw_default != NULL)
3376 {
3377 inhdef->raw_default = newdef->raw_default;
3378 inhdef->cooked_default = newdef->cooked_default;
3379 }
3380
3381 /* Mark the column as locally defined */
3382 inhdef->is_local = true;
3383}
3384
3385/*
3386 * MergeInheritedAttribute
3387 * Merge given parent attribute definition into specified attribute
3388 * inherited from the previous parents.
3389 *
3390 * Input arguments:
3391 * 'inh_columns' is the list of previously inherited ColumnDefs.
3392 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3393 * 'newdef' is the new parent column/attribute definition to be merged.
3394 *
3395 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3396 *
3397 * Notes:
3398 * - The attribute is merged according to the rules laid out in the prologue
3399 * of MergeAttributes().
3400 * - If matching inherited attribute exists but the new attribute can not be
3401 * merged into it, the function throws respective errors.
3402 * - A partition inherits from only a single parent. Hence this function is
3403 * applicable only to a regular inheritance.
3404 */
3405static ColumnDef *
3407 int exist_attno,
3408 const ColumnDef *newdef)
3409{
3410 char *attributeName = newdef->colname;
3411 ColumnDef *prevdef;
3412 Oid prevtypeid,
3413 newtypeid;
3414 int32 prevtypmod,
3415 newtypmod;
3416 Oid prevcollid,
3417 newcollid;
3418
3420 (errmsg("merging multiple inherited definitions of column \"%s\"",
3421 attributeName)));
3422 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3423
3424 /*
3425 * Must have the same type and typmod
3426 */
3427 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3428 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3429 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3430 ereport(ERROR,
3431 (errcode(ERRCODE_DATATYPE_MISMATCH),
3432 errmsg("inherited column \"%s\" has a type conflict",
3433 attributeName),
3434 errdetail("%s versus %s",
3435 format_type_with_typemod(prevtypeid, prevtypmod),
3436 format_type_with_typemod(newtypeid, newtypmod))));
3437
3438 /*
3439 * Must have the same collation
3440 */
3441 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3442 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3443 if (prevcollid != newcollid)
3444 ereport(ERROR,
3445 (errcode(ERRCODE_COLLATION_MISMATCH),
3446 errmsg("inherited column \"%s\" has a collation conflict",
3447 attributeName),
3448 errdetail("\"%s\" versus \"%s\"",
3449 get_collation_name(prevcollid),
3450 get_collation_name(newcollid))));
3451
3452 /*
3453 * Copy/check storage parameter
3454 */
3455 if (prevdef->storage == 0)
3456 prevdef->storage = newdef->storage;
3457 else if (prevdef->storage != newdef->storage)
3458 ereport(ERROR,
3459 (errcode(ERRCODE_DATATYPE_MISMATCH),
3460 errmsg("inherited column \"%s\" has a storage parameter conflict",
3461 attributeName),
3462 errdetail("%s versus %s",
3463 storage_name(prevdef->storage),
3464 storage_name(newdef->storage))));
3465
3466 /*
3467 * Copy/check compression parameter
3468 */
3469 if (prevdef->compression == NULL)
3470 prevdef->compression = newdef->compression;
3471 else if (newdef->compression != NULL)
3472 {
3473 if (strcmp(prevdef->compression, newdef->compression) != 0)
3474 ereport(ERROR,
3475 (errcode(ERRCODE_DATATYPE_MISMATCH),
3476 errmsg("column \"%s\" has a compression method conflict",
3477 attributeName),
3478 errdetail("%s versus %s",
3479 prevdef->compression, newdef->compression)));
3480 }
3481
3482 /*
3483 * Check for GENERATED conflicts
3484 */
3485 if (prevdef->generated != newdef->generated)
3486 ereport(ERROR,
3487 (errcode(ERRCODE_DATATYPE_MISMATCH),
3488 errmsg("inherited column \"%s\" has a generation conflict",
3489 attributeName)));
3490
3491 /*
3492 * Default and other constraints are handled by the caller.
3493 */
3494
3495 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3496 &prevdef->inhcount))
3497 ereport(ERROR,
3498 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3499 errmsg("too many inheritance parents"));
3500
3501 return prevdef;
3502}
3503
3504/*
3505 * StoreCatalogInheritance
3506 * Updates the system catalogs with proper inheritance information.
3507 *
3508 * supers is a list of the OIDs of the new relation's direct ancestors.
3509 */
3510static void
3512 bool child_is_partition)
3513{
3514 Relation relation;
3515 int32 seqNumber;
3516 ListCell *entry;
3517
3518 /*
3519 * sanity checks
3520 */
3521 Assert(OidIsValid(relationId));
3522
3523 if (supers == NIL)
3524 return;
3525
3526 /*
3527 * Store INHERITS information in pg_inherits using direct ancestors only.
3528 * Also enter dependencies on the direct ancestors, and make sure they are
3529 * marked with relhassubclass = true.
3530 *
3531 * (Once upon a time, both direct and indirect ancestors were found here
3532 * and then entered into pg_ipl. Since that catalog doesn't exist
3533 * anymore, there's no need to look for indirect ancestors.)
3534 */
3535 relation = table_open(InheritsRelationId, RowExclusiveLock);
3536
3537 seqNumber = 1;
3538 foreach(entry, supers)
3539 {
3540 Oid parentOid = lfirst_oid(entry);
3541
3542 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3543 child_is_partition);
3544 seqNumber++;
3545 }
3546
3547 table_close(relation, RowExclusiveLock);
3548}
3549
3550/*
3551 * Make catalog entries showing relationId as being an inheritance child
3552 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3553 */
3554static void
3555StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3556 int32 seqNumber, Relation inhRelation,
3557 bool child_is_partition)
3558{
3559 ObjectAddress childobject,
3560 parentobject;
3561
3562 /* store the pg_inherits row */
3563 StoreSingleInheritance(relationId, parentOid, seqNumber);
3564
3565 /*
3566 * Store a dependency too
3567 */
3568 parentobject.classId = RelationRelationId;
3569 parentobject.objectId = parentOid;
3570 parentobject.objectSubId = 0;
3571 childobject.classId = RelationRelationId;
3572 childobject.objectId = relationId;
3573 childobject.objectSubId = 0;
3574
3575 recordDependencyOn(&childobject, &parentobject,
3576 child_dependency_type(child_is_partition));
3577
3578 /*
3579 * Post creation hook of this inheritance. Since object_access_hook
3580 * doesn't take multiple object identifiers, we relay oid of parent
3581 * relation using auxiliary_id argument.
3582 */
3583 InvokeObjectPostAlterHookArg(InheritsRelationId,
3584 relationId, 0,
3585 parentOid, false);
3586
3587 /*
3588 * Mark the parent as having subclasses.
3589 */
3590 SetRelationHasSubclass(parentOid, true);
3591}
3592
3593/*
3594 * Look for an existing column entry with the given name.
3595 *
3596 * Returns the index (starting with 1) if attribute already exists in columns,
3597 * 0 if it doesn't.
3598 */
3599static int
3600findAttrByName(const char *attributeName, const List *columns)
3601{
3602 ListCell *lc;
3603 int i = 1;
3604
3605 foreach(lc, columns)
3606 {
3607 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3608 return i;
3609
3610 i++;
3611 }
3612 return 0;
3613}
3614
3615
3616/*
3617 * SetRelationHasSubclass
3618 * Set the value of the relation's relhassubclass field in pg_class.
3619 *
3620 * It's always safe to set this field to true, because all SQL commands are
3621 * ready to see true and then find no children. On the other hand, commands
3622 * generally assume zero children if this is false.
3623 *
3624 * Caller must hold any self-exclusive lock until end of transaction. If the
3625 * new value is false, caller must have acquired that lock before reading the
3626 * evidence that justified the false value. That way, it properly waits if
3627 * another backend is simultaneously concluding no need to change the tuple
3628 * (new and old values are true).
3629 *
3630 * NOTE: an important side-effect of this operation is that an SI invalidation
3631 * message is sent out to all backends --- including me --- causing plans
3632 * referencing the relation to be rebuilt with the new list of children.
3633 * This must happen even if we find that no change is needed in the pg_class
3634 * row.
3635 */
3636void
3637SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3638{
3639 Relation relationRelation;
3640 HeapTuple tuple;
3641 Form_pg_class classtuple;
3642
3644 ShareUpdateExclusiveLock, false) ||
3645 CheckRelationOidLockedByMe(relationId,
3646 ShareRowExclusiveLock, true));
3647
3648 /*
3649 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3650 */
3651 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3652 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3653 if (!HeapTupleIsValid(tuple))
3654 elog(ERROR, "cache lookup failed for relation %u", relationId);
3655 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3656
3657 if (classtuple->relhassubclass != relhassubclass)
3658 {
3659 classtuple->relhassubclass = relhassubclass;
3660 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3661 }
3662 else
3663 {
3664 /* no need to change tuple, but force relcache rebuild anyway */
3666 }
3667
3668 heap_freetuple(tuple);
3669 table_close(relationRelation, RowExclusiveLock);
3670}
3671
3672/*
3673 * CheckRelationTableSpaceMove
3674 * Check if relation can be moved to new tablespace.
3675 *
3676 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3677 *
3678 * Returns true if the relation can be moved to the new tablespace; raises
3679 * an error if it is not possible to do the move; returns false if the move
3680 * would have no effect.
3681 */
3682bool
3684{
3685 Oid oldTableSpaceId;
3686
3687 /*
3688 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3689 * stored as 0.
3690 */
3691 oldTableSpaceId = rel->rd_rel->reltablespace;
3692 if (newTableSpaceId == oldTableSpaceId ||
3693 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3694 return false;
3695
3696 /*
3697 * We cannot support moving mapped relations into different tablespaces.
3698 * (In particular this eliminates all shared catalogs.)
3699 */
3700 if (RelationIsMapped(rel))
3701 ereport(ERROR,
3702 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3703 errmsg("cannot move system relation \"%s\"",
3705
3706 /* Cannot move a non-shared relation into pg_global */
3707 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3708 ereport(ERROR,
3709 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3710 errmsg("only shared relations can be placed in pg_global tablespace")));
3711
3712 /*
3713 * Do not allow moving temp tables of other backends ... their local
3714 * buffer manager is not going to cope.
3715 */
3716 if (RELATION_IS_OTHER_TEMP(rel))
3717 ereport(ERROR,
3718 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3719 errmsg("cannot move temporary tables of other sessions")));
3720
3721 return true;
3722}
3723
3724/*
3725 * SetRelationTableSpace
3726 * Set new reltablespace and relfilenumber in pg_class entry.
3727 *
3728 * newTableSpaceId is the new tablespace for the relation, and
3729 * newRelFilenumber its new filenumber. If newRelFilenumber is
3730 * InvalidRelFileNumber, this field is not updated.
3731 *
3732 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3733 *
3734 * The caller of this routine had better check if a relation can be
3735 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3736 * first, and is responsible for making the change visible with
3737 * CommandCounterIncrement().
3738 */
3739void
3741 Oid newTableSpaceId,
3742 RelFileNumber newRelFilenumber)
3743{
3744 Relation pg_class;
3745 HeapTuple tuple;
3746 ItemPointerData otid;
3747 Form_pg_class rd_rel;
3748 Oid reloid = RelationGetRelid(rel);
3749
3750 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3751
3752 /* Get a modifiable copy of the relation's pg_class row. */
3753 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3754
3755 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3756 if (!HeapTupleIsValid(tuple))
3757 elog(ERROR, "cache lookup failed for relation %u", reloid);
3758 otid = tuple->t_self;
3759 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3760
3761 /* Update the pg_class row. */
3762 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3763 InvalidOid : newTableSpaceId;
3764 if (RelFileNumberIsValid(newRelFilenumber))
3765 rd_rel->relfilenode = newRelFilenumber;
3766 CatalogTupleUpdate(pg_class, &otid, tuple);
3767 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3768
3769 /*
3770 * Record dependency on tablespace. This is only required for relations
3771 * that have no physical storage.
3772 */
3773 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3774 changeDependencyOnTablespace(RelationRelationId, reloid,
3775 rd_rel->reltablespace);
3776
3777 heap_freetuple(tuple);
3778 table_close(pg_class, RowExclusiveLock);
3779}
3780
3781/*
3782 * renameatt_check - basic sanity checks before attribute rename
3783 */
3784static void
3785renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3786{
3787 char relkind = classform->relkind;
3788
3789 if (classform->reloftype && !recursing)
3790 ereport(ERROR,
3791 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3792 errmsg("cannot rename column of typed table")));
3793
3794 /*
3795 * Renaming the columns of sequences or toast tables doesn't actually
3796 * break anything from the system's point of view, since internal
3797 * references are by attnum. But it doesn't seem right to allow users to
3798 * change names that are hardcoded into the system, hence the following
3799 * restriction.
3800 */
3801 if (relkind != RELKIND_RELATION &&
3802 relkind != RELKIND_VIEW &&
3803 relkind != RELKIND_MATVIEW &&
3804 relkind != RELKIND_COMPOSITE_TYPE &&
3805 relkind != RELKIND_INDEX &&
3806 relkind != RELKIND_PARTITIONED_INDEX &&
3807 relkind != RELKIND_FOREIGN_TABLE &&
3808 relkind != RELKIND_PARTITIONED_TABLE)
3809 ereport(ERROR,
3810 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3811 errmsg("cannot rename columns of relation \"%s\"",
3812 NameStr(classform->relname)),
3814
3815 /*
3816 * permissions checking. only the owner of a class can change its schema.
3817 */
3818 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3820 NameStr(classform->relname));
3821 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3822 ereport(ERROR,
3823 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3824 errmsg("permission denied: \"%s\" is a system catalog",
3825 NameStr(classform->relname))));
3826}
3827
3828/*
3829 * renameatt_internal - workhorse for renameatt
3830 *
3831 * Return value is the attribute number in the 'myrelid' relation.
3832 */
3833static AttrNumber
3835 const char *oldattname,
3836 const char *newattname,
3837 bool recurse,
3838 bool recursing,
3839 int expected_parents,
3840 DropBehavior behavior)
3841{
3842 Relation targetrelation;
3843 Relation attrelation;
3844 HeapTuple atttup;
3845 Form_pg_attribute attform;
3847
3848 /*
3849 * Grab an exclusive lock on the target table, which we will NOT release
3850 * until end of transaction.
3851 */
3852 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3853 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3854
3855 /*
3856 * if the 'recurse' flag is set then we are supposed to rename this
3857 * attribute in all classes that inherit from 'relname' (as well as in
3858 * 'relname').
3859 *
3860 * any permissions or problems with duplicate attributes will cause the
3861 * whole transaction to abort, which is what we want -- all or nothing.
3862 */
3863 if (recurse)
3864 {
3865 List *child_oids,
3866 *child_numparents;
3867 ListCell *lo,
3868 *li;
3869
3870 /*
3871 * we need the number of parents for each child so that the recursive
3872 * calls to renameatt() can determine whether there are any parents
3873 * outside the inheritance hierarchy being processed.
3874 */
3875 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3876 &child_numparents);
3877
3878 /*
3879 * find_all_inheritors does the recursive search of the inheritance
3880 * hierarchy, so all we have to do is process all of the relids in the
3881 * list that it returns.
3882 */
3883 forboth(lo, child_oids, li, child_numparents)
3884 {
3885 Oid childrelid = lfirst_oid(lo);
3886 int numparents = lfirst_int(li);
3887
3888 if (childrelid == myrelid)
3889 continue;
3890 /* note we need not recurse again */
3891 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3892 }
3893 }
3894 else
3895 {
3896 /*
3897 * If we are told not to recurse, there had better not be any child
3898 * tables; else the rename would put them out of step.
3899 *
3900 * expected_parents will only be 0 if we are not already recursing.
3901 */
3902 if (expected_parents == 0 &&
3904 ereport(ERROR,
3905 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3906 errmsg("inherited column \"%s\" must be renamed in child tables too",
3907 oldattname)));
3908 }
3909
3910 /* rename attributes in typed tables of composite type */
3911 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3912 {
3913 List *child_oids;
3914 ListCell *lo;
3915
3916 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3917 RelationGetRelationName(targetrelation),
3918 behavior);
3919
3920 foreach(lo, child_oids)
3921 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3922 }
3923
3924 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3925
3926 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3927 if (!HeapTupleIsValid(atttup))
3928 ereport(ERROR,
3929 (errcode(ERRCODE_UNDEFINED_COLUMN),
3930 errmsg("column \"%s\" does not exist",
3931 oldattname)));
3932 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3933
3934 attnum = attform->attnum;
3935 if (attnum <= 0)
3936 ereport(ERROR,
3937 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3938 errmsg("cannot rename system column \"%s\"",
3939 oldattname)));
3940
3941 /*
3942 * if the attribute is inherited, forbid the renaming. if this is a
3943 * top-level call to renameatt(), then expected_parents will be 0, so the
3944 * effect of this code will be to prohibit the renaming if the attribute
3945 * is inherited at all. if this is a recursive call to renameatt(),
3946 * expected_parents will be the number of parents the current relation has
3947 * within the inheritance hierarchy being processed, so we'll prohibit the
3948 * renaming only if there are additional parents from elsewhere.
3949 */
3950 if (attform->attinhcount > expected_parents)
3951 ereport(ERROR,
3952 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3953 errmsg("cannot rename inherited column \"%s\"",
3954 oldattname)));
3955
3956 /* new name should not already exist */
3957 (void) check_for_column_name_collision(targetrelation, newattname, false);
3958
3959 /* apply the update */
3960 namestrcpy(&(attform->attname), newattname);
3961
3962 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3963
3964 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3965
3966 heap_freetuple(atttup);
3967
3968 table_close(attrelation, RowExclusiveLock);
3969
3970 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3971
3972 return attnum;
3973}
3974
3975/*
3976 * Perform permissions and integrity checks before acquiring a relation lock.
3977 */
3978static void
3980 void *arg)
3981{
3982 HeapTuple tuple;
3983 Form_pg_class form;
3984
3985 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3986 if (!HeapTupleIsValid(tuple))
3987 return; /* concurrently dropped */
3988 form = (Form_pg_class) GETSTRUCT(tuple);
3989 renameatt_check(relid, form, false);
3990 ReleaseSysCache(tuple);
3991}
3992
3993/*
3994 * renameatt - changes the name of an attribute in a relation
3995 *
3996 * The returned ObjectAddress is that of the renamed column.
3997 */
4000{
4001 Oid relid;
4003 ObjectAddress address;
4004
4005 /* lock level taken here should match renameatt_internal */
4007 stmt->missing_ok ? RVR_MISSING_OK : 0,
4009 NULL);
4010
4011 if (!OidIsValid(relid))
4012 {
4014 (errmsg("relation \"%s\" does not exist, skipping",
4015 stmt->relation->relname)));
4016 return InvalidObjectAddress;
4017 }
4018
4019 attnum =
4020 renameatt_internal(relid,
4021 stmt->subname, /* old att name */
4022 stmt->newname, /* new att name */
4023 stmt->relation->inh, /* recursive? */
4024 false, /* recursing? */
4025 0, /* expected inhcount */
4026 stmt->behavior);
4027
4028 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
4029
4030 return address;
4031}
4032
4033/*
4034 * same logic as renameatt_internal
4035 */
4036static ObjectAddress
4038 Oid mytypid,
4039 const char *oldconname,
4040 const char *newconname,
4041 bool recurse,
4042 bool recursing,
4043 int expected_parents)
4044{
4045 Relation targetrelation = NULL;
4046 Oid constraintOid;
4047 HeapTuple tuple;
4049 ObjectAddress address;
4050
4051 Assert(!myrelid || !mytypid);
4052
4053 if (mytypid)
4054 {
4055 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4056 }
4057 else
4058 {
4059 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4060
4061 /*
4062 * don't tell it whether we're recursing; we allow changing typed
4063 * tables here
4064 */
4065 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4066
4067 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4068 }
4069
4070 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4071 if (!HeapTupleIsValid(tuple))
4072 elog(ERROR, "cache lookup failed for constraint %u",
4073 constraintOid);
4074 con = (Form_pg_constraint) GETSTRUCT(tuple);
4075
4076 if (myrelid &&
4077 (con->contype == CONSTRAINT_CHECK ||
4078 con->contype == CONSTRAINT_NOTNULL) &&
4079 !con->connoinherit)
4080 {
4081 if (recurse)
4082 {
4083 List *child_oids,
4084 *child_numparents;
4085 ListCell *lo,
4086 *li;
4087
4088 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4089 &child_numparents);
4090
4091 forboth(lo, child_oids, li, child_numparents)
4092 {
4093 Oid childrelid = lfirst_oid(lo);
4094 int numparents = lfirst_int(li);
4095
4096 if (childrelid == myrelid)
4097 continue;
4098
4099 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4100 }
4101 }
4102 else
4103 {
4104 if (expected_parents == 0 &&
4106 ereport(ERROR,
4107 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4108 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4109 oldconname)));
4110 }
4111
4112 if (con->coninhcount > expected_parents)
4113 ereport(ERROR,
4114 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4115 errmsg("cannot rename inherited constraint \"%s\"",
4116 oldconname)));
4117 }
4118
4119 if (con->conindid
4120 && (con->contype == CONSTRAINT_PRIMARY
4121 || con->contype == CONSTRAINT_UNIQUE
4122 || con->contype == CONSTRAINT_EXCLUSION))
4123 /* rename the index; this renames the constraint as well */
4124 RenameRelationInternal(con->conindid, newconname, false, true);
4125 else
4126 RenameConstraintById(constraintOid, newconname);
4127
4128 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4129
4130 ReleaseSysCache(tuple);
4131
4132 if (targetrelation)
4133 {
4134 /*
4135 * Invalidate relcache so as others can see the new constraint name.
4136 */
4137 CacheInvalidateRelcache(targetrelation);
4138
4139 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4140 }
4141
4142 return address;
4143}
4144
4147{
4148 Oid relid = InvalidOid;
4149 Oid typid = InvalidOid;
4150
4151 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4152 {
4153 Relation rel;
4154 HeapTuple tup;
4155
4156 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4157 rel = table_open(TypeRelationId, RowExclusiveLock);
4158 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4159 if (!HeapTupleIsValid(tup))
4160 elog(ERROR, "cache lookup failed for type %u", typid);
4161 checkDomainOwner(tup);
4162 ReleaseSysCache(tup);
4163 table_close(rel, NoLock);
4164 }
4165 else
4166 {
4167 /* lock level taken here should match rename_constraint_internal */
4169 stmt->missing_ok ? RVR_MISSING_OK : 0,
4171 NULL);
4172 if (!OidIsValid(relid))
4173 {
4175 (errmsg("relation \"%s\" does not exist, skipping",
4176 stmt->relation->relname)));
4177 return InvalidObjectAddress;
4178 }
4179 }
4180
4181 return
4182 rename_constraint_internal(relid, typid,
4183 stmt->subname,
4184 stmt->newname,
4185 (stmt->relation &&
4186 stmt->relation->inh), /* recursive? */
4187 false, /* recursing? */
4188 0 /* expected inhcount */ );
4189}
4190
4191/*
4192 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4193 * RENAME
4194 */
4197{
4198 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4199 Oid relid;
4200 ObjectAddress address;
4201
4202 /*
4203 * Grab an exclusive lock on the target table, index, sequence, view,
4204 * materialized view, or foreign table, which we will NOT release until
4205 * end of transaction.
4206 *
4207 * Lock level used here should match RenameRelationInternal, to avoid lock
4208 * escalation. However, because ALTER INDEX can be used with any relation
4209 * type, we mustn't believe without verification.
4210 */
4211 for (;;)
4212 {
4213 LOCKMODE lockmode;
4214 char relkind;
4215 bool obj_is_index;
4216
4217 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4218
4219 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4220 stmt->missing_ok ? RVR_MISSING_OK : 0,
4222 stmt);
4223
4224 if (!OidIsValid(relid))
4225 {
4227 (errmsg("relation \"%s\" does not exist, skipping",
4228 stmt->relation->relname)));
4229 return InvalidObjectAddress;
4230 }
4231
4232 /*
4233 * We allow mismatched statement and object types (e.g., ALTER INDEX
4234 * to rename a table), but we might've used the wrong lock level. If
4235 * that happens, retry with the correct lock level. We don't bother
4236 * if we already acquired AccessExclusiveLock with an index, however.
4237 */
4238 relkind = get_rel_relkind(relid);
4239 obj_is_index = (relkind == RELKIND_INDEX ||
4240 relkind == RELKIND_PARTITIONED_INDEX);
4241 if (obj_is_index || is_index_stmt == obj_is_index)
4242 break;
4243
4244 UnlockRelationOid(relid, lockmode);
4245 is_index_stmt = obj_is_index;
4246 }
4247
4248 /* Do the work */
4249 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4250
4251 ObjectAddressSet(address, RelationRelationId, relid);
4252
4253 return address;
4254}
4255
4256/*
4257 * RenameRelationInternal - change the name of a relation
4258 */
4259void
4260RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4261{
4262 Relation targetrelation;
4263 Relation relrelation; /* for RELATION relation */
4264 ItemPointerData otid;
4265 HeapTuple reltup;
4266 Form_pg_class relform;
4267 Oid namespaceId;
4268
4269 /*
4270 * Grab a lock on the target relation, which we will NOT release until end
4271 * of transaction. We need at least a self-exclusive lock so that
4272 * concurrent DDL doesn't overwrite the rename if they start updating
4273 * while still seeing the old version. The lock also guards against
4274 * triggering relcache reloads in concurrent sessions, which might not
4275 * handle this information changing under them. For indexes, we can use a
4276 * reduced lock level because RelationReloadIndexInfo() handles indexes
4277 * specially.
4278 */
4279 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4280 namespaceId = RelationGetNamespace(targetrelation);
4281
4282 /*
4283 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4284 */
4285 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4286
4287 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4288 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4289 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4290 otid = reltup->t_self;
4291 relform = (Form_pg_class) GETSTRUCT(reltup);
4292
4293 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4294 ereport(ERROR,
4295 (errcode(ERRCODE_DUPLICATE_TABLE),
4296 errmsg("relation \"%s\" already exists",
4297 newrelname)));
4298
4299 /*
4300 * RenameRelation is careful not to believe the caller's idea of the
4301 * relation kind being handled. We don't have to worry about this, but
4302 * let's not be totally oblivious to it. We can process an index as
4303 * not-an-index, but not the other way around.
4304 */
4305 Assert(!is_index ||
4306 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4307 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4308
4309 /*
4310 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4311 * because it's a copy...)
4312 */
4313 namestrcpy(&(relform->relname), newrelname);
4314
4315 CatalogTupleUpdate(relrelation, &otid, reltup);
4316 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4317
4318 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4319 InvalidOid, is_internal);
4320
4321 heap_freetuple(reltup);
4322 table_close(relrelation, RowExclusiveLock);
4323
4324 /*
4325 * Also rename the associated type, if any.
4326 */
4327 if (OidIsValid(targetrelation->rd_rel->reltype))
4328 RenameTypeInternal(targetrelation->rd_rel->reltype,
4329 newrelname, namespaceId);
4330
4331 /*
4332 * Also rename the associated constraint, if any.
4333 */
4334 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4335 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4336 {
4337 Oid constraintId = get_index_constraint(myrelid);
4338
4339 if (OidIsValid(constraintId))
4340 RenameConstraintById(constraintId, newrelname);
4341 }
4342
4343 /*
4344 * Close rel, but keep lock!
4345 */
4346 relation_close(targetrelation, NoLock);
4347}
4348
4349/*
4350 * ResetRelRewrite - reset relrewrite
4351 */
4352void
4354{
4355 Relation relrelation; /* for RELATION relation */
4356 HeapTuple reltup;
4357 Form_pg_class relform;
4358
4359 /*
4360 * Find relation's pg_class tuple.
4361 */
4362 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4363
4364 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4365 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4366 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4367 relform = (Form_pg_class) GETSTRUCT(reltup);
4368
4369 /*
4370 * Update pg_class tuple.
4371 */
4372 relform->relrewrite = InvalidOid;
4373
4374 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4375
4376 heap_freetuple(reltup);
4377 table_close(relrelation, RowExclusiveLock);
4378}
4379
4380/*
4381 * Disallow ALTER TABLE (and similar commands) when the current backend has
4382 * any open reference to the target table besides the one just acquired by
4383 * the calling command; this implies there's an open cursor or active plan.
4384 * We need this check because our lock doesn't protect us against stomping
4385 * on our own foot, only other people's feet!
4386 *
4387 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4388 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4389 * possibly be relaxed to only error out for certain types of alterations.
4390 * But the use-case for allowing any of these things is not obvious, so we
4391 * won't work hard at it for now.
4392 *
4393 * We also reject these commands if there are any pending AFTER trigger events
4394 * for the rel. This is certainly necessary for the rewriting variants of
4395 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4396 * events would try to fetch the wrong tuples. It might be overly cautious
4397 * in other cases, but again it seems better to err on the side of paranoia.
4398 *
4399 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4400 * we are worried about active indexscans on the index. The trigger-event
4401 * check can be skipped, since we are doing no damage to the parent table.
4402 *
4403 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4404 */
4405void
4407{
4408 int expected_refcnt;
4409
4410 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4411 if (rel->rd_refcnt != expected_refcnt)
4412 ereport(ERROR,
4413 (errcode(ERRCODE_OBJECT_IN_USE),
4414 /* translator: first %s is a SQL command, eg ALTER TABLE */
4415 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4417
4418 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4419 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4421 ereport(ERROR,
4422 (errcode(ERRCODE_OBJECT_IN_USE),
4423 /* translator: first %s is a SQL command, eg ALTER TABLE */
4424 errmsg("cannot %s \"%s\" because it has pending trigger events",
4426}
4427
4428/*
4429 * CheckAlterTableIsSafe
4430 * Verify that it's safe to allow ALTER TABLE on this relation.
4431 *
4432 * This consists of CheckTableNotInUse() plus a check that the relation
4433 * isn't another session's temp table. We must split out the temp-table
4434 * check because there are callers of CheckTableNotInUse() that don't want
4435 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4436 * an orphaned temp schema.) Compare truncate_check_activity().
4437 */
4438static void
4440{
4441 /*
4442 * Don't allow ALTER on temp tables of other backends. Their local buffer
4443 * manager is not going to cope if we need to change the table's contents.
4444 * Even if we don't, there may be optimizations that assume temp tables
4445 * aren't subject to such interference.
4446 */
4447 if (RELATION_IS_OTHER_TEMP(rel))
4448 ereport(ERROR,
4449 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4450 errmsg("cannot alter temporary tables of other sessions")));
4451
4452 /*
4453 * Also check for active uses of the relation in the current transaction,
4454 * including open scans and pending AFTER trigger events.
4455 */
4456 CheckTableNotInUse(rel, "ALTER TABLE");
4457}
4458
4459/*
4460 * AlterTableLookupRelation
4461 * Look up, and lock, the OID for the relation named by an alter table
4462 * statement.
4463 */
4464Oid
4466{
4467 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4468 stmt->missing_ok ? RVR_MISSING_OK : 0,
4470 stmt);
4471}
4472
4473/*
4474 * AlterTable
4475 * Execute ALTER TABLE, which can be a list of subcommands
4476 *
4477 * ALTER TABLE is performed in three phases:
4478 * 1. Examine subcommands and perform pre-transformation checking.
4479 * 2. Validate and transform subcommands, and update system catalogs.
4480 * 3. Scan table(s) to check new constraints, and optionally recopy
4481 * the data into new table(s).
4482 * Phase 3 is not performed unless one or more of the subcommands requires
4483 * it. The intention of this design is to allow multiple independent
4484 * updates of the table schema to be performed with only one pass over the
4485 * data.
4486 *
4487 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4488 * each table to be affected (there may be multiple affected tables if the
4489 * commands traverse a table inheritance hierarchy). Also we do preliminary
4490 * validation of the subcommands. Because earlier subcommands may change
4491 * the catalog state seen by later commands, there are limits to what can
4492 * be done in this phase. Generally, this phase acquires table locks,
4493 * checks permissions and relkind, and recurses to find child tables.
4494 *
4495 * ATRewriteCatalogs performs phase 2 for each affected table.
4496 * Certain subcommands need to be performed before others to avoid
4497 * unnecessary conflicts; for example, DROP COLUMN should come before
4498 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4499 * lists, one for each logical "pass" of phase 2.
4500 *
4501 * ATRewriteTables performs phase 3 for those tables that need it.
4502 *
4503 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4504 * since phase 1 already does it. However, for certain subcommand types
4505 * it is only possible to determine how to recurse at phase 2 time; for
4506 * those cases, phase 1 sets the cmd->recurse flag.
4507 *
4508 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4509 * the whole operation; we don't have to do anything special to clean up.
4510 *
4511 * The caller must lock the relation, with an appropriate lock level
4512 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4513 * or higher. We pass the lock level down
4514 * so that we can apply it recursively to inherited tables. Note that the
4515 * lock level we want as we recurse might well be higher than required for
4516 * that specific subcommand. So we pass down the overall lock requirement,
4517 * rather than reassess it at lower levels.
4518 *
4519 * The caller also provides a "context" which is to be passed back to
4520 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4521 * Some of the fields therein, such as the relid, are used here as well.
4522 */
4523void
4525 AlterTableUtilityContext *context)
4526{
4527 Relation rel;
4528
4529 /* Caller is required to provide an adequate lock. */
4530 rel = relation_open(context->relid, NoLock);
4531
4533
4534 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4535}
4536
4537/*
4538 * AlterTableInternal
4539 *
4540 * ALTER TABLE with target specified by OID
4541 *
4542 * We do not reject if the relation is already open, because it's quite
4543 * likely that one or more layers of caller have it open. That means it
4544 * is unsafe to use this entry point for alterations that could break
4545 * existing query plans. On the assumption it's not used for such, we
4546 * don't have to reject pending AFTER triggers, either.
4547 *
4548 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4549 * used for any subcommand types that require parse transformation or
4550 * could generate subcommands that have to be passed to ProcessUtility.
4551 */
4552void
4553AlterTableInternal(Oid relid, List *cmds, bool recurse)
4554{
4555 Relation rel;
4556 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4557
4558 rel = relation_open(relid, lockmode);
4559
4561
4562 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4563}
4564
4565/*
4566 * AlterTableGetLockLevel
4567 *
4568 * Sets the overall lock level required for the supplied list of subcommands.
4569 * Policy for doing this set according to needs of AlterTable(), see
4570 * comments there for overall explanation.
4571 *
4572 * Function is called before and after parsing, so it must give same
4573 * answer each time it is called. Some subcommands are transformed
4574 * into other subcommand types, so the transform must never be made to a
4575 * lower lock level than previously assigned. All transforms are noted below.
4576 *
4577 * Since this is called before we lock the table we cannot use table metadata
4578 * to influence the type of lock we acquire.
4579 *
4580 * There should be no lockmodes hardcoded into the subcommand functions. All
4581 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4582 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4583 * and does not travel through this section of code and cannot be combined with
4584 * any of the subcommands given here.
4585 *
4586 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4587 * so any changes that might affect SELECTs running on standbys need to use
4588 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4589 * have a solution for that also.
4590 *
4591 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4592 * that takes a lock less than AccessExclusiveLock can change object definitions
4593 * while pg_dump is running. Be careful to check that the appropriate data is
4594 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4595 * otherwise we might end up with an inconsistent dump that can't restore.
4596 */
4599{
4600 /*
4601 * This only works if we read catalog tables using MVCC snapshots.
4602 */
4603 ListCell *lcmd;
4605
4606 foreach(lcmd, cmds)
4607 {
4608 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4609 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4610
4611 switch (cmd->subtype)
4612 {
4613 /*
4614 * These subcommands rewrite the heap, so require full locks.
4615 */
4616 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4617 * to SELECT */
4618 case AT_SetAccessMethod: /* must rewrite heap */
4619 case AT_SetTableSpace: /* must rewrite heap */
4620 case AT_AlterColumnType: /* must rewrite heap */
4621 cmd_lockmode = AccessExclusiveLock;
4622 break;
4623
4624 /*
4625 * These subcommands may require addition of toast tables. If
4626 * we add a toast table to a table currently being scanned, we
4627 * might miss data added to the new toast table by concurrent
4628 * insert transactions.
4629 */
4630 case AT_SetStorage: /* may add toast tables, see
4631 * ATRewriteCatalogs() */
4632 cmd_lockmode = AccessExclusiveLock;
4633 break;
4634
4635 /*
4636 * Removing constraints can affect SELECTs that have been
4637 * optimized assuming the constraint holds true. See also
4638 * CloneFkReferenced.
4639 */
4640 case AT_DropConstraint: /* as DROP INDEX */
4641 case AT_DropNotNull: /* may change some SQL plans */
4642 cmd_lockmode = AccessExclusiveLock;
4643 break;
4644
4645 /*
4646 * Subcommands that may be visible to concurrent SELECTs
4647 */
4648 case AT_DropColumn: /* change visible to SELECT */
4649 case AT_AddColumnToView: /* CREATE VIEW */
4650 case AT_DropOids: /* used to equiv to DropColumn */
4651 case AT_EnableAlwaysRule: /* may change SELECT rules */
4652 case AT_EnableReplicaRule: /* may change SELECT rules */
4653 case AT_EnableRule: /* may change SELECT rules */
4654 case AT_DisableRule: /* may change SELECT rules */
4655 cmd_lockmode = AccessExclusiveLock;
4656 break;
4657
4658 /*
4659 * Changing owner may remove implicit SELECT privileges
4660 */
4661 case AT_ChangeOwner: /* change visible to SELECT */
4662 cmd_lockmode = AccessExclusiveLock;
4663 break;
4664
4665 /*
4666 * Changing foreign table options may affect optimization.
4667 */
4668 case AT_GenericOptions:
4670 cmd_lockmode = AccessExclusiveLock;
4671 break;
4672
4673 /*
4674 * These subcommands affect write operations only.
4675 */
4676 case AT_EnableTrig:
4679 case AT_EnableTrigAll:
4680 case AT_EnableTrigUser:
4681 case AT_DisableTrig:
4682 case AT_DisableTrigAll:
4683 case AT_DisableTrigUser:
4684 cmd_lockmode = ShareRowExclusiveLock;
4685 break;
4686
4687 /*
4688 * These subcommands affect write operations only. XXX
4689 * Theoretically, these could be ShareRowExclusiveLock.
4690 */
4691 case AT_ColumnDefault:
4693 case AT_AlterConstraint:
4694 case AT_AddIndex: /* from ADD CONSTRAINT */
4696 case AT_ReplicaIdentity:
4697 case AT_SetNotNull:
4702 case AT_AddIdentity:
4703 case AT_DropIdentity:
4704 case AT_SetIdentity:
4705 case AT_SetExpression:
4706 case AT_DropExpression:
4707 case AT_SetCompression:
4708 cmd_lockmode = AccessExclusiveLock;
4709 break;
4710
4711 case AT_AddConstraint:
4712 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4713 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4714 if (IsA(cmd->def, Constraint))
4715 {
4716 Constraint *con = (Constraint *) cmd->def;
4717
4718 switch (con->contype)
4719 {
4720 case CONSTR_EXCLUSION:
4721 case CONSTR_PRIMARY:
4722 case CONSTR_UNIQUE:
4723
4724 /*
4725 * Cases essentially the same as CREATE INDEX. We
4726 * could reduce the lock strength to ShareLock if
4727 * we can work out how to allow concurrent catalog
4728 * updates. XXX Might be set down to
4729 * ShareRowExclusiveLock but requires further
4730 * analysis.
4731 */
4732 cmd_lockmode = AccessExclusiveLock;
4733 break;
4734 case CONSTR_FOREIGN:
4735
4736 /*
4737 * We add triggers to both tables when we add a
4738 * Foreign Key, so the lock level must be at least
4739 * as strong as CREATE TRIGGER.
4740 */
4741 cmd_lockmode = ShareRowExclusiveLock;
4742 break;
4743
4744 default:
4745 cmd_lockmode = AccessExclusiveLock;
4746 }
4747 }
4748 break;
4749
4750 /*
4751 * These subcommands affect inheritance behaviour. Queries
4752 * started before us will continue to see the old inheritance
4753 * behaviour, while queries started after we commit will see
4754 * new behaviour. No need to prevent reads or writes to the
4755 * subtable while we hook it up though. Changing the TupDesc
4756 * may be a problem, so keep highest lock.
4757 */
4758 case AT_AddInherit:
4759 case AT_DropInherit:
4760 cmd_lockmode = AccessExclusiveLock;
4761 break;
4762
4763 /*
4764 * These subcommands affect implicit row type conversion. They
4765 * have affects similar to CREATE/DROP CAST on queries. don't
4766 * provide for invalidating parse trees as a result of such
4767 * changes, so we keep these at AccessExclusiveLock.
4768 */
4769 case AT_AddOf:
4770 case AT_DropOf:
4771 cmd_lockmode = AccessExclusiveLock;
4772 break;
4773
4774 /*
4775 * Only used by CREATE OR REPLACE VIEW which must conflict
4776 * with an SELECTs currently using the view.
4777 */
4779 cmd_lockmode = AccessExclusiveLock;
4780 break;
4781
4782 /*
4783 * These subcommands affect general strategies for performance
4784 * and maintenance, though don't change the semantic results
4785 * from normal data reads and writes. Delaying an ALTER TABLE
4786 * behind currently active writes only delays the point where
4787 * the new strategy begins to take effect, so there is no
4788 * benefit in waiting. In this case the minimum restriction
4789 * applies: we don't currently allow concurrent catalog
4790 * updates.
4791 */
4792 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4793 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4794 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4795 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4796 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4797 cmd_lockmode = ShareUpdateExclusiveLock;
4798 break;
4799
4800 case AT_SetLogged:
4801 case AT_SetUnLogged:
4802 cmd_lockmode = AccessExclusiveLock;
4803 break;
4804
4805 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4806 cmd_lockmode = ShareUpdateExclusiveLock;
4807 break;
4808
4809 /*
4810 * Rel options are more complex than first appears. Options
4811 * are set here for tables, views and indexes; for historical
4812 * reasons these can all be used with ALTER TABLE, so we can't
4813 * decide between them using the basic grammar.
4814 */
4815 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4816 * getTables() */
4817 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4818 * getTables() */
4819 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4820 break;
4821
4822 case AT_AttachPartition:
4823 cmd_lockmode = ShareUpdateExclusiveLock;
4824 break;
4825
4826 case AT_DetachPartition:
4827 if (((PartitionCmd *) cmd->def)->concurrent)
4828 cmd_lockmode = ShareUpdateExclusiveLock;
4829 else
4830 cmd_lockmode = AccessExclusiveLock;
4831 break;
4832
4834 cmd_lockmode = ShareUpdateExclusiveLock;
4835 break;
4836
4837 default: /* oops */
4838 elog(ERROR, "unrecognized alter table type: %d",
4839 (int) cmd->subtype);
4840 break;
4841 }
4842
4843 /*
4844 * Take the greatest lockmode from any subcommand
4845 */
4846 if (cmd_lockmode > lockmode)
4847 lockmode = cmd_lockmode;
4848 }
4849
4850 return lockmode;
4851}
4852
4853/*
4854 * ATController provides top level control over the phases.
4855 *
4856 * parsetree is passed in to allow it to be passed to event triggers
4857 * when requested.
4858 */
4859static void
4861 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4862 AlterTableUtilityContext *context)
4863{
4864 List *wqueue = NIL;
4865 ListCell *lcmd;
4866
4867 /* Phase 1: preliminary examination of commands, create work queue */
4868 foreach(lcmd, cmds)
4869 {
4870 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4871
4872 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4873 }
4874
4875 /* Close the relation, but keep lock until commit */
4876 relation_close(rel, NoLock);
4877
4878 /* Phase 2: update system catalogs */
4879 ATRewriteCatalogs(&wqueue, lockmode, context);
4880
4881 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4882 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4883}
4884
4885/*
4886 * ATPrepCmd
4887 *
4888 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4889 * recursion and permission checks.
4890 *
4891 * Caller must have acquired appropriate lock type on relation already.
4892 * This lock should be held until commit.
4893 */
4894static void
4896 bool recurse, bool recursing, LOCKMODE lockmode,
4897 AlterTableUtilityContext *context)
4898{
4899 AlteredTableInfo *tab;
4901
4902 /* Find or create work queue entry for this table */
4903 tab = ATGetQueueEntry(wqueue, rel);
4904
4905 /*
4906 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4907 * partitions that are pending detach.
4908 */
4909 if (rel->rd_rel->relispartition &&
4912 ereport(ERROR,
4913 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4914 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4916 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4917
4918 /*
4919 * Copy the original subcommand for each table, so we can scribble on it.
4920 * This avoids conflicts when different child tables need to make
4921 * different parse transformations (for example, the same column may have
4922 * different column numbers in different children).
4923 */
4924 cmd = copyObject(cmd);
4925
4926 /*
4927 * Do permissions and relkind checking, recursion to child tables if
4928 * needed, and any additional phase-1 processing needed. (But beware of
4929 * adding any processing that looks at table details that another
4930 * subcommand could change. In some cases we reject multiple subcommands
4931 * that could try to change the same state in contrary ways.)
4932 */
4933 switch (cmd->subtype)
4934 {
4935 case AT_AddColumn: /* ADD COLUMN */
4936 ATSimplePermissions(cmd->subtype, rel,
4939 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4940 lockmode, context);
4941 /* Recursion occurs during execution phase */
4942 pass = AT_PASS_ADD_COL;
4943 break;
4944 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4946 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4947 lockmode, context);
4948 /* Recursion occurs during execution phase */
4949 pass = AT_PASS_ADD_COL;
4950 break;
4951 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4952
4953 /*
4954 * We allow defaults on views so that INSERT into a view can have
4955 * default-ish behavior. This works because the rewriter
4956 * substitutes default values into INSERTs before it expands
4957 * rules.
4958 */
4959 ATSimplePermissions(cmd->subtype, rel,
4962 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4963 /* No command-specific prep needed */
4965 break;
4966 case AT_CookedColumnDefault: /* add a pre-cooked default */
4967 /* This is currently used only in CREATE TABLE */
4968 /* (so the permission check really isn't necessary) */
4969 ATSimplePermissions(cmd->subtype, rel,
4971 /* This command never recurses */
4973 break;
4974 case AT_AddIdentity:
4975 ATSimplePermissions(cmd->subtype, rel,
4978 /* Set up recursion for phase 2; no other prep needed */
4979 if (recurse)
4980 cmd->recurse = true;
4982 break;
4983 case AT_SetIdentity:
4984 ATSimplePermissions(cmd->subtype, rel,
4987 /* Set up recursion for phase 2; no other prep needed */
4988 if (recurse)
4989 cmd->recurse = true;
4990 /* This should run after AddIdentity, so do it in MISC pass */
4991 pass = AT_PASS_MISC;
4992 break;
4993 case AT_DropIdentity:
4994 ATSimplePermissions(cmd->subtype, rel,
4997 /* Set up recursion for phase 2; no other prep needed */
4998 if (recurse)
4999 cmd->recurse = true;
5000 pass = AT_PASS_DROP;
5001 break;
5002 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5003 ATSimplePermissions(cmd->subtype, rel,
5005 /* Set up recursion for phase 2; no other prep needed */
5006 if (recurse)
5007 cmd->recurse = true;
5008 pass = AT_PASS_DROP;
5009 break;
5010 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5011 ATSimplePermissions(cmd->subtype, rel,
5013 /* Set up recursion for phase 2; no other prep needed */
5014 if (recurse)
5015 cmd->recurse = true;
5016 pass = AT_PASS_COL_ATTRS;
5017 break;
5018 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5019 ATSimplePermissions(cmd->subtype, rel,
5021 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5023 break;
5024 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5025 ATSimplePermissions(cmd->subtype, rel,
5027 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5028 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5029 pass = AT_PASS_DROP;
5030 break;
5031 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5032 ATSimplePermissions(cmd->subtype, rel,
5035 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5036 /* No command-specific prep needed */
5037 pass = AT_PASS_MISC;
5038 break;
5039 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5040 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5041 ATSimplePermissions(cmd->subtype, rel,
5044 /* This command never recurses */
5045 pass = AT_PASS_MISC;
5046 break;
5047 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5048 ATSimplePermissions(cmd->subtype, rel,
5051 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5052 /* No command-specific prep needed */
5053 pass = AT_PASS_MISC;
5054 break;
5055 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5056 ATSimplePermissions(cmd->subtype, rel,
5058 /* This command never recurses */
5059 /* No command-specific prep needed */
5060 pass = AT_PASS_MISC;
5061 break;
5062 case AT_DropColumn: /* DROP COLUMN */
5063 ATSimplePermissions(cmd->subtype, rel,
5066 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5067 lockmode, context);
5068 /* Recursion occurs during execution phase */
5069 pass = AT_PASS_DROP;
5070 break;
5071 case AT_AddIndex: /* ADD INDEX */
5073 /* This command never recurses */
5074 /* No command-specific prep needed */
5075 pass = AT_PASS_ADD_INDEX;
5076 break;
5077 case AT_AddConstraint: /* ADD CONSTRAINT */
5078 ATSimplePermissions(cmd->subtype, rel,
5080 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5081 if (recurse)
5082 {
5083 /* recurses at exec time; lock descendants and set flag */
5084 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5085 cmd->recurse = true;
5086 }
5087 pass = AT_PASS_ADD_CONSTR;
5088 break;
5089 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5091 /* This command never recurses */
5092 /* No command-specific prep needed */
5094 break;
5095 case AT_DropConstraint: /* DROP CONSTRAINT */
5096 ATSimplePermissions(cmd->subtype, rel,
5098 ATCheckPartitionsNotInUse(rel, lockmode);
5099 /* Other recursion occurs during execution phase */
5100 /* No command-specific prep needed except saving recurse flag */
5101 if (recurse)
5102 cmd->recurse = true;
5103 pass = AT_PASS_DROP;
5104 break;
5105 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5106 ATSimplePermissions(cmd->subtype, rel,
5109 /* See comments for ATPrepAlterColumnType */
5110 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5111 AT_PASS_UNSET, context);
5112 Assert(cmd != NULL);
5113 /* Performs own recursion */
5114 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5115 lockmode, context);
5116 pass = AT_PASS_ALTER_TYPE;
5117 break;
5120 /* This command never recurses */
5121 /* No command-specific prep needed */
5122 pass = AT_PASS_MISC;
5123 break;
5124 case AT_ChangeOwner: /* ALTER OWNER */
5125 /* This command never recurses */
5126 /* No command-specific prep needed */
5127 pass = AT_PASS_MISC;
5128 break;
5129 case AT_ClusterOn: /* CLUSTER ON */
5130 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5131 ATSimplePermissions(cmd->subtype, rel,
5133 /* These commands never recurse */
5134 /* No command-specific prep needed */
5135 pass = AT_PASS_MISC;
5136 break;
5137 case AT_SetLogged: /* SET LOGGED */
5138 case AT_SetUnLogged: /* SET UNLOGGED */
5140 if (tab->chgPersistence)
5141 ereport(ERROR,
5142 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5143 errmsg("cannot change persistence setting twice")));
5144 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5145 pass = AT_PASS_MISC;
5146 break;
5147 case AT_DropOids: /* SET WITHOUT OIDS */
5148 ATSimplePermissions(cmd->subtype, rel,
5150 pass = AT_PASS_DROP;
5151 break;
5152 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5153 ATSimplePermissions(cmd->subtype, rel,
5155
5156 /* check if another access method change was already requested */
5157 if (tab->chgAccessMethod)
5158 ereport(ERROR,
5159 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5160 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5161
5162 ATPrepSetAccessMethod(tab, rel, cmd->name);
5163 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5164 break;
5165 case AT_SetTableSpace: /* SET TABLESPACE */
5168 /* This command never recurses */
5169 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5170 pass = AT_PASS_MISC; /* doesn't actually matter */
5171 break;
5172 case AT_SetRelOptions: /* SET (...) */
5173 case AT_ResetRelOptions: /* RESET (...) */
5174 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5175 ATSimplePermissions(cmd->subtype, rel,
5178 /* This command never recurses */
5179 /* No command-specific prep needed */
5180 pass = AT_PASS_MISC;
5181 break;
5182 case AT_AddInherit: /* INHERIT */
5183 ATSimplePermissions(cmd->subtype, rel,
5185 /* This command never recurses */
5186 ATPrepAddInherit(rel);
5187 pass = AT_PASS_MISC;
5188 break;
5189 case AT_DropInherit: /* NO INHERIT */
5190 ATSimplePermissions(cmd->subtype, rel,
5192 /* This command never recurses */
5193 /* No command-specific prep needed */
5194 pass = AT_PASS_MISC;
5195 break;
5196 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5197 ATSimplePermissions(cmd->subtype, rel,
5199 /* Recursion occurs during execution phase */
5200 if (recurse)
5201 cmd->recurse = true;
5202 pass = AT_PASS_MISC;
5203 break;
5204 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5205 ATSimplePermissions(cmd->subtype, rel,
5207 /* Recursion occurs during execution phase */
5208 /* No command-specific prep needed except saving recurse flag */
5209 if (recurse)
5210 cmd->recurse = true;
5211 pass = AT_PASS_MISC;
5212 break;
5213 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5214 ATSimplePermissions(cmd->subtype, rel,
5216 pass = AT_PASS_MISC;
5217 /* This command never recurses */
5218 /* No command-specific prep needed */
5219 break;
5220 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5223 case AT_EnableTrigAll:
5224 case AT_EnableTrigUser:
5225 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5226 case AT_DisableTrigAll:
5227 case AT_DisableTrigUser:
5228 ATSimplePermissions(cmd->subtype, rel,
5230 /* Set up recursion for phase 2; no other prep needed */
5231 if (recurse)
5232 cmd->recurse = true;
5233 pass = AT_PASS_MISC;
5234 break;
5235 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5238 case AT_DisableRule:
5239 case AT_AddOf: /* OF */
5240 case AT_DropOf: /* NOT OF */
5245 ATSimplePermissions(cmd->subtype, rel,
5247 /* These commands never recurse */
5248 /* No command-specific prep needed */
5249 pass = AT_PASS_MISC;
5250 break;
5251 case AT_GenericOptions:
5253 /* No command-specific prep needed */
5254 pass = AT_PASS_MISC;
5255 break;
5256 case AT_AttachPartition:
5257 ATSimplePermissions(cmd->subtype, rel,
5259 /* No command-specific prep needed */
5260 pass = AT_PASS_MISC;
5261 break;
5262 case AT_DetachPartition:
5264 /* No command-specific prep needed */
5265 pass = AT_PASS_MISC;
5266 break;
5269 /* No command-specific prep needed */
5270 pass = AT_PASS_MISC;
5271 break;
5272 default: /* oops */
5273 elog(ERROR, "unrecognized alter table type: %d",
5274 (int) cmd->subtype);
5275 pass = AT_PASS_UNSET; /* keep compiler quiet */
5276 break;
5277 }
5278 Assert(pass > AT_PASS_UNSET);
5279
5280 /* Add the subcommand to the appropriate list for phase 2 */
5281 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5282}
5283
5284/*
5285 * ATRewriteCatalogs
5286 *
5287 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5288 * dispatched in a "safe" execution order (designed to avoid unnecessary
5289 * conflicts).
5290 */
5291static void
5293 AlterTableUtilityContext *context)
5294{
5295 ListCell *ltab;
5296
5297 /*
5298 * We process all the tables "in parallel", one pass at a time. This is
5299 * needed because we may have to propagate work from one table to another
5300 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5301 * re-adding of the foreign key constraint to the other table). Work can
5302 * only be propagated into later passes, however.
5303 */
5304 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5305 {
5306 /* Go through each table that needs to be processed */
5307 foreach(ltab, *wqueue)
5308 {
5309 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5310 List *subcmds = tab->subcmds[pass];
5311 ListCell *lcmd;
5312
5313 if (subcmds == NIL)
5314 continue;
5315
5316 /*
5317 * Open the relation and store it in tab. This allows subroutines
5318 * close and reopen, if necessary. Appropriate lock was obtained
5319 * by phase 1, needn't get it again.
5320 */
5321 tab->rel = relation_open(tab->relid, NoLock);
5322
5323 foreach(lcmd, subcmds)
5324 ATExecCmd(wqueue, tab,
5326 lockmode, pass, context);
5327
5328 /*
5329 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5330 * (this is not done in ATExecAlterColumnType since it should be
5331 * done only once if multiple columns of a table are altered).
5332 */
5333 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5334 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5335
5336 if (tab->rel)
5337 {
5338 relation_close(tab->rel, NoLock);
5339 tab->rel = NULL;
5340 }
5341 }
5342 }
5343
5344 /* Check to see if a toast table must be added. */
5345 foreach(ltab, *wqueue)
5346 {
5347 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5348
5349 /*
5350 * If the table is source table of ATTACH PARTITION command, we did
5351 * not modify anything about it that will change its toasting
5352 * requirement, so no need to check.
5353 */
5354 if (((tab->relkind == RELKIND_RELATION ||
5355 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5356 tab->partition_constraint == NULL) ||
5357 tab->relkind == RELKIND_MATVIEW)
5358 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5359 }
5360}
5361
5362/*
5363 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5364 */
5365static void
5367 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5368 AlterTableUtilityContext *context)
5369{
5371 Relation rel = tab->rel;
5372
5373 switch (cmd->subtype)
5374 {
5375 case AT_AddColumn: /* ADD COLUMN */
5376 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5377 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5378 cmd->recurse, false,
5379 lockmode, cur_pass, context);
5380 break;
5381 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5382 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5383 break;
5384 case AT_CookedColumnDefault: /* add a pre-cooked default */
5385 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5386 break;
5387 case AT_AddIdentity:
5388 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5389 cur_pass, context);
5390 Assert(cmd != NULL);
5391 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5392 break;
5393 case AT_SetIdentity:
5394 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5395 cur_pass, context);
5396 Assert(cmd != NULL);
5397 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5398 break;
5399 case AT_DropIdentity:
5400 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5401 break;
5402 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5403 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5404 break;
5405 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5406 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5407 cmd->recurse, false, lockmode);
5408 break;
5409 case AT_SetExpression:
5410 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5411 break;
5412 case AT_DropExpression:
5413 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5414 break;
5415 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5416 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5417 break;
5418 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5419 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5420 break;
5421 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5422 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5423 break;
5424 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5425 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5426 break;
5427 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5428 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5429 lockmode);
5430 break;
5431 case AT_DropColumn: /* DROP COLUMN */
5432 address = ATExecDropColumn(wqueue, rel, cmd->name,
5433 cmd->behavior, cmd->recurse, false,
5434 cmd->missing_ok, lockmode,
5435 NULL);
5436 break;
5437 case AT_AddIndex: /* ADD INDEX */
5438 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5439 lockmode);
5440 break;
5441 case AT_ReAddIndex: /* ADD INDEX */
5442 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5443 lockmode);
5444 break;
5445 case AT_ReAddStatistics: /* ADD STATISTICS */
5446 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5447 true, lockmode);
5448 break;
5449 case AT_AddConstraint: /* ADD CONSTRAINT */
5450 /* Transform the command only during initial examination */
5451 if (cur_pass == AT_PASS_ADD_CONSTR)
5452 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5453 cmd->recurse, lockmode,
5454 cur_pass, context);
5455 /* Depending on constraint type, might be no more work to do now */
5456 if (cmd != NULL)
5457 address =
5458 ATExecAddConstraint(wqueue, tab, rel,
5459 (Constraint *) cmd->def,
5460 cmd->recurse, false, lockmode);
5461 break;
5462 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5463 address =
5464 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5465 true, true, lockmode);
5466 break;
5467 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5468 * constraint */
5469 address =
5470 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5471 ((AlterDomainStmt *) cmd->def)->def,
5472 NULL);
5473 break;
5474 case AT_ReAddComment: /* Re-add existing comment */
5475 address = CommentObject((CommentStmt *) cmd->def);
5476 break;
5477 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5478 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5479 lockmode);
5480 break;
5481 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5482 address = ATExecAlterConstraint(wqueue, rel,
5484 cmd->recurse, lockmode);
5485 break;
5486 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5487 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5488 false, lockmode);
5489 break;
5490 case AT_DropConstraint: /* DROP CONSTRAINT */
5491 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5492 cmd->recurse,
5493 cmd->missing_ok, lockmode);
5494 break;
5495 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5496 /* parse transformation was done earlier */
5497 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5498 break;
5499 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5500 address =
5502 (List *) cmd->def, lockmode);
5503 break;
5504 case AT_ChangeOwner: /* ALTER OWNER */
5506 get_rolespec_oid(cmd->newowner, false),
5507 false, lockmode);
5508 break;
5509 case AT_ClusterOn: /* CLUSTER ON */
5510 address = ATExecClusterOn(rel, cmd->name, lockmode);
5511 break;
5512 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5513 ATExecDropCluster(rel, lockmode);
5514 break;
5515 case AT_SetLogged: /* SET LOGGED */
5516 case AT_SetUnLogged: /* SET UNLOGGED */
5517 break;
5518 case AT_DropOids: /* SET WITHOUT OIDS */
5519 /* nothing to do here, oid columns don't exist anymore */
5520 break;
5521 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5522
5523 /*
5524 * Only do this for partitioned tables, for which this is just a
5525 * catalog change. Tables with storage are handled by Phase 3.
5526 */
5527 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5528 tab->chgAccessMethod)
5530 break;
5531 case AT_SetTableSpace: /* SET TABLESPACE */
5532
5533 /*
5534 * Only do this for partitioned tables and indexes, for which this
5535 * is just a catalog change. Other relation types which have
5536 * storage are handled by Phase 3.
5537 */
5538 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5539 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5541
5542 break;
5543 case AT_SetRelOptions: /* SET (...) */
5544 case AT_ResetRelOptions: /* RESET (...) */
5545 case AT_ReplaceRelOptions: /* replace entire option list */
5546 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5547 break;
5548 case AT_EnableTrig: /* ENABLE TRIGGER name */
5551 cmd->recurse,
5552 lockmode);
5553 break;
5554 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5556 TRIGGER_FIRES_ALWAYS, false,
5557 cmd->recurse,
5558 lockmode);
5559 break;
5560 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5563 cmd->recurse,
5564 lockmode);
5565 break;
5566 case AT_DisableTrig: /* DISABLE TRIGGER name */
5568 TRIGGER_DISABLED, false,
5569 cmd->recurse,
5570 lockmode);
5571 break;
5572 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5575 cmd->recurse,
5576 lockmode);
5577 break;
5578 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5580 TRIGGER_DISABLED, false,
5581 cmd->recurse,
5582 lockmode);
5583 break;
5584 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5587 cmd->recurse,
5588 lockmode);
5589 break;
5590 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5592 TRIGGER_DISABLED, true,
5593 cmd->recurse,
5594 lockmode);
5595 break;
5596
5597 case AT_EnableRule: /* ENABLE RULE name */
5598 ATExecEnableDisableRule(rel, cmd->name,
5599 RULE_FIRES_ON_ORIGIN, lockmode);
5600 break;
5601 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5602 ATExecEnableDisableRule(rel, cmd->name,
5603 RULE_FIRES_ALWAYS, lockmode);
5604 break;
5605 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5606 ATExecEnableDisableRule(rel, cmd->name,
5607 RULE_FIRES_ON_REPLICA, lockmode);
5608 break;
5609 case AT_DisableRule: /* DISABLE RULE name */
5610 ATExecEnableDisableRule(rel, cmd->name,
5611 RULE_DISABLED, lockmode);
5612 break;
5613
5614 case AT_AddInherit:
5615 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5616 break;
5617 case AT_DropInherit:
5618 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5619 break;
5620 case AT_AddOf:
5621 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5622 break;
5623 case AT_DropOf:
5624 ATExecDropOf(rel, lockmode);
5625 break;
5626 case AT_ReplicaIdentity:
5627 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5628 break;
5630 ATExecSetRowSecurity(rel, true);
5631 break;
5633 ATExecSetRowSecurity(rel, false);
5634 break;
5637 break;
5640 break;
5641 case AT_GenericOptions:
5642 ATExecGenericOptions(rel, (List *) cmd->def);
5643 break;
5644 case AT_AttachPartition:
5645 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5646 cur_pass, context);
5647 Assert(cmd != NULL);
5648 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5649 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5650 context);
5651 else
5652 address = ATExecAttachPartitionIdx(wqueue, rel,
5653 ((PartitionCmd *) cmd->def)->name);
5654 break;
5655 case AT_DetachPartition:
5656 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5657 cur_pass, context);
5658 Assert(cmd != NULL);
5659 /* ATPrepCmd ensures it must be a table */
5660 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5661 address = ATExecDetachPartition(wqueue, tab, rel,
5662 ((PartitionCmd *) cmd->def)->name,
5663 ((PartitionCmd *) cmd->def)->concurrent);
5664 break;
5666 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5667 break;
5668 default: /* oops */
5669 elog(ERROR, "unrecognized alter table type: %d",
5670 (int) cmd->subtype);
5671 break;
5672 }
5673
5674 /*
5675 * Report the subcommand to interested event triggers.
5676 */
5677 if (cmd)
5678 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5679
5680 /*
5681 * Bump the command counter to ensure the next subcommand in the sequence
5682 * can see the changes so far
5683 */
5685}
5686
5687/*
5688 * ATParseTransformCmd: perform parse transformation for one subcommand
5689 *
5690 * Returns the transformed subcommand tree, if there is one, else NULL.
5691 *
5692 * The parser may hand back additional AlterTableCmd(s) and/or other
5693 * utility statements, either before or after the original subcommand.
5694 * Other AlterTableCmds are scheduled into the appropriate slot of the
5695 * AlteredTableInfo (they had better be for later passes than the current one).
5696 * Utility statements that are supposed to happen before the AlterTableCmd
5697 * are executed immediately. Those that are supposed to happen afterwards
5698 * are added to the tab->afterStmts list to be done at the very end.
5699 */
5700static AlterTableCmd *
5702 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5703 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5704{
5705 AlterTableCmd *newcmd = NULL;
5707 List *beforeStmts;
5708 List *afterStmts;
5709 ListCell *lc;
5710
5711 /* Gin up an AlterTableStmt with just this subcommand and this table */
5712 atstmt->relation =
5715 -1);
5716 atstmt->relation->inh = recurse;
5717 atstmt->cmds = list_make1(cmd);
5718 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5719 atstmt->missing_ok = false;
5720
5721 /* Transform the AlterTableStmt */
5723 atstmt,
5724 context->queryString,
5725 &beforeStmts,
5726 &afterStmts);
5727
5728 /* Execute any statements that should happen before these subcommand(s) */
5729 foreach(lc, beforeStmts)
5730 {
5731 Node *stmt = (Node *) lfirst(lc);
5732
5735 }
5736
5737 /* Examine the transformed subcommands and schedule them appropriately */
5738 foreach(lc, atstmt->cmds)
5739 {
5741 AlterTablePass pass;
5742
5743 /*
5744 * This switch need only cover the subcommand types that can be added
5745 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5746 * executing the subcommand immediately, as a substitute for the
5747 * original subcommand. (Note, however, that this does cause
5748 * AT_AddConstraint subcommands to be rescheduled into later passes,
5749 * which is important for index and foreign key constraints.)
5750 *
5751 * We assume we needn't do any phase-1 checks for added subcommands.
5752 */
5753 switch (cmd2->subtype)
5754 {
5755 case AT_AddIndex:
5756 pass = AT_PASS_ADD_INDEX;
5757 break;
5760 break;
5761 case AT_AddConstraint:
5762 /* Recursion occurs during execution phase */
5763 if (recurse)
5764 cmd2->recurse = true;
5765 switch (castNode(Constraint, cmd2->def)->contype)
5766 {
5767 case CONSTR_NOTNULL:
5768 pass = AT_PASS_COL_ATTRS;
5769 break;
5770 case CONSTR_PRIMARY:
5771 case CONSTR_UNIQUE:
5772 case CONSTR_EXCLUSION:
5774 break;
5775 default:
5777 break;
5778 }
5779 break;
5781 /* This command never recurses */
5782 /* No command-specific prep needed */
5783 pass = AT_PASS_MISC;
5784 break;
5785 default:
5786 pass = cur_pass;
5787 break;
5788 }
5789
5790 if (pass < cur_pass)
5791 {
5792 /* Cannot schedule into a pass we already finished */
5793 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5794 pass);
5795 }
5796 else if (pass > cur_pass)
5797 {
5798 /* OK, queue it up for later */
5799 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5800 }
5801 else
5802 {
5803 /*
5804 * We should see at most one subcommand for the current pass,
5805 * which is the transformed version of the original subcommand.
5806 */
5807 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5808 {
5809 /* Found the transformed version of our subcommand */
5810 newcmd = cmd2;
5811 }
5812 else
5813 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5814 pass);
5815 }
5816 }
5817
5818 /* Queue up any after-statements to happen at the end */
5819 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5820
5821 return newcmd;
5822}
5823
5824/*
5825 * ATRewriteTables: ALTER TABLE phase 3
5826 */
5827static void
5828ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5829 AlterTableUtilityContext *context)
5830{
5831 ListCell *ltab;
5832
5833 /* Go through each table that needs to be checked or rewritten */
5834 foreach(ltab, *wqueue)
5835 {
5836 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5837
5838 /* Relations without storage may be ignored here */
5839 if (!RELKIND_HAS_STORAGE(tab->relkind))
5840 continue;
5841
5842 /*
5843 * If we change column data types, the operation has to be propagated
5844 * to tables that use this table's rowtype as a column type.
5845 * tab->newvals will also be non-NULL in the case where we're adding a
5846 * column with a default. We choose to forbid that case as well,
5847 * since composite types might eventually support defaults.
5848 *
5849 * (Eventually we'll probably need to check for composite type
5850 * dependencies even when we're just scanning the table without a
5851 * rewrite, but at the moment a composite type does not enforce any
5852 * constraints, so it's not necessary/appropriate to enforce them just
5853 * during ALTER.)
5854 */
5855 if (tab->newvals != NIL || tab->rewrite > 0)
5856 {
5857 Relation rel;
5858
5859 rel = table_open(tab->relid, NoLock);
5860 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5861 table_close(rel, NoLock);
5862 }
5863
5864 /*
5865 * We only need to rewrite the table if at least one column needs to
5866 * be recomputed, or we are changing its persistence or access method.
5867 *
5868 * There are two reasons for requiring a rewrite when changing
5869 * persistence: on one hand, we need to ensure that the buffers
5870 * belonging to each of the two relations are marked with or without
5871 * BM_PERMANENT properly. On the other hand, since rewriting creates
5872 * and assigns a new relfilenumber, we automatically create or drop an
5873 * init fork for the relation as appropriate.
5874 */
5875 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5876 {
5877 /* Build a temporary relation and copy data */
5878 Relation OldHeap;
5879 Oid OIDNewHeap;
5880 Oid NewAccessMethod;
5881 Oid NewTableSpace;
5882 char persistence;
5883
5884 OldHeap = table_open(tab->relid, NoLock);
5885
5886 /*
5887 * We don't support rewriting of system catalogs; there are too
5888 * many corner cases and too little benefit. In particular this
5889 * is certainly not going to work for mapped catalogs.
5890 */
5891 if (IsSystemRelation(OldHeap))
5892 ereport(ERROR,
5893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5894 errmsg("cannot rewrite system relation \"%s\"",
5895 RelationGetRelationName(OldHeap))));
5896
5897 if (RelationIsUsedAsCatalogTable(OldHeap))
5898 ereport(ERROR,
5899 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5900 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5901 RelationGetRelationName(OldHeap))));
5902
5903 /*
5904 * Don't allow rewrite on temp tables of other backends ... their
5905 * local buffer manager is not going to cope. (This is redundant
5906 * with the check in CheckAlterTableIsSafe, but for safety we'll
5907 * check here too.)
5908 */
5909 if (RELATION_IS_OTHER_TEMP(OldHeap))
5910 ereport(ERROR,
5911 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5912 errmsg("cannot rewrite temporary tables of other sessions")));
5913
5914 /*
5915 * Select destination tablespace (same as original unless user
5916 * requested a change)
5917 */
5918 if (tab->newTableSpace)
5919 NewTableSpace = tab->newTableSpace;
5920 else
5921 NewTableSpace = OldHeap->rd_rel->reltablespace;
5922
5923 /*
5924 * Select destination access method (same as original unless user
5925 * requested a change)
5926 */
5927 if (tab->chgAccessMethod)
5928 NewAccessMethod = tab->newAccessMethod;
5929 else
5930 NewAccessMethod = OldHeap->rd_rel->relam;
5931
5932 /*
5933 * Select persistence of transient table (same as original unless
5934 * user requested a change)
5935 */
5936 persistence = tab->chgPersistence ?
5937 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5938
5939 table_close(OldHeap, NoLock);
5940
5941 /*
5942 * Fire off an Event Trigger now, before actually rewriting the
5943 * table.
5944 *
5945 * We don't support Event Trigger for nested commands anywhere,
5946 * here included, and parsetree is given NULL when coming from
5947 * AlterTableInternal.
5948 *
5949 * And fire it only once.
5950 */
5951 if (parsetree)
5952 EventTriggerTableRewrite((Node *) parsetree,
5953 tab->relid,
5954 tab->rewrite);
5955
5956 /*
5957 * Create transient table that will receive the modified data.
5958 *
5959 * Ensure it is marked correctly as logged or unlogged. We have
5960 * to do this here so that buffers for the new relfilenumber will
5961 * have the right persistence set, and at the same time ensure
5962 * that the original filenumbers's buffers will get read in with
5963 * the correct setting (i.e. the original one). Otherwise a
5964 * rollback after the rewrite would possibly result with buffers
5965 * for the original filenumbers having the wrong persistence
5966 * setting.
5967 *
5968 * NB: This relies on swap_relation_files() also swapping the
5969 * persistence. That wouldn't work for pg_class, but that can't be
5970 * unlogged anyway.
5971 */
5972 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5973 persistence, lockmode);
5974
5975 /*
5976 * Copy the heap data into the new table with the desired
5977 * modifications, and test the current data within the table
5978 * against new constraints generated by ALTER TABLE commands.
5979 */
5980 ATRewriteTable(tab, OIDNewHeap);
5981
5982 /*
5983 * Swap the physical files of the old and new heaps, then rebuild
5984 * indexes and discard the old heap. We can use RecentXmin for
5985 * the table's new relfrozenxid because we rewrote all the tuples
5986 * in ATRewriteTable, so no older Xid remains in the table. Also,
5987 * we never try to swap toast tables by content, since we have no
5988 * interest in letting this code work on system catalogs.
5989 */
5990 finish_heap_swap(tab->relid, OIDNewHeap,
5991 false, false, true,
5993 RecentXmin,
5995 persistence);
5996
5997 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5998 }
5999 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6000 {
6001 if (tab->chgPersistence)
6003 }
6004 else
6005 {
6006 /*
6007 * If required, test the current data within the table against new
6008 * constraints generated by ALTER TABLE commands, but don't
6009 * rebuild data.
6010 */
6011 if (tab->constraints != NIL || tab->verify_new_notnull ||
6012 tab->partition_constraint != NULL)
6014
6015 /*
6016 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6017 * just do a block-by-block copy.
6018 */
6019 if (tab->newTableSpace)
6020 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6021 }
6022
6023 /*
6024 * Also change persistence of owned sequences, so that it matches the
6025 * table persistence.
6026 */
6027 if (tab->chgPersistence)
6028 {
6029 List *seqlist = getOwnedSequences(tab->relid);
6030 ListCell *lc;
6031
6032 foreach(lc, seqlist)
6033 {
6034 Oid seq_relid = lfirst_oid(lc);
6035
6037 }
6038 }
6039 }
6040
6041 /*
6042 * Foreign key constraints are checked in a final pass, since (a) it's
6043 * generally best to examine each one separately, and (b) it's at least
6044 * theoretically possible that we have changed both relations of the
6045 * foreign key, and we'd better have finished both rewrites before we try
6046 * to read the tables.
6047 */
6048 foreach(ltab, *wqueue)
6049 {
6050 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6051 Relation rel = NULL;
6052 ListCell *lcon;
6053
6054 /* Relations without storage may be ignored here too */
6055 if (!RELKIND_HAS_STORAGE(tab->relkind))
6056 continue;
6057
6058 foreach(lcon, tab->constraints)
6059 {
6060 NewConstraint *con = lfirst(lcon);
6061
6062 if (con->contype == CONSTR_FOREIGN)
6063 {
6064 Constraint *fkconstraint = (Constraint *) con->qual;
6065 Relation refrel;
6066
6067 if (rel == NULL)
6068 {
6069 /* Long since locked, no need for another */
6070 rel = table_open(tab->relid, NoLock);
6071 }
6072
6073 refrel = table_open(con->refrelid, RowShareLock);
6074
6075 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6076 con->refindid,
6077 con->conid,
6078 con->conwithperiod);
6079
6080 /*
6081 * No need to mark the constraint row as validated, we did
6082 * that when we inserted the row earlier.
6083 */
6084
6085 table_close(refrel, NoLock);
6086 }
6087 }
6088
6089 if (rel)
6090 table_close(rel, NoLock);
6091 }
6092
6093 /* Finally, run any afterStmts that were queued up */
6094 foreach(ltab, *wqueue)
6095 {
6096 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6097 ListCell *lc;
6098
6099 foreach(lc, tab->afterStmts)
6100 {
6101 Node *stmt = (Node *) lfirst(lc);
6102
6105 }
6106 }
6107}
6108
6109/*
6110 * ATRewriteTable: scan or rewrite one table
6111 *
6112 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6113 * must already hold AccessExclusiveLock on it.
6114 */
6115static void
6117{
6118 Relation oldrel;
6119 Relation newrel;
6120 TupleDesc oldTupDesc;
6121 TupleDesc newTupDesc;
6122 bool needscan = false;
6123 List *notnull_attrs;
6124 List *notnull_virtual_attrs;
6125 int i;
6126 ListCell *l;
6127 EState *estate;
6128 CommandId mycid;
6129 BulkInsertState bistate;
6130 int ti_options;
6131 ExprState *partqualstate = NULL;
6132
6133 /*
6134 * Open the relation(s). We have surely already locked the existing
6135 * table.
6136 */
6137 oldrel = table_open(tab->relid, NoLock);
6138 oldTupDesc = tab->oldDesc;
6139 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6140
6141 if (OidIsValid(OIDNewHeap))
6142 {
6144 false));
6145 newrel = table_open(OIDNewHeap, NoLock);
6146 }
6147 else
6148 newrel = NULL;
6149
6150 /*
6151 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6152 * is empty, so don't bother using it.
6153 */
6154 if (newrel)
6155 {
6156 mycid = GetCurrentCommandId(true);
6157 bistate = GetBulkInsertState();
6158 ti_options = TABLE_INSERT_SKIP_FSM;
6159 }
6160 else
6161 {
6162 /* keep compiler quiet about using these uninitialized */
6163 mycid = 0;
6164 bistate = NULL;
6165 ti_options = 0;
6166 }
6167
6168 /*
6169 * Generate the constraint and default execution states
6170 */
6171
6172 estate = CreateExecutorState();
6173
6174 /* Build the needed expression execution states */
6175 foreach(l, tab->constraints)
6176 {
6177 NewConstraint *con = lfirst(l);
6178
6179 switch (con->contype)
6180 {
6181 case CONSTR_CHECK:
6182 needscan = true;
6183 con->qualstate = ExecPrepareExpr((Expr *) expand_generated_columns_in_expr(con->qual, oldrel, 1), estate);
6184 break;
6185 case CONSTR_FOREIGN:
6186 /* Nothing to do here */
6187 break;
6188 default:
6189 elog(ERROR, "unrecognized constraint type: %d",
6190 (int) con->contype);
6191 }
6192 }
6193
6194 /* Build expression execution states for partition check quals */
6195 if (tab->partition_constraint)
6196 {
6197 needscan = true;
6198 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6199 }
6200
6201 foreach(l, tab->newvals)
6202 {
6203 NewColumnValue *ex = lfirst(l);
6204
6205 /* expr already planned */
6206 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6207 }
6208
6209 notnull_attrs = notnull_virtual_attrs = NIL;
6210 if (newrel || tab->verify_new_notnull)
6211 {
6212 /*
6213 * If we are rebuilding the tuples OR if we added any new but not
6214 * verified not-null constraints, check all *valid* not-null
6215 * constraints. This is a bit of overkill but it minimizes risk of
6216 * bugs.
6217 *
6218 * notnull_attrs does *not* collect attribute numbers for valid
6219 * not-null constraints over virtual generated columns; instead, they
6220 * are collected in notnull_virtual_attrs for verification elsewhere.
6221 */
6222 for (i = 0; i < newTupDesc->natts; i++)
6223 {
6224 CompactAttribute *attr = TupleDescCompactAttr(newTupDesc, i);
6225
6226 if (attr->attnullability == ATTNULLABLE_VALID &&
6227 !attr->attisdropped)
6228 {
6229 Form_pg_attribute wholeatt = TupleDescAttr(newTupDesc, i);
6230
6231 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6232 notnull_attrs = lappend_int(notnull_attrs, wholeatt->attnum);
6233 else
6234 notnull_virtual_attrs = lappend_int(notnull_virtual_attrs,
6235 wholeatt->attnum);
6236 }
6237 }
6238 if (notnull_attrs || notnull_virtual_attrs)
6239 needscan = true;
6240 }
6241
6242 if (newrel || needscan)
6243 {
6244 ExprContext *econtext;
6245 TupleTableSlot *oldslot;
6246 TupleTableSlot *newslot;
6247 TableScanDesc scan;
6248 MemoryContext oldCxt;
6249 List *dropped_attrs = NIL;
6250 ListCell *lc;
6251 Snapshot snapshot;
6252 ResultRelInfo *rInfo = NULL;
6253
6254 /*
6255 * When adding or changing a virtual generated column with a not-null
6256 * constraint, we need to evaluate whether the generation expression
6257 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6258 * prepare a dummy ResultRelInfo.
6259 */
6260 if (notnull_virtual_attrs != NIL)
6261 {
6262 MemoryContext oldcontext;
6263
6264 Assert(newTupDesc->constr->has_generated_virtual);
6265 Assert(newTupDesc->constr->has_not_null);
6266 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6267 rInfo = makeNode(ResultRelInfo);
6268 InitResultRelInfo(rInfo,
6269 oldrel,
6270 0, /* dummy rangetable index */
6271 NULL,
6272 estate->es_instrument);
6273 MemoryContextSwitchTo(oldcontext);
6274 }
6275
6276 if (newrel)
6278 (errmsg_internal("rewriting table \"%s\"",
6279 RelationGetRelationName(oldrel))));
6280 else
6282 (errmsg_internal("verifying table \"%s\"",
6283 RelationGetRelationName(oldrel))));
6284
6285 if (newrel)
6286 {
6287 /*
6288 * All predicate locks on the tuples or pages are about to be made
6289 * invalid, because we move tuples around. Promote them to
6290 * relation locks.
6291 */
6293 }
6294
6295 econtext = GetPerTupleExprContext(estate);
6296
6297 /*
6298 * Create necessary tuple slots. When rewriting, two slots are needed,
6299 * otherwise one suffices. In the case where one slot suffices, we
6300 * need to use the new tuple descriptor, otherwise some constraints
6301 * can't be evaluated. Note that even when the tuple layout is the
6302 * same and no rewrite is required, the tupDescs might not be
6303 * (consider ADD COLUMN without a default).
6304 */
6305 if (tab->rewrite)
6306 {
6307 Assert(newrel != NULL);
6308 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6309 table_slot_callbacks(oldrel));
6310 newslot = MakeSingleTupleTableSlot(newTupDesc,
6311 table_slot_callbacks(newrel));
6312
6313 /*
6314 * Set all columns in the new slot to NULL initially, to ensure
6315 * columns added as part of the rewrite are initialized to NULL.
6316 * That is necessary as tab->newvals will not contain an
6317 * expression for columns with a NULL default, e.g. when adding a
6318 * column without a default together with a column with a default
6319 * requiring an actual rewrite.
6320 */
6321 ExecStoreAllNullTuple(newslot);
6322 }
6323 else
6324 {
6325 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6326 table_slot_callbacks(oldrel));
6327 newslot = NULL;
6328 }
6329
6330 /*
6331 * Any attributes that are dropped according to the new tuple
6332 * descriptor can be set to NULL. We precompute the list of dropped
6333 * attributes to avoid needing to do so in the per-tuple loop.
6334 */
6335 for (i = 0; i < newTupDesc->natts; i++)
6336 {
6337 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6338 dropped_attrs = lappend_int(dropped_attrs, i);
6339 }
6340
6341 /*
6342 * Scan through the rows, generating a new row if needed and then
6343 * checking all the constraints.
6344 */
6345 snapshot = RegisterSnapshot(GetLatestSnapshot());
6346 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6347
6348 /*
6349 * Switch to per-tuple memory context and reset it for each tuple
6350 * produced, so we don't leak memory.
6351 */
6353
6354 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6355 {
6356 TupleTableSlot *insertslot;
6357
6358 if (tab->rewrite > 0)
6359 {
6360 /* Extract data from old tuple */
6361 slot_getallattrs(oldslot);
6362 ExecClearTuple(newslot);
6363
6364 /* copy attributes */
6365 memcpy(newslot->tts_values, oldslot->tts_values,
6366 sizeof(Datum) * oldslot->tts_nvalid);
6367 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6368 sizeof(bool) * oldslot->tts_nvalid);
6369
6370 /* Set dropped attributes to null in new tuple */
6371 foreach(lc, dropped_attrs)
6372 newslot->tts_isnull[lfirst_int(lc)] = true;
6373
6374 /*
6375 * Constraints and GENERATED expressions might reference the
6376 * tableoid column, so fill tts_tableOid with the desired
6377 * value. (We must do this each time, because it gets
6378 * overwritten with newrel's OID during storing.)
6379 */
6380 newslot->tts_tableOid = RelationGetRelid(oldrel);
6381
6382 /*
6383 * Process supplied expressions to replace selected columns.
6384 *
6385 * First, evaluate expressions whose inputs come from the old
6386 * tuple.
6387 */
6388 econtext->ecxt_scantuple = oldslot;
6389
6390 foreach(l, tab->newvals)
6391 {
6392 NewColumnValue *ex = lfirst(l);
6393
6394 if (ex->is_generated)
6395 continue;
6396
6397 newslot->tts_values[ex->attnum - 1]
6398 = ExecEvalExpr(ex->exprstate,
6399 econtext,
6400 &newslot->tts_isnull[ex->attnum - 1]);
6401 }
6402
6403 ExecStoreVirtualTuple(newslot);
6404
6405 /*
6406 * Now, evaluate any expressions whose inputs come from the
6407 * new tuple. We assume these columns won't reference each
6408 * other, so that there's no ordering dependency.
6409 */
6410 econtext->ecxt_scantuple = newslot;
6411
6412 foreach(l, tab->newvals)
6413 {
6414 NewColumnValue *ex = lfirst(l);
6415
6416 if (!ex->is_generated)
6417 continue;
6418
6419 newslot->tts_values[ex->attnum - 1]
6420 = ExecEvalExpr(ex->exprstate,
6421 econtext,
6422 &newslot->tts_isnull[ex->attnum - 1]);
6423 }
6424
6425 insertslot = newslot;
6426 }
6427 else
6428 {
6429 /*
6430 * If there's no rewrite, old and new table are guaranteed to
6431 * have the same AM, so we can just use the old slot to verify
6432 * new constraints etc.
6433 */
6434 insertslot = oldslot;
6435 }
6436
6437 /* Now check any constraints on the possibly-changed tuple */
6438 econtext->ecxt_scantuple = insertslot;
6439
6440 foreach_int(attn, notnull_attrs)
6441 {
6442 if (slot_attisnull(insertslot, attn))
6443 {
6444 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn - 1);
6445
6446 ereport(ERROR,
6447 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6448 errmsg("column \"%s\" of relation \"%s\" contains null values",
6449 NameStr(attr->attname),
6450 RelationGetRelationName(oldrel)),
6451 errtablecol(oldrel, attn)));
6452 }
6453 }
6454
6455 if (notnull_virtual_attrs != NIL)
6456 {
6458
6459 attnum = ExecRelGenVirtualNotNull(rInfo, insertslot,
6460 estate,
6461 notnull_virtual_attrs);
6463 {
6464 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attnum - 1);
6465
6466 ereport(ERROR,
6467 errcode(ERRCODE_NOT_NULL_VIOLATION),
6468 errmsg("column \"%s\" of relation \"%s\" contains null values",
6469 NameStr(attr->attname),
6470 RelationGetRelationName(oldrel)),
6471 errtablecol(oldrel, attnum));
6472 }
6473 }
6474
6475 foreach(l, tab->constraints)
6476 {
6477 NewConstraint *con = lfirst(l);
6478
6479 switch (con->contype)
6480 {
6481 case CONSTR_CHECK:
6482 if (!ExecCheck(con->qualstate, econtext))
6483 ereport(ERROR,
6484 (errcode(ERRCODE_CHECK_VIOLATION),
6485 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6486 con->name,
6487 RelationGetRelationName(oldrel)),
6488 errtableconstraint(oldrel, con->name)));
6489 break;
6490 case CONSTR_NOTNULL:
6491 case CONSTR_FOREIGN:
6492 /* Nothing to do here */
6493 break;
6494 default:
6495 elog(ERROR, "unrecognized constraint type: %d",
6496 (int) con->contype);
6497 }
6498 }
6499
6500 if (partqualstate && !ExecCheck(partqualstate, econtext))
6501 {
6502 if (tab->validate_default)
6503 ereport(ERROR,
6504 (errcode(ERRCODE_CHECK_VIOLATION),
6505 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6506 RelationGetRelationName(oldrel)),
6507 errtable(oldrel)));
6508 else
6509 ereport(ERROR,
6510 (errcode(ERRCODE_CHECK_VIOLATION),
6511 errmsg("partition constraint of relation \"%s\" is violated by some row",
6512 RelationGetRelationName(oldrel)),
6513 errtable(oldrel)));
6514 }
6515
6516 /* Write the tuple out to the new relation */
6517 if (newrel)
6518 table_tuple_insert(newrel, insertslot, mycid,
6519 ti_options, bistate);
6520
6521 ResetExprContext(econtext);
6522
6524 }
6525
6526 MemoryContextSwitchTo(oldCxt);
6527 table_endscan(scan);
6528 UnregisterSnapshot(snapshot);
6529
6531 if (newslot)
6533 }
6534
6535 FreeExecutorState(estate);
6536
6537 table_close(oldrel, NoLock);
6538 if (newrel)
6539 {
6540 FreeBulkInsertState(bistate);
6541
6542 table_finish_bulk_insert(newrel, ti_options);
6543
6544 table_close(newrel, NoLock);
6545 }
6546}
6547
6548/*
6549 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6550 */
6551static AlteredTableInfo *
6553{
6554 Oid relid = RelationGetRelid(rel);
6555 AlteredTableInfo *tab;
6556 ListCell *ltab;
6557
6558 foreach(ltab, *wqueue)
6559 {
6560 tab = (AlteredTableInfo *) lfirst(ltab);
6561 if (tab->relid == relid)
6562 return tab;
6563 }
6564
6565 /*
6566 * Not there, so add it. Note that we make a copy of the relation's
6567 * existing descriptor before anything interesting can happen to it.
6568 */
6569 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6570 tab->relid = relid;
6571 tab->rel = NULL; /* set later */
6572 tab->relkind = rel->rd_rel->relkind;
6575 tab->chgAccessMethod = false;
6577 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6578 tab->chgPersistence = false;
6579
6580 *wqueue = lappend(*wqueue, tab);
6581
6582 return tab;
6583}
6584
6585static const char *
6587{
6588 switch (cmdtype)
6589 {
6590 case AT_AddColumn:
6591 case AT_AddColumnToView:
6592 return "ADD COLUMN";
6593 case AT_ColumnDefault:
6595 return "ALTER COLUMN ... SET DEFAULT";
6596 case AT_DropNotNull:
6597 return "ALTER COLUMN ... DROP NOT NULL";
6598 case AT_SetNotNull:
6599 return "ALTER COLUMN ... SET NOT NULL";
6600 case AT_SetExpression:
6601 return "ALTER COLUMN ... SET EXPRESSION";
6602 case AT_DropExpression:
6603 return "ALTER COLUMN ... DROP EXPRESSION";
6604 case AT_SetStatistics:
6605 return "ALTER COLUMN ... SET STATISTICS";
6606 case AT_SetOptions:
6607 return "ALTER COLUMN ... SET";
6608 case AT_ResetOptions:
6609 return "ALTER COLUMN ... RESET";
6610 case AT_SetStorage:
6611 return "ALTER COLUMN ... SET STORAGE";
6612 case AT_SetCompression:
6613 return "ALTER COLUMN ... SET COMPRESSION";
6614 case AT_DropColumn:
6615 return "DROP COLUMN";
6616 case AT_AddIndex:
6617 case AT_ReAddIndex:
6618 return NULL; /* not real grammar */
6619 case AT_AddConstraint:
6620 case AT_ReAddConstraint:
6623 return "ADD CONSTRAINT";
6624 case AT_AlterConstraint:
6625 return "ALTER CONSTRAINT";
6627 return "VALIDATE CONSTRAINT";
6628 case AT_DropConstraint:
6629 return "DROP CONSTRAINT";
6630 case AT_ReAddComment:
6631 return NULL; /* not real grammar */
6632 case AT_AlterColumnType:
6633 return "ALTER COLUMN ... SET DATA TYPE";
6635 return "ALTER COLUMN ... OPTIONS";
6636 case AT_ChangeOwner:
6637 return "OWNER TO";
6638 case AT_ClusterOn:
6639 return "CLUSTER ON";
6640 case AT_DropCluster:
6641 return "SET WITHOUT CLUSTER";
6642 case AT_SetAccessMethod:
6643 return "SET ACCESS METHOD";
6644 case AT_SetLogged:
6645 return "SET LOGGED";
6646 case AT_SetUnLogged:
6647 return "SET UNLOGGED";
6648 case AT_DropOids:
6649 return "SET WITHOUT OIDS";
6650 case AT_SetTableSpace:
6651 return "SET TABLESPACE";
6652 case AT_SetRelOptions:
6653 return "SET";
6654 case AT_ResetRelOptions:
6655 return "RESET";
6657 return NULL; /* not real grammar */
6658 case AT_EnableTrig:
6659 return "ENABLE TRIGGER";
6661 return "ENABLE ALWAYS TRIGGER";
6663 return "ENABLE REPLICA TRIGGER";
6664 case AT_DisableTrig:
6665 return "DISABLE TRIGGER";
6666 case AT_EnableTrigAll:
6667 return "ENABLE TRIGGER ALL";
6668 case AT_DisableTrigAll:
6669 return "DISABLE TRIGGER ALL";
6670 case AT_EnableTrigUser:
6671 return "ENABLE TRIGGER USER";
6672 case AT_DisableTrigUser:
6673 return "DISABLE TRIGGER USER";
6674 case AT_EnableRule:
6675 return "ENABLE RULE";
6677 return "ENABLE ALWAYS RULE";
6679 return "ENABLE REPLICA RULE";
6680 case AT_DisableRule:
6681 return "DISABLE RULE";
6682 case AT_AddInherit:
6683 return "INHERIT";
6684 case AT_DropInherit:
6685 return "NO INHERIT";
6686 case AT_AddOf:
6687 return "OF";
6688 case AT_DropOf:
6689 return "NOT OF";
6690 case AT_ReplicaIdentity:
6691 return "REPLICA IDENTITY";
6693 return "ENABLE ROW SECURITY";
6695 return "DISABLE ROW SECURITY";
6697 return "FORCE ROW SECURITY";
6699 return "NO FORCE ROW SECURITY";
6700 case AT_GenericOptions:
6701 return "OPTIONS";
6702 case AT_AttachPartition:
6703 return "ATTACH PARTITION";
6704 case AT_DetachPartition:
6705 return "DETACH PARTITION";
6707 return "DETACH PARTITION ... FINALIZE";
6708 case AT_AddIdentity:
6709 return "ALTER COLUMN ... ADD IDENTITY";
6710 case AT_SetIdentity:
6711 return "ALTER COLUMN ... SET";
6712 case AT_DropIdentity:
6713 return "ALTER COLUMN ... DROP IDENTITY";
6714 case AT_ReAddStatistics:
6715 return NULL; /* not real grammar */
6716 }
6717
6718 return NULL;
6719}
6720
6721/*
6722 * ATSimplePermissions
6723 *
6724 * - Ensure that it is a relation (or possibly a view)
6725 * - Ensure this user is the owner
6726 * - Ensure that it is not a system table
6727 */
6728static void
6729ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6730{
6731 int actual_target;
6732
6733 switch (rel->rd_rel->relkind)
6734 {
6735 case RELKIND_RELATION:
6736 actual_target = ATT_TABLE;
6737 break;
6738 case RELKIND_PARTITIONED_TABLE:
6739 actual_target = ATT_PARTITIONED_TABLE;
6740 break;
6741 case RELKIND_VIEW:
6742 actual_target = ATT_VIEW;
6743 break;
6744 case RELKIND_MATVIEW:
6745 actual_target = ATT_MATVIEW;
6746 break;
6747 case RELKIND_INDEX:
6748 actual_target = ATT_INDEX;
6749 break;
6750 case RELKIND_PARTITIONED_INDEX:
6751 actual_target = ATT_PARTITIONED_INDEX;
6752 break;
6753 case RELKIND_COMPOSITE_TYPE:
6754 actual_target = ATT_COMPOSITE_TYPE;
6755 break;
6756 case RELKIND_FOREIGN_TABLE:
6757 actual_target = ATT_FOREIGN_TABLE;
6758 break;
6759 case RELKIND_SEQUENCE:
6760 actual_target = ATT_SEQUENCE;
6761 break;
6762 default:
6763 actual_target = 0;
6764 break;
6765 }
6766
6767 /* Wrong target type? */
6768 if ((actual_target & allowed_targets) == 0)
6769 {
6770 const char *action_str = alter_table_type_to_string(cmdtype);
6771
6772 if (action_str)
6773 ereport(ERROR,
6774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6775 /* translator: %s is a group of some SQL keywords */
6776 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6777 action_str, RelationGetRelationName(rel)),
6778 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6779 else
6780 /* internal error? */
6781 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6783 }
6784
6785 /* Permissions checks */
6786 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6789
6791 ereport(ERROR,
6792 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6793 errmsg("permission denied: \"%s\" is a system catalog",
6795}
6796
6797/*
6798 * ATSimpleRecursion
6799 *
6800 * Simple table recursion sufficient for most ALTER TABLE operations.
6801 * All direct and indirect children are processed in an unspecified order.
6802 * Note that if a child inherits from the original table via multiple
6803 * inheritance paths, it will be visited just once.
6804 */
6805static void
6807 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6808 AlterTableUtilityContext *context)
6809{
6810 /*
6811 * Propagate to children, if desired and if there are (or might be) any
6812 * children.
6813 */
6814 if (recurse && rel->rd_rel->relhassubclass)
6815 {
6816 Oid relid = RelationGetRelid(rel);
6817 ListCell *child;
6818 List *children;
6819
6820 children = find_all_inheritors(relid, lockmode, NULL);
6821
6822 /*
6823 * find_all_inheritors does the recursive search of the inheritance
6824 * hierarchy, so all we have to do is process all of the relids in the
6825 * list that it returns.
6826 */
6827 foreach(child, children)
6828 {
6829 Oid childrelid = lfirst_oid(child);
6830 Relation childrel;
6831
6832 if (childrelid == relid)
6833 continue;
6834 /* find_all_inheritors already got lock */
6835 childrel = relation_open(childrelid, NoLock);
6836 CheckAlterTableIsSafe(childrel);
6837 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6838 relation_close(childrel, NoLock);
6839 }
6840 }
6841}
6842
6843/*
6844 * Obtain list of partitions of the given table, locking them all at the given
6845 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6846 *
6847 * This function is a no-op if the given relation is not a partitioned table;
6848 * in particular, nothing is done if it's a legacy inheritance parent.
6849 */
6850static void
6852{
6853 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6854 {
6855 List *inh;
6856 ListCell *cell;
6857
6858 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6859 /* first element is the parent rel; must ignore it */
6860 for_each_from(cell, inh, 1)
6861 {
6862 Relation childrel;
6863
6864 /* find_all_inheritors already got lock */
6865 childrel = table_open(lfirst_oid(cell), NoLock);
6866 CheckAlterTableIsSafe(childrel);
6867 table_close(childrel, NoLock);
6868 }
6869 list_free(inh);
6870 }
6871}
6872
6873/*
6874 * ATTypedTableRecursion
6875 *
6876 * Propagate ALTER TYPE operations to the typed tables of that type.
6877 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6878 * recursion to inheritance children of the typed tables.
6879 */
6880static void
6882 LOCKMODE lockmode, AlterTableUtilityContext *context)
6883{
6884 ListCell *child;
6885 List *children;
6886
6887 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6888
6889 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6891 cmd->behavior);
6892
6893 foreach(child, children)
6894 {
6895 Oid childrelid = lfirst_oid(child);
6896 Relation childrel;
6897
6898 childrel = relation_open(childrelid, lockmode);
6899 CheckAlterTableIsSafe(childrel);
6900 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6901 relation_close(childrel, NoLock);
6902 }
6903}
6904
6905
6906/*
6907 * find_composite_type_dependencies
6908 *
6909 * Check to see if the type "typeOid" is being used as a column in some table
6910 * (possibly nested several levels deep in composite types, arrays, etc!).
6911 * Eventually, we'd like to propagate the check or rewrite operation
6912 * into such tables, but for now, just error out if we find any.
6913 *
6914 * Caller should provide either the associated relation of a rowtype,
6915 * or a type name (not both) for use in the error message, if any.
6916 *
6917 * Note that "typeOid" is not necessarily a composite type; it could also be
6918 * another container type such as an array or range, or a domain over one of
6919 * these things. The name of this function is therefore somewhat historical,
6920 * but it's not worth changing.
6921 *
6922 * We assume that functions and views depending on the type are not reasons
6923 * to reject the ALTER. (How safe is this really?)
6924 */
6925void
6927 const char *origTypeName)
6928{
6929 Relation depRel;
6930 ScanKeyData key[2];
6931 SysScanDesc depScan;
6932 HeapTuple depTup;
6933
6934 /* since this function recurses, it could be driven to stack overflow */
6936
6937 /*
6938 * We scan pg_depend to find those things that depend on the given type.
6939 * (We assume we can ignore refobjsubid for a type.)
6940 */
6941 depRel = table_open(DependRelationId, AccessShareLock);
6942
6943 ScanKeyInit(&key[0],
6944 Anum_pg_depend_refclassid,
6945 BTEqualStrategyNumber, F_OIDEQ,
6946 ObjectIdGetDatum(TypeRelationId));
6947 ScanKeyInit(&key[1],
6948 Anum_pg_depend_refobjid,
6949 BTEqualStrategyNumber, F_OIDEQ,
6950 ObjectIdGetDatum(typeOid));
6951
6952 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6953 NULL, 2, key);
6954
6955 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6956 {
6957 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6958 Relation rel;
6959 TupleDesc tupleDesc;
6961
6962 /* Check for directly dependent types */
6963 if (pg_depend->classid == TypeRelationId)
6964 {
6965 /*
6966 * This must be an array, domain, or range containing the given
6967 * type, so recursively check for uses of this type. Note that
6968 * any error message will mention the original type not the
6969 * container; this is intentional.
6970 */
6971 find_composite_type_dependencies(pg_depend->objid,
6972 origRelation, origTypeName);
6973 continue;
6974 }
6975
6976 /* Else, ignore dependees that aren't relations */
6977 if (pg_depend->classid != RelationRelationId)
6978 continue;
6979
6980 rel = relation_open(pg_depend->objid, AccessShareLock);
6981 tupleDesc = RelationGetDescr(rel);
6982
6983 /*
6984 * If objsubid identifies a specific column, refer to that in error
6985 * messages. Otherwise, search to see if there's a user column of the
6986 * type. (We assume system columns are never of interesting types.)
6987 * The search is needed because an index containing an expression
6988 * column of the target type will just be recorded as a whole-relation
6989 * dependency. If we do not find a column of the type, the dependency
6990 * must indicate that the type is transiently referenced in an index
6991 * expression but not stored on disk, which we assume is OK, just as
6992 * we do for references in views. (It could also be that the target
6993 * type is embedded in some container type that is stored in an index
6994 * column, but the previous recursion should catch such cases.)
6995 */
6996 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6997 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6998 else
6999 {
7000 att = NULL;
7001 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7002 {
7003 att = TupleDescAttr(tupleDesc, attno - 1);
7004 if (att->atttypid == typeOid && !att->attisdropped)
7005 break;
7006 att = NULL;
7007 }
7008 if (att == NULL)
7009 {
7010 /* No such column, so assume OK */
7012 continue;
7013 }
7014 }
7015
7016 /*
7017 * We definitely should reject if the relation has storage. If it's
7018 * partitioned, then perhaps we don't have to reject: if there are
7019 * partitions then we'll fail when we find one, else there is no
7020 * stored data to worry about. However, it's possible that the type
7021 * change would affect conclusions about whether the type is sortable
7022 * or hashable and thus (if it's a partitioning column) break the
7023 * partitioning rule. For now, reject for partitioned rels too.
7024 */
7025 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7026 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7027 {
7028 if (origTypeName)
7029 ereport(ERROR,
7030 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7031 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7032 origTypeName,
7034 NameStr(att->attname))));
7035 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7036 ereport(ERROR,
7037 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7038 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7039 RelationGetRelationName(origRelation),
7041 NameStr(att->attname))));
7042 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7043 ereport(ERROR,
7044 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7045 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7046 RelationGetRelationName(origRelation),
7048 NameStr(att->attname))));
7049 else
7050 ereport(ERROR,
7051 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7052 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7053 RelationGetRelationName(origRelation),
7055 NameStr(att->attname))));
7056 }
7057 else if (OidIsValid(rel->rd_rel->reltype))
7058 {
7059 /*
7060 * A view or composite type itself isn't a problem, but we must
7061 * recursively check for indirect dependencies via its rowtype.
7062 */
7064 origRelation, origTypeName);
7065 }
7066
7068 }
7069
7070 systable_endscan(depScan);
7071
7073}
7074
7075
7076/*
7077 * find_typed_table_dependencies
7078 *
7079 * Check to see if a composite type is being used as the type of a
7080 * typed table. Abort if any are found and behavior is RESTRICT.
7081 * Else return the list of tables.
7082 */
7083static List *
7084find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
7085{
7086 Relation classRel;
7087 ScanKeyData key[1];
7088 TableScanDesc scan;
7089 HeapTuple tuple;
7090 List *result = NIL;
7091
7092 classRel = table_open(RelationRelationId, AccessShareLock);
7093
7094 ScanKeyInit(&key[0],
7095 Anum_pg_class_reloftype,
7096 BTEqualStrategyNumber, F_OIDEQ,
7097 ObjectIdGetDatum(typeOid));
7098
7099 scan = table_beginscan_catalog(classRel, 1, key);
7100
7101 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7102 {
7103 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
7104
7105 if (behavior == DROP_RESTRICT)
7106 ereport(ERROR,
7107 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
7108 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7109 typeName),
7110 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7111 else
7112 result = lappend_oid(result, classform->oid);
7113 }
7114
7115 table_endscan(scan);
7116 table_close(classRel, AccessShareLock);
7117
7118 return result;
7119}
7120
7121
7122/*
7123 * check_of_type
7124 *
7125 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7126 * isn't suitable, throw an error. Currently, we require that the type
7127 * originated with CREATE TYPE AS. We could support any row type, but doing so
7128 * would require handling a number of extra corner cases in the DDL commands.
7129 * (Also, allowing domain-over-composite would open up a can of worms about
7130 * whether and how the domain's constraints should apply to derived tables.)
7131 */
7132void
7134{
7135 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7136 bool typeOk = false;
7137
7138 if (typ->typtype == TYPTYPE_COMPOSITE)
7139 {
7140 Relation typeRelation;
7141
7142 Assert(OidIsValid(typ->typrelid));
7143 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7144 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7145
7146 /*
7147 * Close the parent rel, but keep our AccessShareLock on it until xact
7148 * commit. That will prevent someone else from deleting or ALTERing
7149 * the type before the typed table creation/conversion commits.
7150 */
7151 relation_close(typeRelation, NoLock);
7152
7153 if (!typeOk)
7154 ereport(ERROR,
7155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7156 errmsg("type %s is the row type of another table",
7157 format_type_be(typ->oid)),
7158 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7159 }
7160 else
7161 ereport(ERROR,
7162 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7163 errmsg("type %s is not a composite type",
7164 format_type_be(typ->oid))));
7165}
7166
7167
7168/*
7169 * ALTER TABLE ADD COLUMN
7170 *
7171 * Adds an additional attribute to a relation making the assumption that
7172 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7173 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7174 * AlterTableCmd's.
7175 *
7176 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7177 * have to decide at runtime whether to recurse or not depending on whether we
7178 * actually add a column or merely merge with an existing column. (We can't
7179 * check this in a static pre-pass because it won't handle multiple inheritance
7180 * situations correctly.)
7181 */
7182static void
7183ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7184 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7185 AlterTableUtilityContext *context)
7186{
7187 if (rel->rd_rel->reloftype && !recursing)
7188 ereport(ERROR,
7189 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7190 errmsg("cannot add column to typed table")));
7191
7192 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7193 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7194
7195 if (recurse && !is_view)
7196 cmd->recurse = true;
7197}
7198
7199/*
7200 * Add a column to a table. The return value is the address of the
7201 * new column in the parent relation.
7202 *
7203 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7204 * copy (but that happens only after we check for IF NOT EXISTS).
7205 */
7206static ObjectAddress
7208 AlterTableCmd **cmd, bool recurse, bool recursing,
7209 LOCKMODE lockmode, AlterTablePass cur_pass,
7210 AlterTableUtilityContext *context)
7211{
7212 Oid myrelid = RelationGetRelid(rel);
7213 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7214 bool if_not_exists = (*cmd)->missing_ok;
7215 Relation pgclass,
7216 attrdesc;
7217 HeapTuple reltup;
7218 Form_pg_class relform;
7219 Form_pg_attribute attribute;
7220 int newattnum;
7221 char relkind;
7222 Expr *defval;
7223 List *children;
7224 ListCell *child;
7225 AlterTableCmd *childcmd;
7226 ObjectAddress address;
7227 TupleDesc tupdesc;
7228
7229 /* since this function recurses, it could be driven to stack overflow */
7231
7232 /* At top level, permission check was done in ATPrepCmd, else do it */
7233 if (recursing)
7234 ATSimplePermissions((*cmd)->subtype, rel,
7236
7237 if (rel->rd_rel->relispartition && !recursing)
7238 ereport(ERROR,
7239 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7240 errmsg("cannot add column to a partition")));
7241
7242 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7243
7244 /*
7245 * Are we adding the column to a recursion child? If so, check whether to
7246 * merge with an existing definition for the column. If we do merge, we
7247 * must not recurse. Children will already have the column, and recursing
7248 * into them would mess up attinhcount.
7249 */
7250 if (colDef->inhcount > 0)
7251 {
7252 HeapTuple tuple;
7253
7254 /* Does child already have a column by this name? */
7255 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7256 if (HeapTupleIsValid(tuple))
7257 {
7258 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7259 Oid ctypeId;
7260 int32 ctypmod;
7261 Oid ccollid;
7262
7263 /* Child column must match on type, typmod, and collation */
7264 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7265 if (ctypeId != childatt->atttypid ||
7266 ctypmod != childatt->atttypmod)
7267 ereport(ERROR,
7268 (errcode(ERRCODE_DATATYPE_MISMATCH),
7269 errmsg("child table \"%s\" has different type for column \"%s\"",
7270 RelationGetRelationName(rel), colDef->colname)));
7271 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7272 if (ccollid != childatt->attcollation)
7273 ereport(ERROR,
7274 (errcode(ERRCODE_COLLATION_MISMATCH),
7275 errmsg("child table \"%s\" has different collation for column \"%s\"",
7276 RelationGetRelationName(rel), colDef->colname),
7277 errdetail("\"%s\" versus \"%s\"",
7278 get_collation_name(ccollid),
7279 get_collation_name(childatt->attcollation))));
7280
7281 /* Bump the existing child att's inhcount */
7282 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7283 &childatt->attinhcount))
7284 ereport(ERROR,
7285 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7286 errmsg("too many inheritance parents"));
7287 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7288
7289 heap_freetuple(tuple);
7290
7291 /* Inform the user about the merge */
7293 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7294 colDef->colname, RelationGetRelationName(rel))));
7295
7296 table_close(attrdesc, RowExclusiveLock);
7297
7298 /* Make the child column change visible */
7300
7301 return InvalidObjectAddress;
7302 }
7303 }
7304
7305 /* skip if the name already exists and if_not_exists is true */
7306 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7307 {
7308 table_close(attrdesc, RowExclusiveLock);
7309 return InvalidObjectAddress;
7310 }
7311
7312 /*
7313 * Okay, we need to add the column, so go ahead and do parse
7314 * transformation. This can result in queueing up, or even immediately
7315 * executing, subsidiary operations (such as creation of unique indexes);
7316 * so we mustn't do it until we have made the if_not_exists check.
7317 *
7318 * When recursing, the command was already transformed and we needn't do
7319 * so again. Also, if context isn't given we can't transform. (That
7320 * currently happens only for AT_AddColumnToView; we expect that view.c
7321 * passed us a ColumnDef that doesn't need work.)
7322 */
7323 if (context != NULL && !recursing)
7324 {
7325 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7326 cur_pass, context);
7327 Assert(*cmd != NULL);
7328 colDef = castNode(ColumnDef, (*cmd)->def);
7329 }
7330
7331 /*
7332 * Regular inheritance children are independent enough not to inherit the
7333 * identity column from parent hence cannot recursively add identity
7334 * column if the table has inheritance children.
7335 *
7336 * Partitions, on the other hand, are integral part of a partitioned table
7337 * and inherit identity column. Hence propagate identity column down the
7338 * partition hierarchy.
7339 */
7340 if (colDef->identity &&
7341 recurse &&
7342 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7344 ereport(ERROR,
7345 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7346 errmsg("cannot recursively add identity column to table that has child tables")));
7347
7348 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7349
7350 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7351 if (!HeapTupleIsValid(reltup))
7352 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7353 relform = (Form_pg_class) GETSTRUCT(reltup);
7354 relkind = relform->relkind;
7355
7356 /* Determine the new attribute's number */
7357 newattnum = relform->relnatts + 1;
7358 if (newattnum > MaxHeapAttributeNumber)
7359 ereport(ERROR,
7360 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7361 errmsg("tables can have at most %d columns",
7363
7364 /*
7365 * Construct new attribute's pg_attribute entry.
7366 */
7367 tupdesc = BuildDescForRelation(list_make1(colDef));
7368
7369 attribute = TupleDescAttr(tupdesc, 0);
7370
7371 /* Fix up attribute number */
7372 attribute->attnum = newattnum;
7373
7374 /* make sure datatype is legal for a column */
7375 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7376 list_make1_oid(rel->rd_rel->reltype),
7377 0);
7378
7379 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7380
7381 table_close(attrdesc, RowExclusiveLock);
7382
7383 /*
7384 * Update pg_class tuple as appropriate
7385 */
7386 relform->relnatts = newattnum;
7387
7388 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7389
7390 heap_freetuple(reltup);
7391
7392 /* Post creation hook for new attribute */
7393 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7394
7395 table_close(pgclass, RowExclusiveLock);
7396
7397 /* Make the attribute's catalog entry visible */
7399
7400 /*
7401 * Store the DEFAULT, if any, in the catalogs
7402 */
7403 if (colDef->raw_default)
7404 {
7405 RawColumnDefault *rawEnt;
7406
7407 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7408 rawEnt->attnum = attribute->attnum;
7409 rawEnt->raw_default = copyObject(colDef->raw_default);
7410 rawEnt->generated = colDef->generated;
7411
7412 /*
7413 * This function is intended for CREATE TABLE, so it processes a
7414 * _list_ of defaults, but we just do one.
7415 */
7417 false, true, false, NULL);
7418
7419 /* Make the additional catalog changes visible */
7421 }
7422
7423 /*
7424 * Tell Phase 3 to fill in the default expression, if there is one.
7425 *
7426 * If there is no default, Phase 3 doesn't have to do anything, because
7427 * that effectively means that the default is NULL. The heap tuple access
7428 * routines always check for attnum > # of attributes in tuple, and return
7429 * NULL if so, so without any modification of the tuple data we will get
7430 * the effect of NULL values in the new column.
7431 *
7432 * An exception occurs when the new column is of a domain type: the domain
7433 * might have a not-null constraint, or a check constraint that indirectly
7434 * rejects nulls. If there are any domain constraints then we construct
7435 * an explicit NULL default value that will be passed through
7436 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7437 * rewriting the table which we really wouldn't have to do; but we do it
7438 * to preserve the historical behavior that such a failure will be raised
7439 * only if the table currently contains some rows.)
7440 *
7441 * Note: we use build_column_default, and not just the cooked default
7442 * returned by AddRelationNewConstraints, so that the right thing happens
7443 * when a datatype's default applies.
7444 *
7445 * Note: it might seem that this should happen at the end of Phase 2, so
7446 * that the effects of subsequent subcommands can be taken into account.
7447 * It's intentional that we do it now, though. The new column should be
7448 * filled according to what is said in the ADD COLUMN subcommand, so that
7449 * the effects are the same as if this subcommand had been run by itself
7450 * and the later subcommands had been issued in new ALTER TABLE commands.
7451 *
7452 * We can skip this entirely for relations without storage, since Phase 3
7453 * is certainly not going to touch them.
7454 */
7455 if (RELKIND_HAS_STORAGE(relkind))
7456 {
7457 bool has_domain_constraints;
7458 bool has_missing = false;
7459
7460 /*
7461 * For an identity column, we can't use build_column_default(),
7462 * because the sequence ownership isn't set yet. So do it manually.
7463 */
7464 if (colDef->identity)
7465 {
7467
7468 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7469 nve->typeId = attribute->atttypid;
7470
7471 defval = (Expr *) nve;
7472 }
7473 else
7474 defval = (Expr *) build_column_default(rel, attribute->attnum);
7475
7476 /* Build CoerceToDomain(NULL) expression if needed */
7477 has_domain_constraints = DomainHasConstraints(attribute->atttypid);
7478 if (!defval && has_domain_constraints)
7479 {
7480 Oid baseTypeId;
7481 int32 baseTypeMod;
7482 Oid baseTypeColl;
7483
7484 baseTypeMod = attribute->atttypmod;
7485 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7486 baseTypeColl = get_typcollation(baseTypeId);
7487 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7488 defval = (Expr *) coerce_to_target_type(NULL,
7489 (Node *) defval,
7490 baseTypeId,
7491 attribute->atttypid,
7492 attribute->atttypmod,
7495 -1);
7496 if (defval == NULL) /* should not happen */
7497 elog(ERROR, "failed to coerce base type to domain");
7498 }
7499
7500 if (defval)
7501 {
7503
7504 /* Prepare defval for execution, either here or in Phase 3 */
7505 defval = expression_planner(defval);
7506
7507 /* Add the new default to the newvals list */
7509 newval->attnum = attribute->attnum;
7510 newval->expr = defval;
7511 newval->is_generated = (colDef->generated != '\0');
7512
7513 tab->newvals = lappend(tab->newvals, newval);
7514
7515 /*
7516 * Attempt to skip a complete table rewrite by storing the
7517 * specified DEFAULT value outside of the heap. This is only
7518 * allowed for plain relations and non-generated columns, and the
7519 * default expression can't be volatile (stable is OK). Note that
7520 * contain_volatile_functions deems CoerceToDomain immutable, but
7521 * here we consider that coercion to a domain with constraints is
7522 * volatile; else it might fail even when the table is empty.
7523 */
7524 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7525 !colDef->generated &&
7526 !has_domain_constraints &&
7527 !contain_volatile_functions((Node *) defval))
7528 {
7529 EState *estate;
7530 ExprState *exprState;
7531 Datum missingval;
7532 bool missingIsNull;
7533
7534 /* Evaluate the default expression */
7535 estate = CreateExecutorState();
7536 exprState = ExecPrepareExpr(defval, estate);
7537 missingval = ExecEvalExpr(exprState,
7538 GetPerTupleExprContext(estate),
7539 &missingIsNull);
7540 /* If it turns out NULL, nothing to do; else store it */
7541 if (!missingIsNull)
7542 {
7543 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7544 /* Make the additional catalog change visible */
7546 has_missing = true;
7547 }
7548 FreeExecutorState(estate);
7549 }
7550 else
7551 {
7552 /*
7553 * Failed to use missing mode. We have to do a table rewrite
7554 * to install the value --- unless it's a virtual generated
7555 * column.
7556 */
7557 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7559 }
7560 }
7561
7562 if (!has_missing)
7563 {
7564 /*
7565 * If the new column is NOT NULL, and there is no missing value,
7566 * tell Phase 3 it needs to check for NULLs.
7567 */
7568 tab->verify_new_notnull |= colDef->is_not_null;
7569 }
7570 }
7571
7572 /*
7573 * Add needed dependency entries for the new column.
7574 */
7575 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7576 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7577
7578 /*
7579 * Propagate to children as appropriate. Unlike most other ALTER
7580 * routines, we have to do this one level of recursion at a time; we can't
7581 * use find_all_inheritors to do it in one pass.
7582 */
7583 children =
7585
7586 /*
7587 * If we are told not to recurse, there had better not be any child
7588 * tables; else the addition would put them out of step.
7589 */
7590 if (children && !recurse)
7591 ereport(ERROR,
7592 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7593 errmsg("column must be added to child tables too")));
7594
7595 /* Children should see column as singly inherited */
7596 if (!recursing)
7597 {
7598 childcmd = copyObject(*cmd);
7599 colDef = castNode(ColumnDef, childcmd->def);
7600 colDef->inhcount = 1;
7601 colDef->is_local = false;
7602 }
7603 else
7604 childcmd = *cmd; /* no need to copy again */
7605
7606 foreach(child, children)
7607 {
7608 Oid childrelid = lfirst_oid(child);
7609 Relation childrel;
7610 AlteredTableInfo *childtab;
7611
7612 /* find_inheritance_children already got lock */
7613 childrel = table_open(childrelid, NoLock);
7614 CheckAlterTableIsSafe(childrel);
7615
7616 /* Find or create work queue entry for this table */
7617 childtab = ATGetQueueEntry(wqueue, childrel);
7618
7619 /* Recurse to child; return value is ignored */
7620 ATExecAddColumn(wqueue, childtab, childrel,
7621 &childcmd, recurse, true,
7622 lockmode, cur_pass, context);
7623
7624 table_close(childrel, NoLock);
7625 }
7626
7627 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7628 return address;
7629}
7630
7631/*
7632 * If a new or renamed column will collide with the name of an existing
7633 * column and if_not_exists is false then error out, else do nothing.
7634 */
7635static bool
7637 bool if_not_exists)
7638{
7639 HeapTuple attTuple;
7640 int attnum;
7641
7642 /*
7643 * this test is deliberately not attisdropped-aware, since if one tries to
7644 * add a column matching a dropped column name, it's gonna fail anyway.
7645 */
7646 attTuple = SearchSysCache2(ATTNAME,
7648 PointerGetDatum(colname));
7649 if (!HeapTupleIsValid(attTuple))
7650 return true;
7651
7652 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7653 ReleaseSysCache(attTuple);
7654
7655 /*
7656 * We throw a different error message for conflicts with system column
7657 * names, since they are normally not shown and the user might otherwise
7658 * be confused about the reason for the conflict.
7659 */
7660 if (attnum <= 0)
7661 ereport(ERROR,
7662 (errcode(ERRCODE_DUPLICATE_COLUMN),
7663 errmsg("column name \"%s\" conflicts with a system column name",
7664 colname)));
7665 else
7666 {
7667 if (if_not_exists)
7668 {
7670 (errcode(ERRCODE_DUPLICATE_COLUMN),
7671 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7672 colname, RelationGetRelationName(rel))));
7673 return false;
7674 }
7675
7676 ereport(ERROR,
7677 (errcode(ERRCODE_DUPLICATE_COLUMN),
7678 errmsg("column \"%s\" of relation \"%s\" already exists",
7679 colname, RelationGetRelationName(rel))));
7680 }
7681
7682 return true;
7683}
7684
7685/*
7686 * Install a column's dependency on its datatype.
7687 */
7688static void
7690{
7691 ObjectAddress myself,
7692 referenced;
7693
7694 myself.classId = RelationRelationId;
7695 myself.objectId = relid;
7696 myself.objectSubId = attnum;
7697 referenced.classId = TypeRelationId;
7698 referenced.objectId = typid;
7699 referenced.objectSubId = 0;
7700 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7701}
7702
7703/*
7704 * Install a column's dependency on its collation.
7705 */
7706static void
7708{
7709 ObjectAddress myself,
7710 referenced;
7711
7712 /* We know the default collation is pinned, so don't bother recording it */
7713 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7714 {
7715 myself.classId = RelationRelationId;
7716 myself.objectId = relid;
7717 myself.objectSubId = attnum;
7718 referenced.classId = CollationRelationId;
7719 referenced.objectId = collid;
7720 referenced.objectSubId = 0;
7721 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7722 }
7723}
7724
7725/*
7726 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7727 *
7728 * Return the address of the modified column. If the column was already
7729 * nullable, InvalidObjectAddress is returned.
7730 */
7731static ObjectAddress
7732ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7733 LOCKMODE lockmode)
7734{
7735 HeapTuple tuple;
7736 HeapTuple conTup;
7737 Form_pg_attribute attTup;
7739 Relation attr_rel;
7740 ObjectAddress address;
7741
7742 /*
7743 * lookup the attribute
7744 */
7745 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7746
7747 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7748 if (!HeapTupleIsValid(tuple))
7749 ereport(ERROR,
7750 (errcode(ERRCODE_UNDEFINED_COLUMN),
7751 errmsg("column \"%s\" of relation \"%s\" does not exist",
7752 colName, RelationGetRelationName(rel))));
7753 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7754 attnum = attTup->attnum;
7755 ObjectAddressSubSet(address, RelationRelationId,
7756 RelationGetRelid(rel), attnum);
7757
7758 /* If the column is already nullable there's nothing to do. */
7759 if (!attTup->attnotnull)
7760 {
7761 table_close(attr_rel, RowExclusiveLock);
7762 return InvalidObjectAddress;
7763 }
7764
7765 /* Prevent them from altering a system attribute */
7766 if (attnum <= 0)
7767 ereport(ERROR,
7768 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7769 errmsg("cannot alter system column \"%s\"",
7770 colName)));
7771
7772 if (attTup->attidentity)
7773 ereport(ERROR,
7774 (errcode(ERRCODE_SYNTAX_ERROR),
7775 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7776 colName, RelationGetRelationName(rel))));
7777
7778 /*
7779 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7780 */
7781 if (rel->rd_rel->relispartition)
7782 {
7783 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7784 Relation parent = table_open(parentId, AccessShareLock);
7785 TupleDesc tupDesc = RelationGetDescr(parent);
7786 AttrNumber parent_attnum;
7787
7788 parent_attnum = get_attnum(parentId, colName);
7789 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7790 ereport(ERROR,
7791 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7792 errmsg("column \"%s\" is marked NOT NULL in parent table",
7793 colName)));
7795 }
7796
7797 /*
7798 * Find the constraint that makes this column NOT NULL, and drop it.
7799 * dropconstraint_internal() resets attnotnull.
7800 */
7802 if (conTup == NULL)
7803 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7804 colName, RelationGetRelationName(rel));
7805
7806 /* The normal case: we have a pg_constraint row, remove it */
7807 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7808 false, lockmode);
7809 heap_freetuple(conTup);
7810
7811 InvokeObjectPostAlterHook(RelationRelationId,
7812 RelationGetRelid(rel), attnum);
7813
7814 table_close(attr_rel, RowExclusiveLock);
7815
7816 return address;
7817}
7818
7819/*
7820 * set_attnotnull
7821 * Helper to update/validate the pg_attribute status of a not-null
7822 * constraint
7823 *
7824 * pg_attribute.attnotnull is set true, if it isn't already.
7825 * If queue_validation is true, also set up wqueue to validate the constraint.
7826 * wqueue may be given as NULL when validation is not needed (e.g., on table
7827 * creation).
7828 */
7829static void
7831 bool is_valid, bool queue_validation)
7832{
7833 Form_pg_attribute attr;
7834 CompactAttribute *thisatt;
7835
7836 Assert(!queue_validation || wqueue);
7837
7839
7840 /*
7841 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7842 * attribute.
7843 */
7844 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7845 if (attr->attisdropped)
7846 return;
7847
7848 if (!attr->attnotnull)
7849 {
7850 Relation attr_rel;
7851 HeapTuple tuple;
7852
7853 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7854
7856 if (!HeapTupleIsValid(tuple))
7857 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7858 attnum, RelationGetRelid(rel));
7859
7860 thisatt = TupleDescCompactAttr(RelationGetDescr(rel), attnum - 1);
7862
7863 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7864
7865 attr->attnotnull = true;
7866 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7867
7868 /*
7869 * If the nullness isn't already proven by validated constraints, have
7870 * ALTER TABLE phase 3 test for it.
7871 */
7872 if (queue_validation && wqueue &&
7874 {
7875 AlteredTableInfo *tab;
7876
7877 tab = ATGetQueueEntry(wqueue, rel);
7878 tab->verify_new_notnull = true;
7879 }
7880
7882
7883 table_close(attr_rel, RowExclusiveLock);
7884 heap_freetuple(tuple);
7885 }
7886 else
7887 {
7889 }
7890}
7891
7892/*
7893 * ALTER TABLE ALTER COLUMN SET NOT NULL
7894 *
7895 * Add a not-null constraint to a single table and its children. Returns
7896 * the address of the constraint added to the parent relation, if one gets
7897 * added, or InvalidObjectAddress otherwise.
7898 *
7899 * We must recurse to child tables during execution, rather than using
7900 * ALTER TABLE's normal prep-time recursion.
7901 */
7902static ObjectAddress
7903ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7904 bool recurse, bool recursing, LOCKMODE lockmode)
7905{
7906 HeapTuple tuple;
7908 ObjectAddress address;
7909 Constraint *constraint;
7910 CookedConstraint *ccon;
7911 List *cooked;
7912 bool is_no_inherit = false;
7913
7914 /* Guard against stack overflow due to overly deep inheritance tree. */
7916
7917 /* At top level, permission check was done in ATPrepCmd, else do it */
7918 if (recursing)
7919 {
7922 Assert(conName != NULL);
7923 }
7924
7925 attnum = get_attnum(RelationGetRelid(rel), colName);
7927 ereport(ERROR,
7928 (errcode(ERRCODE_UNDEFINED_COLUMN),
7929 errmsg("column \"%s\" of relation \"%s\" does not exist",
7930 colName, RelationGetRelationName(rel))));
7931
7932 /* Prevent them from altering a system attribute */
7933 if (attnum <= 0)
7934 ereport(ERROR,
7935 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7936 errmsg("cannot alter system column \"%s\"",
7937 colName)));
7938
7939 /* See if there's already a constraint */
7941 if (HeapTupleIsValid(tuple))
7942 {
7944 bool changed = false;
7945
7946 /*
7947 * Don't let a NO INHERIT constraint be changed into inherit.
7948 */
7949 if (conForm->connoinherit && recurse)
7950 ereport(ERROR,
7951 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7952 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7953 NameStr(conForm->conname),
7955
7956 /*
7957 * If we find an appropriate constraint, we're almost done, but just
7958 * need to change some properties on it: if we're recursing, increment
7959 * coninhcount; if not, set conislocal if not already set.
7960 */
7961 if (recursing)
7962 {
7963 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7964 &conForm->coninhcount))
7965 ereport(ERROR,
7966 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7967 errmsg("too many inheritance parents"));
7968 changed = true;
7969 }
7970 else if (!conForm->conislocal)
7971 {
7972 conForm->conislocal = true;
7973 changed = true;
7974 }
7975 else if (!conForm->convalidated)
7976 {
7977 /*
7978 * Flip attnotnull and convalidated, and also validate the
7979 * constraint.
7980 */
7981 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
7982 recurse, recursing, lockmode);
7983 }
7984
7985 if (changed)
7986 {
7987 Relation constr_rel;
7988
7989 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7990
7991 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7992 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7993 table_close(constr_rel, RowExclusiveLock);
7994 }
7995
7996 if (changed)
7997 return address;
7998 else
7999 return InvalidObjectAddress;
8000 }
8001
8002 /*
8003 * If we're asked not to recurse, and children exist, raise an error for
8004 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8005 * specified.
8006 */
8007 if (!recurse &&
8009 NoLock) != NIL)
8010 {
8011 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8012 ereport(ERROR,
8013 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8014 errmsg("constraint must be added to child tables too"),
8015 errhint("Do not specify the ONLY keyword."));
8016 else
8017 is_no_inherit = true;
8018 }
8019
8020 /*
8021 * No constraint exists; we must add one. First determine a name to use,
8022 * if we haven't already.
8023 */
8024 if (!recursing)
8025 {
8026 Assert(conName == NULL);
8028 colName, "not_null",
8030 NIL);
8031 }
8032
8033 constraint = makeNotNullConstraint(makeString(colName));
8034 constraint->is_no_inherit = is_no_inherit;
8035 constraint->conname = conName;
8036
8037 /* and do it */
8038 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8039 false, !recursing, false, NULL);
8040 ccon = linitial(cooked);
8041 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8042
8043 InvokeObjectPostAlterHook(RelationRelationId,
8044 RelationGetRelid(rel), attnum);
8045
8046 /* Mark pg_attribute.attnotnull for the column and queue validation */
8047 set_attnotnull(wqueue, rel, attnum, true, true);
8048
8049 /*
8050 * Recurse to propagate the constraint to children that don't have one.
8051 */
8052 if (recurse)
8053 {
8054 List *children;
8055
8057 lockmode);
8058
8059 foreach_oid(childoid, children)
8060 {
8061 Relation childrel = table_open(childoid, NoLock);
8062
8064
8065 ATExecSetNotNull(wqueue, childrel, conName, colName,
8066 recurse, true, lockmode);
8067 table_close(childrel, NoLock);
8068 }
8069 }
8070
8071 return address;
8072}
8073
8074/*
8075 * NotNullImpliedByRelConstraints
8076 * Does rel's existing constraints imply NOT NULL for the given attribute?
8077 */
8078static bool
8080{
8081 NullTest *nnulltest = makeNode(NullTest);
8082
8083 nnulltest->arg = (Expr *) makeVar(1,
8084 attr->attnum,
8085 attr->atttypid,
8086 attr->atttypmod,
8087 attr->attcollation,
8088 0);
8089 nnulltest->nulltesttype = IS_NOT_NULL;
8090
8091 /*
8092 * argisrow = false is correct even for a composite column, because
8093 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8094 * case, just IS DISTINCT FROM NULL.
8095 */
8096 nnulltest->argisrow = false;
8097 nnulltest->location = -1;
8098
8099 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8100 {
8102 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8103 RelationGetRelationName(rel), NameStr(attr->attname))));
8104 return true;
8105 }
8106
8107 return false;
8108}
8109
8110/*
8111 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8112 *
8113 * Return the address of the affected column.
8114 */
8115static ObjectAddress
8116ATExecColumnDefault(Relation rel, const char *colName,
8117 Node *newDefault, LOCKMODE lockmode)
8118{
8119 TupleDesc tupdesc = RelationGetDescr(rel);
8121 ObjectAddress address;
8122
8123 /*
8124 * get the number of the attribute
8125 */
8126 attnum = get_attnum(RelationGetRelid(rel), colName);
8128 ereport(ERROR,
8129 (errcode(ERRCODE_UNDEFINED_COLUMN),
8130 errmsg("column \"%s\" of relation \"%s\" does not exist",
8131 colName, RelationGetRelationName(rel))));
8132
8133 /* Prevent them from altering a system attribute */
8134 if (attnum <= 0)
8135 ereport(ERROR,
8136 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8137 errmsg("cannot alter system column \"%s\"",
8138 colName)));
8139
8140 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8141 ereport(ERROR,
8142 (errcode(ERRCODE_SYNTAX_ERROR),
8143 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8144 colName, RelationGetRelationName(rel)),
8145 /* translator: %s is an SQL ALTER command */
8146 newDefault ? 0 : errhint("Use %s instead.",
8147 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8148
8149 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8150 ereport(ERROR,
8151 (errcode(ERRCODE_SYNTAX_ERROR),
8152 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8153 colName, RelationGetRelationName(rel)),
8154 newDefault ?
8155 /* translator: %s is an SQL ALTER command */
8156 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8157 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8158 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8159
8160 /*
8161 * Remove any old default for the column. We use RESTRICT here for
8162 * safety, but at present we do not expect anything to depend on the
8163 * default.
8164 *
8165 * We treat removing the existing default as an internal operation when it
8166 * is preparatory to adding a new default, but as a user-initiated
8167 * operation when the user asked for a drop.
8168 */
8170 newDefault != NULL);
8171
8172 if (newDefault)
8173 {
8174 /* SET DEFAULT */
8175 RawColumnDefault *rawEnt;
8176
8177 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8178 rawEnt->attnum = attnum;
8179 rawEnt->raw_default = newDefault;
8180 rawEnt->generated = '\0';
8181
8182 /*
8183 * This function is intended for CREATE TABLE, so it processes a
8184 * _list_ of defaults, but we just do one.
8185 */
8187 false, true, false, NULL);
8188 }
8189
8190 ObjectAddressSubSet(address, RelationRelationId,
8191 RelationGetRelid(rel), attnum);
8192 return address;
8193}
8194
8195/*
8196 * Add a pre-cooked default expression.
8197 *
8198 * Return the address of the affected column.
8199 */
8200static ObjectAddress
8202 Node *newDefault)
8203{
8204 ObjectAddress address;
8205
8206 /* We assume no checking is required */
8207
8208 /*
8209 * Remove any old default for the column. We use RESTRICT here for
8210 * safety, but at present we do not expect anything to depend on the
8211 * default. (In ordinary cases, there could not be a default in place
8212 * anyway, but it's possible when combining LIKE with inheritance.)
8213 */
8215 true);
8216
8217 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8218
8219 ObjectAddressSubSet(address, RelationRelationId,
8220 RelationGetRelid(rel), attnum);
8221 return address;
8222}
8223
8224/*
8225 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8226 *
8227 * Return the address of the affected column.
8228 */
8229static ObjectAddress
8230ATExecAddIdentity(Relation rel, const char *colName,
8231 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8232{
8233 Relation attrelation;
8234 HeapTuple tuple;
8235 Form_pg_attribute attTup;
8237 ObjectAddress address;
8238 ColumnDef *cdef = castNode(ColumnDef, def);
8239 bool ispartitioned;
8240
8241 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8242 if (ispartitioned && !recurse)
8243 ereport(ERROR,
8244 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8245 errmsg("cannot add identity to a column of only the partitioned table"),
8246 errhint("Do not specify the ONLY keyword.")));
8247
8248 if (rel->rd_rel->relispartition && !recursing)
8249 ereport(ERROR,
8250 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8251 errmsg("cannot add identity to a column of a partition"));
8252
8253 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8254
8255 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8256 if (!HeapTupleIsValid(tuple))
8257 ereport(ERROR,
8258 (errcode(ERRCODE_UNDEFINED_COLUMN),
8259 errmsg("column \"%s\" of relation \"%s\" does not exist",
8260 colName, RelationGetRelationName(rel))));
8261 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8262 attnum = attTup->attnum;
8263
8264 /* Can't alter a system attribute */
8265 if (attnum <= 0)
8266 ereport(ERROR,
8267 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8268 errmsg("cannot alter system column \"%s\"",
8269 colName)));
8270
8271 /*
8272 * Creating a column as identity implies NOT NULL, so adding the identity
8273 * to an existing column that is not NOT NULL would create a state that
8274 * cannot be reproduced without contortions.
8275 */
8276 if (!attTup->attnotnull)
8277 ereport(ERROR,
8278 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8279 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8280 colName, RelationGetRelationName(rel))));
8281
8282 if (attTup->attidentity)
8283 ereport(ERROR,
8284 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8285 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8286 colName, RelationGetRelationName(rel))));
8287
8288 if (attTup->atthasdef)
8289 ereport(ERROR,
8290 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8291 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8292 colName, RelationGetRelationName(rel))));
8293
8294 attTup->attidentity = cdef->identity;
8295 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8296
8297 InvokeObjectPostAlterHook(RelationRelationId,
8298 RelationGetRelid(rel),
8299 attTup->attnum);
8300 ObjectAddressSubSet(address, RelationRelationId,
8301 RelationGetRelid(rel), attnum);
8302 heap_freetuple(tuple);
8303
8304 table_close(attrelation, RowExclusiveLock);
8305
8306 /*
8307 * Recurse to propagate the identity column to partitions. Identity is
8308 * not inherited in regular inheritance children.
8309 */
8310 if (recurse && ispartitioned)
8311 {
8312 List *children;
8313 ListCell *lc;
8314
8315 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8316
8317 foreach(lc, children)
8318 {
8319 Relation childrel;
8320
8321 childrel = table_open(lfirst_oid(lc), NoLock);
8322 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8323 table_close(childrel, NoLock);
8324 }
8325 }
8326
8327 return address;
8328}
8329
8330/*
8331 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8332 *
8333 * Return the address of the affected column.
8334 */
8335static ObjectAddress
8336ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8337 LOCKMODE lockmode, bool recurse, bool recursing)
8338{
8340 DefElem *generatedEl = NULL;
8341 HeapTuple tuple;
8342 Form_pg_attribute attTup;
8344 Relation attrelation;
8345 ObjectAddress address;
8346 bool ispartitioned;
8347
8348 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8349 if (ispartitioned && !recurse)
8350 ereport(ERROR,
8351 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8352 errmsg("cannot change identity column of only the partitioned table"),
8353 errhint("Do not specify the ONLY keyword.")));
8354
8355 if (rel->rd_rel->relispartition && !recursing)
8356 ereport(ERROR,
8357 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8358 errmsg("cannot change identity column of a partition"));
8359
8360 foreach(option, castNode(List, def))
8361 {
8362 DefElem *defel = lfirst_node(DefElem, option);
8363
8364 if (strcmp(defel->defname, "generated") == 0)
8365 {
8366 if (generatedEl)
8367 ereport(ERROR,
8368 (errcode(ERRCODE_SYNTAX_ERROR),
8369 errmsg("conflicting or redundant options")));
8370 generatedEl = defel;
8371 }
8372 else
8373 elog(ERROR, "option \"%s\" not recognized",
8374 defel->defname);
8375 }
8376
8377 /*
8378 * Even if there is nothing to change here, we run all the checks. There
8379 * will be a subsequent ALTER SEQUENCE that relies on everything being
8380 * there.
8381 */
8382
8383 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8384 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8385 if (!HeapTupleIsValid(tuple))
8386 ereport(ERROR,
8387 (errcode(ERRCODE_UNDEFINED_COLUMN),
8388 errmsg("column \"%s\" of relation \"%s\" does not exist",
8389 colName, RelationGetRelationName(rel))));
8390
8391 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8392 attnum = attTup->attnum;
8393
8394 if (attnum <= 0)
8395 ereport(ERROR,
8396 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8397 errmsg("cannot alter system column \"%s\"",
8398 colName)));
8399
8400 if (!attTup->attidentity)
8401 ereport(ERROR,
8402 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8403 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8404 colName, RelationGetRelationName(rel))));
8405
8406 if (generatedEl)
8407 {
8408 attTup->attidentity = defGetInt32(generatedEl);
8409 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8410
8411 InvokeObjectPostAlterHook(RelationRelationId,
8412 RelationGetRelid(rel),
8413 attTup->attnum);
8414 ObjectAddressSubSet(address, RelationRelationId,
8415 RelationGetRelid(rel), attnum);
8416 }
8417 else
8418 address = InvalidObjectAddress;
8419
8420 heap_freetuple(tuple);
8421 table_close(attrelation, RowExclusiveLock);
8422
8423 /*
8424 * Recurse to propagate the identity change to partitions. Identity is not
8425 * inherited in regular inheritance children.
8426 */
8427 if (generatedEl && recurse && ispartitioned)
8428 {
8429 List *children;
8430 ListCell *lc;
8431
8432 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8433
8434 foreach(lc, children)
8435 {
8436 Relation childrel;
8437
8438 childrel = table_open(lfirst_oid(lc), NoLock);
8439 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8440 table_close(childrel, NoLock);
8441 }
8442 }
8443
8444 return address;
8445}
8446
8447/*
8448 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8449 *
8450 * Return the address of the affected column.
8451 */
8452static ObjectAddress
8453ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8454 bool recurse, bool recursing)
8455{
8456 HeapTuple tuple;
8457 Form_pg_attribute attTup;
8459 Relation attrelation;
8460 ObjectAddress address;
8461 Oid seqid;
8462 ObjectAddress seqaddress;
8463 bool ispartitioned;
8464
8465 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8466 if (ispartitioned && !recurse)
8467 ereport(ERROR,
8468 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8469 errmsg("cannot drop identity from a column of only the partitioned table"),
8470 errhint("Do not specify the ONLY keyword.")));
8471
8472 if (rel->rd_rel->relispartition && !recursing)
8473 ereport(ERROR,
8474 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8475 errmsg("cannot drop identity from a column of a partition"));
8476
8477 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8478 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8479 if (!HeapTupleIsValid(tuple))
8480 ereport(ERROR,
8481 (errcode(ERRCODE_UNDEFINED_COLUMN),
8482 errmsg("column \"%s\" of relation \"%s\" does not exist",
8483 colName, RelationGetRelationName(rel))));
8484
8485 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8486 attnum = attTup->attnum;
8487
8488 if (attnum <= 0)
8489 ereport(ERROR,
8490 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8491 errmsg("cannot alter system column \"%s\"",
8492 colName)));
8493
8494 if (!attTup->attidentity)
8495 {
8496 if (!missing_ok)
8497 ereport(ERROR,
8498 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8499 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8500 colName, RelationGetRelationName(rel))));
8501 else
8502 {
8504 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8505 colName, RelationGetRelationName(rel))));
8506 heap_freetuple(tuple);
8507 table_close(attrelation, RowExclusiveLock);
8508 return InvalidObjectAddress;
8509 }
8510 }
8511
8512 attTup->attidentity = '\0';
8513 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8514
8515 InvokeObjectPostAlterHook(RelationRelationId,
8516 RelationGetRelid(rel),
8517 attTup->attnum);
8518 ObjectAddressSubSet(address, RelationRelationId,
8519 RelationGetRelid(rel), attnum);
8520 heap_freetuple(tuple);
8521
8522 table_close(attrelation, RowExclusiveLock);
8523
8524 /*
8525 * Recurse to drop the identity from column in partitions. Identity is
8526 * not inherited in regular inheritance children so ignore them.
8527 */
8528 if (recurse && ispartitioned)
8529 {
8530 List *children;
8531 ListCell *lc;
8532
8533 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8534
8535 foreach(lc, children)
8536 {
8537 Relation childrel;
8538
8539 childrel = table_open(lfirst_oid(lc), NoLock);
8540 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8541 table_close(childrel, NoLock);
8542 }
8543 }
8544
8545 if (!recursing)
8546 {
8547 /* drop the internal sequence */
8548 seqid = getIdentitySequence(rel, attnum, false);
8549 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8550 RelationRelationId, DEPENDENCY_INTERNAL);
8552 seqaddress.classId = RelationRelationId;
8553 seqaddress.objectId = seqid;
8554 seqaddress.objectSubId = 0;
8556 }
8557
8558 return address;
8559}
8560
8561/*
8562 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8563 *
8564 * Return the address of the affected column.
8565 */
8566static ObjectAddress
8567ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8568 Node *newExpr, LOCKMODE lockmode)
8569{
8570 HeapTuple tuple;
8571 Form_pg_attribute attTup;
8573 char attgenerated;
8574 bool rewrite;
8575 Oid attrdefoid;
8576 ObjectAddress address;
8577 Expr *defval;
8579 RawColumnDefault *rawEnt;
8580
8581 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8582 if (!HeapTupleIsValid(tuple))
8583 ereport(ERROR,
8584 (errcode(ERRCODE_UNDEFINED_COLUMN),
8585 errmsg("column \"%s\" of relation \"%s\" does not exist",
8586 colName, RelationGetRelationName(rel))));
8587
8588 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8589
8590 attnum = attTup->attnum;
8591 if (attnum <= 0)
8592 ereport(ERROR,
8593 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8594 errmsg("cannot alter system column \"%s\"",
8595 colName)));
8596
8597 attgenerated = attTup->attgenerated;
8598 if (!attgenerated)
8599 ereport(ERROR,
8600 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8601 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8602 colName, RelationGetRelationName(rel))));
8603
8604 /*
8605 * TODO: This could be done, just need to recheck any constraints
8606 * afterwards.
8607 */
8608 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8609 rel->rd_att->constr && rel->rd_att->constr->num_check > 0)
8610 ereport(ERROR,
8611 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8612 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables with check constraints"),
8613 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8614 colName, RelationGetRelationName(rel))));
8615
8616 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8617 tab->verify_new_notnull = true;
8618
8619 /*
8620 * We need to prevent this because a change of expression could affect a
8621 * row filter and inject expressions that are not permitted in a row
8622 * filter. XXX We could try to have a more precise check to catch only
8623 * publications with row filters, or even re-verify the row filter
8624 * expressions.
8625 */
8626 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8628 ereport(ERROR,
8629 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8630 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns on tables that are part of a publication"),
8631 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8632 colName, RelationGetRelationName(rel))));
8633
8634 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8635
8636 ReleaseSysCache(tuple);
8637
8638 if (rewrite)
8639 {
8640 /*
8641 * Clear all the missing values if we're rewriting the table, since
8642 * this renders them pointless.
8643 */
8645
8646 /* make sure we don't conflict with later attribute modifications */
8648
8649 /*
8650 * Find everything that depends on the column (constraints, indexes,
8651 * etc), and record enough information to let us recreate the objects
8652 * after rewrite.
8653 */
8655 }
8656
8657 /*
8658 * Drop the dependency records of the GENERATED expression, in particular
8659 * its INTERNAL dependency on the column, which would otherwise cause
8660 * dependency.c to refuse to perform the deletion.
8661 */
8662 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8663 if (!OidIsValid(attrdefoid))
8664 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8665 RelationGetRelid(rel), attnum);
8666 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8667
8668 /* Make above changes visible */
8670
8671 /*
8672 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8673 * safety, but at present we do not expect anything to depend on the
8674 * expression.
8675 */
8677 false, false);
8678
8679 /* Prepare to store the new expression, in the catalogs */
8680 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8681 rawEnt->attnum = attnum;
8682 rawEnt->raw_default = newExpr;
8683 rawEnt->generated = attgenerated;
8684
8685 /* Store the generated expression */
8687 false, true, false, NULL);
8688
8689 /* Make above new expression visible */
8691
8692 if (rewrite)
8693 {
8694 /* Prepare for table rewrite */
8695 defval = (Expr *) build_column_default(rel, attnum);
8696
8698 newval->attnum = attnum;
8699 newval->expr = expression_planner(defval);
8700 newval->is_generated = true;
8701
8702 tab->newvals = lappend(tab->newvals, newval);
8704 }
8705
8706 /* Drop any pg_statistic entry for the column */
8708
8709 InvokeObjectPostAlterHook(RelationRelationId,
8710 RelationGetRelid(rel), attnum);
8711
8712 ObjectAddressSubSet(address, RelationRelationId,
8713 RelationGetRelid(rel), attnum);
8714 return address;
8715}
8716
8717/*
8718 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8719 */
8720static void
8721ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8722{
8723 /*
8724 * Reject ONLY if there are child tables. We could implement this, but it
8725 * is a bit complicated. GENERATED clauses must be attached to the column
8726 * definition and cannot be added later like DEFAULT, so if a child table
8727 * has a generation expression that the parent does not have, the child
8728 * column will necessarily be an attislocal column. So to implement ONLY
8729 * here, we'd need extra code to update attislocal of the direct child
8730 * tables, somewhat similar to how DROP COLUMN does it, so that the
8731 * resulting state can be properly dumped and restored.
8732 */
8733 if (!recurse &&
8735 ereport(ERROR,
8736 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8737 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8738
8739 /*
8740 * Cannot drop generation expression from inherited columns.
8741 */
8742 if (!recursing)
8743 {
8744 HeapTuple tuple;
8745 Form_pg_attribute attTup;
8746
8748 if (!HeapTupleIsValid(tuple))
8749 ereport(ERROR,
8750 (errcode(ERRCODE_UNDEFINED_COLUMN),
8751 errmsg("column \"%s\" of relation \"%s\" does not exist",
8752 cmd->name, RelationGetRelationName(rel))));
8753
8754 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8755
8756 if (attTup->attinhcount > 0)
8757 ereport(ERROR,
8758 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8759 errmsg("cannot drop generation expression from inherited column")));
8760 }
8761}
8762
8763/*
8764 * Return the address of the affected column.
8765 */
8766static ObjectAddress
8767ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8768{
8769 HeapTuple tuple;
8770 Form_pg_attribute attTup;
8772 Relation attrelation;
8773 Oid attrdefoid;
8774 ObjectAddress address;
8775
8776 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8777 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8778 if (!HeapTupleIsValid(tuple))
8779 ereport(ERROR,
8780 (errcode(ERRCODE_UNDEFINED_COLUMN),
8781 errmsg("column \"%s\" of relation \"%s\" does not exist",
8782 colName, RelationGetRelationName(rel))));
8783
8784 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8785 attnum = attTup->attnum;
8786
8787 if (attnum <= 0)
8788 ereport(ERROR,
8789 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8790 errmsg("cannot alter system column \"%s\"",
8791 colName)));
8792
8793 /*
8794 * TODO: This could be done, but it would need a table rewrite to
8795 * materialize the generated values. Note that for the time being, we
8796 * still error with missing_ok, so that we don't silently leave the column
8797 * as generated.
8798 */
8799 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8800 ereport(ERROR,
8801 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8802 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8803 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8804 colName, RelationGetRelationName(rel))));
8805
8806 if (!attTup->attgenerated)
8807 {
8808 if (!missing_ok)
8809 ereport(ERROR,
8810 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8811 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8812 colName, RelationGetRelationName(rel))));
8813 else
8814 {
8816 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8817 colName, RelationGetRelationName(rel))));
8818 heap_freetuple(tuple);
8819 table_close(attrelation, RowExclusiveLock);
8820 return InvalidObjectAddress;
8821 }
8822 }
8823
8824 /*
8825 * Mark the column as no longer generated. (The atthasdef flag needs to
8826 * get cleared too, but RemoveAttrDefault will handle that.)
8827 */
8828 attTup->attgenerated = '\0';
8829 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8830
8831 InvokeObjectPostAlterHook(RelationRelationId,
8832 RelationGetRelid(rel),
8833 attnum);
8834 heap_freetuple(tuple);
8835
8836 table_close(attrelation, RowExclusiveLock);
8837
8838 /*
8839 * Drop the dependency records of the GENERATED expression, in particular
8840 * its INTERNAL dependency on the column, which would otherwise cause
8841 * dependency.c to refuse to perform the deletion.
8842 */
8843 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8844 if (!OidIsValid(attrdefoid))
8845 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8846 RelationGetRelid(rel), attnum);
8847 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8848
8849 /* Make above changes visible */
8851
8852 /*
8853 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8854 * safety, but at present we do not expect anything to depend on the
8855 * default.
8856 */
8858 false, false);
8859
8860 ObjectAddressSubSet(address, RelationRelationId,
8861 RelationGetRelid(rel), attnum);
8862 return address;
8863}
8864
8865/*
8866 * ALTER TABLE ALTER COLUMN SET STATISTICS
8867 *
8868 * Return value is the address of the modified column
8869 */
8870static ObjectAddress
8871ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8872{
8873 int newtarget = 0;
8874 bool newtarget_default;
8875 Relation attrelation;
8876 HeapTuple tuple,
8877 newtuple;
8878 Form_pg_attribute attrtuple;
8880 ObjectAddress address;
8881 Datum repl_val[Natts_pg_attribute];
8882 bool repl_null[Natts_pg_attribute];
8883 bool repl_repl[Natts_pg_attribute];
8884
8885 /*
8886 * We allow referencing columns by numbers only for indexes, since table
8887 * column numbers could contain gaps if columns are later dropped.
8888 */
8889 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8890 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8891 !colName)
8892 ereport(ERROR,
8893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8894 errmsg("cannot refer to non-index column by number")));
8895
8896 /* -1 was used in previous versions for the default setting */
8897 if (newValue && intVal(newValue) != -1)
8898 {
8899 newtarget = intVal(newValue);
8900 newtarget_default = false;
8901 }
8902 else
8903 newtarget_default = true;
8904
8905 if (!newtarget_default)
8906 {
8907 /*
8908 * Limit target to a sane range
8909 */
8910 if (newtarget < 0)
8911 {
8912 ereport(ERROR,
8913 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8914 errmsg("statistics target %d is too low",
8915 newtarget)));
8916 }
8917 else if (newtarget > MAX_STATISTICS_TARGET)
8918 {
8919 newtarget = MAX_STATISTICS_TARGET;
8921 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8922 errmsg("lowering statistics target to %d",
8923 newtarget)));
8924 }
8925 }
8926
8927 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8928
8929 if (colName)
8930 {
8931 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8932
8933 if (!HeapTupleIsValid(tuple))
8934 ereport(ERROR,
8935 (errcode(ERRCODE_UNDEFINED_COLUMN),
8936 errmsg("column \"%s\" of relation \"%s\" does not exist",
8937 colName, RelationGetRelationName(rel))));
8938 }
8939 else
8940 {
8941 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8942
8943 if (!HeapTupleIsValid(tuple))
8944 ereport(ERROR,
8945 (errcode(ERRCODE_UNDEFINED_COLUMN),
8946 errmsg("column number %d of relation \"%s\" does not exist",
8947 colNum, RelationGetRelationName(rel))));
8948 }
8949
8950 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8951
8952 attnum = attrtuple->attnum;
8953 if (attnum <= 0)
8954 ereport(ERROR,
8955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8956 errmsg("cannot alter system column \"%s\"",
8957 colName)));
8958
8959 /*
8960 * Prevent this as long as the ANALYZE code skips virtual generated
8961 * columns.
8962 */
8963 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8964 ereport(ERROR,
8965 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8966 errmsg("cannot alter statistics on virtual generated column \"%s\"",
8967 colName)));
8968
8969 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8970 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8971 {
8972 if (attnum > rel->rd_index->indnkeyatts)
8973 ereport(ERROR,
8974 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8975 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8976 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8977 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8978 ereport(ERROR,
8979 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8980 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8981 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8982 errhint("Alter statistics on table column instead.")));
8983 }
8984
8985 /* Build new tuple. */
8986 memset(repl_null, false, sizeof(repl_null));
8987 memset(repl_repl, false, sizeof(repl_repl));
8988 if (!newtarget_default)
8989 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8990 else
8991 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8992 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8993 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8994 repl_val, repl_null, repl_repl);
8995 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8996
8997 InvokeObjectPostAlterHook(RelationRelationId,
8998 RelationGetRelid(rel),
8999 attrtuple->attnum);
9000 ObjectAddressSubSet(address, RelationRelationId,
9001 RelationGetRelid(rel), attnum);
9002
9003 heap_freetuple(newtuple);
9004
9005 ReleaseSysCache(tuple);
9006
9007 table_close(attrelation, RowExclusiveLock);
9008
9009 return address;
9010}
9011
9012/*
9013 * Return value is the address of the modified column
9014 */
9015static ObjectAddress
9016ATExecSetOptions(Relation rel, const char *colName, Node *options,
9017 bool isReset, LOCKMODE lockmode)
9018{
9019 Relation attrelation;
9020 HeapTuple tuple,
9021 newtuple;
9022 Form_pg_attribute attrtuple;
9024 Datum datum,
9025 newOptions;
9026 bool isnull;
9027 ObjectAddress address;
9028 Datum repl_val[Natts_pg_attribute];
9029 bool repl_null[Natts_pg_attribute];
9030 bool repl_repl[Natts_pg_attribute];
9031
9032 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9033
9034 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9035
9036 if (!HeapTupleIsValid(tuple))
9037 ereport(ERROR,
9038 (errcode(ERRCODE_UNDEFINED_COLUMN),
9039 errmsg("column \"%s\" of relation \"%s\" does not exist",
9040 colName, RelationGetRelationName(rel))));
9041 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9042
9043 attnum = attrtuple->attnum;
9044 if (attnum <= 0)
9045 ereport(ERROR,
9046 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9047 errmsg("cannot alter system column \"%s\"",
9048 colName)));
9049
9050 /* Generate new proposed attoptions (text array) */
9051 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
9052 &isnull);
9053 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9054 castNode(List, options), NULL, NULL,
9055 false, isReset);
9056 /* Validate new options */
9057 (void) attribute_reloptions(newOptions, true);
9058
9059 /* Build new tuple. */
9060 memset(repl_null, false, sizeof(repl_null));
9061 memset(repl_repl, false, sizeof(repl_repl));
9062 if (newOptions != (Datum) 0)
9063 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
9064 else
9065 repl_null[Anum_pg_attribute_attoptions - 1] = true;
9066 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
9067 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
9068 repl_val, repl_null, repl_repl);
9069
9070 /* Update system catalog. */
9071 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9072
9073 InvokeObjectPostAlterHook(RelationRelationId,
9074 RelationGetRelid(rel),
9075 attrtuple->attnum);
9076 ObjectAddressSubSet(address, RelationRelationId,
9077 RelationGetRelid(rel), attnum);
9078
9079 heap_freetuple(newtuple);
9080
9081 ReleaseSysCache(tuple);
9082
9083 table_close(attrelation, RowExclusiveLock);
9084
9085 return address;
9086}
9087
9088/*
9089 * Helper function for ATExecSetStorage and ATExecSetCompression
9090 *
9091 * Set the attstorage and/or attcompression fields for index columns
9092 * associated with the specified table column.
9093 */
9094static void
9097 bool setstorage, char newstorage,
9098 bool setcompression, char newcompression,
9099 LOCKMODE lockmode)
9100{
9101 ListCell *lc;
9102
9103 foreach(lc, RelationGetIndexList(rel))
9104 {
9105 Oid indexoid = lfirst_oid(lc);
9106 Relation indrel;
9107 AttrNumber indattnum = 0;
9108 HeapTuple tuple;
9109
9110 indrel = index_open(indexoid, lockmode);
9111
9112 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9113 {
9114 if (indrel->rd_index->indkey.values[i] == attnum)
9115 {
9116 indattnum = i + 1;
9117 break;
9118 }
9119 }
9120
9121 if (indattnum == 0)
9122 {
9123 index_close(indrel, lockmode);
9124 continue;
9125 }
9126
9127 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9128
9129 if (HeapTupleIsValid(tuple))
9130 {
9131 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9132
9133 if (setstorage)
9134 attrtuple->attstorage = newstorage;
9135
9136 if (setcompression)
9137 attrtuple->attcompression = newcompression;
9138
9139 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9140
9141 InvokeObjectPostAlterHook(RelationRelationId,
9142 RelationGetRelid(rel),
9143 attrtuple->attnum);
9144
9145 heap_freetuple(tuple);
9146 }
9147
9148 index_close(indrel, lockmode);
9149 }
9150}
9151
9152/*
9153 * ALTER TABLE ALTER COLUMN SET STORAGE
9154 *
9155 * Return value is the address of the modified column
9156 */
9157static ObjectAddress
9158ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9159{
9160 Relation attrelation;
9161 HeapTuple tuple;
9162 Form_pg_attribute attrtuple;
9164 ObjectAddress address;
9165
9166 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9167
9168 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9169
9170 if (!HeapTupleIsValid(tuple))
9171 ereport(ERROR,
9172 (errcode(ERRCODE_UNDEFINED_COLUMN),
9173 errmsg("column \"%s\" of relation \"%s\" does not exist",
9174 colName, RelationGetRelationName(rel))));
9175 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9176
9177 attnum = attrtuple->attnum;
9178 if (attnum <= 0)
9179 ereport(ERROR,
9180 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9181 errmsg("cannot alter system column \"%s\"",
9182 colName)));
9183
9184 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9185
9186 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9187
9188 InvokeObjectPostAlterHook(RelationRelationId,
9189 RelationGetRelid(rel),
9190 attrtuple->attnum);
9191
9192 /*
9193 * Apply the change to indexes as well (only for simple index columns,
9194 * matching behavior of index.c ConstructTupleDescriptor()).
9195 */
9196 SetIndexStorageProperties(rel, attrelation, attnum,
9197 true, attrtuple->attstorage,
9198 false, 0,
9199 lockmode);
9200
9201 heap_freetuple(tuple);
9202
9203 table_close(attrelation, RowExclusiveLock);
9204
9205 ObjectAddressSubSet(address, RelationRelationId,
9206 RelationGetRelid(rel), attnum);
9207 return address;
9208}
9209
9210
9211/*
9212 * ALTER TABLE DROP COLUMN
9213 *
9214 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9215 * because we have to decide at runtime whether to recurse or not depending
9216 * on whether attinhcount goes to zero or not. (We can't check this in a
9217 * static pre-pass because it won't handle multiple inheritance situations
9218 * correctly.)
9219 */
9220static void
9221ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9222 AlterTableCmd *cmd, LOCKMODE lockmode,
9223 AlterTableUtilityContext *context)
9224{
9225 if (rel->rd_rel->reloftype && !recursing)
9226 ereport(ERROR,
9227 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9228 errmsg("cannot drop column from typed table")));
9229
9230 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9231 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9232
9233 if (recurse)
9234 cmd->recurse = true;
9235}
9236
9237/*
9238 * Drops column 'colName' from relation 'rel' and returns the address of the
9239 * dropped column. The column is also dropped (or marked as no longer
9240 * inherited from relation) from the relation's inheritance children, if any.
9241 *
9242 * In the recursive invocations for inheritance child relations, instead of
9243 * dropping the column directly (if to be dropped at all), its object address
9244 * is added to 'addrs', which must be non-NULL in such invocations. All
9245 * columns are dropped at the same time after all the children have been
9246 * checked recursively.
9247 */
9248static ObjectAddress
9249ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9250 DropBehavior behavior,
9251 bool recurse, bool recursing,
9252 bool missing_ok, LOCKMODE lockmode,
9253 ObjectAddresses *addrs)
9254{
9255 HeapTuple tuple;
9256 Form_pg_attribute targetatt;
9258 List *children;
9259 ObjectAddress object;
9260 bool is_expr;
9261
9262 /* At top level, permission check was done in ATPrepCmd, else do it */
9263 if (recursing)
9266
9267 /* Initialize addrs on the first invocation */
9268 Assert(!recursing || addrs != NULL);
9269
9270 /* since this function recurses, it could be driven to stack overflow */
9272
9273 if (!recursing)
9274 addrs = new_object_addresses();
9275
9276 /*
9277 * get the number of the attribute
9278 */
9279 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9280 if (!HeapTupleIsValid(tuple))
9281 {
9282 if (!missing_ok)
9283 {
9284 ereport(ERROR,
9285 (errcode(ERRCODE_UNDEFINED_COLUMN),
9286 errmsg("column \"%s\" of relation \"%s\" does not exist",
9287 colName, RelationGetRelationName(rel))));
9288 }
9289 else
9290 {
9292 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9293 colName, RelationGetRelationName(rel))));
9294 return InvalidObjectAddress;
9295 }
9296 }
9297 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9298
9299 attnum = targetatt->attnum;
9300
9301 /* Can't drop a system attribute */
9302 if (attnum <= 0)
9303 ereport(ERROR,
9304 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9305 errmsg("cannot drop system column \"%s\"",
9306 colName)));
9307
9308 /*
9309 * Don't drop inherited columns, unless recursing (presumably from a drop
9310 * of the parent column)
9311 */
9312 if (targetatt->attinhcount > 0 && !recursing)
9313 ereport(ERROR,
9314 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9315 errmsg("cannot drop inherited column \"%s\"",
9316 colName)));
9317
9318 /*
9319 * Don't drop columns used in the partition key, either. (If we let this
9320 * go through, the key column's dependencies would cause a cascaded drop
9321 * of the whole table, which is surely not what the user expected.)
9322 */
9323 if (has_partition_attrs(rel,
9325 &is_expr))
9326 ereport(ERROR,
9327 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9328 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9329 colName, RelationGetRelationName(rel))));
9330
9331 ReleaseSysCache(tuple);
9332
9333 /*
9334 * Propagate to children as appropriate. Unlike most other ALTER
9335 * routines, we have to do this one level of recursion at a time; we can't
9336 * use find_all_inheritors to do it in one pass.
9337 */
9338 children =
9340
9341 if (children)
9342 {
9343 Relation attr_rel;
9344 ListCell *child;
9345
9346 /*
9347 * In case of a partitioned table, the column must be dropped from the
9348 * partitions as well.
9349 */
9350 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9351 ereport(ERROR,
9352 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9353 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9354 errhint("Do not specify the ONLY keyword.")));
9355
9356 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9357 foreach(child, children)
9358 {
9359 Oid childrelid = lfirst_oid(child);
9360 Relation childrel;
9361 Form_pg_attribute childatt;
9362
9363 /* find_inheritance_children already got lock */
9364 childrel = table_open(childrelid, NoLock);
9365 CheckAlterTableIsSafe(childrel);
9366
9367 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9368 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9369 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9370 colName, childrelid);
9371 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9372
9373 if (childatt->attinhcount <= 0) /* shouldn't happen */
9374 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9375 childrelid, colName);
9376
9377 if (recurse)
9378 {
9379 /*
9380 * If the child column has other definition sources, just
9381 * decrement its inheritance count; if not, recurse to delete
9382 * it.
9383 */
9384 if (childatt->attinhcount == 1 && !childatt->attislocal)
9385 {
9386 /* Time to delete this child column, too */
9387 ATExecDropColumn(wqueue, childrel, colName,
9388 behavior, true, true,
9389 false, lockmode, addrs);
9390 }
9391 else
9392 {
9393 /* Child column must survive my deletion */
9394 childatt->attinhcount--;
9395
9396 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9397
9398 /* Make update visible */
9400 }
9401 }
9402 else
9403 {
9404 /*
9405 * If we were told to drop ONLY in this table (no recursion),
9406 * we need to mark the inheritors' attributes as locally
9407 * defined rather than inherited.
9408 */
9409 childatt->attinhcount--;
9410 childatt->attislocal = true;
9411
9412 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9413
9414 /* Make update visible */
9416 }
9417
9418 heap_freetuple(tuple);
9419
9420 table_close(childrel, NoLock);
9421 }
9422 table_close(attr_rel, RowExclusiveLock);
9423 }
9424
9425 /* Add object to delete */
9426 object.classId = RelationRelationId;
9427 object.objectId = RelationGetRelid(rel);
9428 object.objectSubId = attnum;
9429 add_exact_object_address(&object, addrs);
9430
9431 if (!recursing)
9432 {
9433 /* Recursion has ended, drop everything that was collected */
9434 performMultipleDeletions(addrs, behavior, 0);
9435 free_object_addresses(addrs);
9436 }
9437
9438 return object;
9439}
9440
9441/*
9442 * Prepare to add a primary key on a table, by adding not-null constraints
9443 * on all columns.
9444 *
9445 * The not-null constraints for a primary key must cover the whole inheritance
9446 * hierarchy (failing to ensure that leads to funny corner cases). For the
9447 * normal case where we're asked to recurse, this routine checks if the
9448 * not-null constraints exist already, and if not queues a requirement for
9449 * them to be created by phase 2.
9450 *
9451 * For the case where we're asked not to recurse, we verify that a not-null
9452 * constraint exists on each column of each (direct) child table, throwing an
9453 * error if not. Not throwing an error would also work, because a not-null
9454 * constraint would be created anyway, but it'd cause a silent scan of the
9455 * child table to verify absence of nulls. We prefer to let the user know so
9456 * that they can add the constraint manually without having to hold
9457 * AccessExclusiveLock while at it.
9458 *
9459 * However, it's also important that we do not acquire locks on children if
9460 * the not-null constraints already exist on the parent, to avoid risking
9461 * deadlocks during parallel pg_restore of PKs on partitioned tables.
9462 */
9463static void
9465 bool recurse, LOCKMODE lockmode,
9466 AlterTableUtilityContext *context)
9467{
9468 Constraint *pkconstr;
9469 List *children = NIL;
9470 bool got_children = false;
9471
9472 pkconstr = castNode(Constraint, cmd->def);
9473 if (pkconstr->contype != CONSTR_PRIMARY)
9474 return;
9475
9476 /* Verify that columns are not-null, or request that they be made so */
9477 foreach_node(String, column, pkconstr->keys)
9478 {
9479 AlterTableCmd *newcmd;
9480 Constraint *nnconstr;
9481 HeapTuple tuple;
9482
9483 /*
9484 * First check if a suitable constraint exists. If it does, we don't
9485 * need to request another one. We do need to bail out if it's not
9486 * valid, though.
9487 */
9488 tuple = findNotNullConstraint(RelationGetRelid(rel), strVal(column));
9489 if (tuple != NULL)
9490 {
9491 verifyNotNullPKCompatible(tuple, strVal(column));
9492
9493 /* All good with this one; don't request another */
9494 heap_freetuple(tuple);
9495 continue;
9496 }
9497 else if (!recurse)
9498 {
9499 /*
9500 * No constraint on this column. Asked not to recurse, we won't
9501 * create one here, but verify that all children have one.
9502 */
9503 if (!got_children)
9504 {
9506 lockmode);
9507 /* only search for children on the first time through */
9508 got_children = true;
9509 }
9510
9511 foreach_oid(childrelid, children)
9512 {
9513 HeapTuple tup;
9514
9515 tup = findNotNullConstraint(childrelid, strVal(column));
9516 if (!tup)
9517 ereport(ERROR,
9518 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9519 strVal(column), get_rel_name(childrelid)));
9520 /* verify it's good enough */
9521 verifyNotNullPKCompatible(tup, strVal(column));
9522 }
9523 }
9524
9525 /* This column is not already not-null, so add it to the queue */
9526 nnconstr = makeNotNullConstraint(column);
9527
9528 newcmd = makeNode(AlterTableCmd);
9529 newcmd->subtype = AT_AddConstraint;
9530 /* note we force recurse=true here; see above */
9531 newcmd->recurse = true;
9532 newcmd->def = (Node *) nnconstr;
9533
9534 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9535 }
9536}
9537
9538/*
9539 * Verify whether the given not-null constraint is compatible with a
9540 * primary key. If not, an error is thrown.
9541 */
9542static void
9543verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
9544{
9546
9547 if (conForm->contype != CONSTRAINT_NOTNULL)
9548 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9549
9550 /* a NO INHERIT constraint is no good */
9551 if (conForm->connoinherit)
9552 ereport(ERROR,
9553 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9554 errmsg("cannot create primary key on column \"%s\"", colname),
9555 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9556 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9557 NameStr(conForm->conname), colname,
9558 get_rel_name(conForm->conrelid), "NO INHERIT"),
9559 errhint("You might need to make the existing constraint inheritable using %s.",
9560 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9561
9562 /* an unvalidated constraint is no good */
9563 if (!conForm->convalidated)
9564 ereport(ERROR,
9565 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
9566 errmsg("cannot create primary key on column \"%s\"", colname),
9567 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9568 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9569 NameStr(conForm->conname), colname,
9570 get_rel_name(conForm->conrelid), "NOT VALID"),
9571 errhint("You might need to validate it using %s.",
9572 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9573}
9574
9575/*
9576 * ALTER TABLE ADD INDEX
9577 *
9578 * There is no such command in the grammar, but parse_utilcmd.c converts
9579 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9580 * us schedule creation of the index at the appropriate time during ALTER.
9581 *
9582 * Return value is the address of the new index.
9583 */
9584static ObjectAddress
9586 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9587{
9588 bool check_rights;
9589 bool skip_build;
9590 bool quiet;
9591 ObjectAddress address;
9592
9594 Assert(!stmt->concurrent);
9595
9596 /* The IndexStmt has already been through transformIndexStmt */
9597 Assert(stmt->transformed);
9598
9599 /* suppress schema rights check when rebuilding existing index */
9600 check_rights = !is_rebuild;
9601 /* skip index build if phase 3 will do it or we're reusing an old one */
9602 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9603 /* suppress notices when rebuilding existing index */
9604 quiet = is_rebuild;
9605
9606 address = DefineIndex(RelationGetRelid(rel),
9607 stmt,
9608 InvalidOid, /* no predefined OID */
9609 InvalidOid, /* no parent index */
9610 InvalidOid, /* no parent constraint */
9611 -1, /* total_parts unknown */
9612 true, /* is_alter_table */
9613 check_rights,
9614 false, /* check_not_in_use - we did it already */
9615 skip_build,
9616 quiet);
9617
9618 /*
9619 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9620 * new index instead of building from scratch. Restore associated fields.
9621 * This may store InvalidSubTransactionId in both fields, in which case
9622 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9623 * this after the CCI that made catalog rows visible to any rebuild. The
9624 * DROP of the old edition of this index will have scheduled the storage
9625 * for deletion at commit, so cancel that pending deletion.
9626 */
9627 if (RelFileNumberIsValid(stmt->oldNumber))
9628 {
9629 Relation irel = index_open(address.objectId, NoLock);
9630
9631 irel->rd_createSubid = stmt->oldCreateSubid;
9632 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9634 index_close(irel, NoLock);
9635 }
9636
9637 return address;
9638}
9639
9640/*
9641 * ALTER TABLE ADD STATISTICS
9642 *
9643 * This is no such command in the grammar, but we use this internally to add
9644 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9645 * column type change.
9646 */
9647static ObjectAddress
9649 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9650{
9651 ObjectAddress address;
9652
9654
9655 /* The CreateStatsStmt has already been through transformStatsStmt */
9656 Assert(stmt->transformed);
9657
9658 address = CreateStatistics(stmt);
9659
9660 return address;
9661}
9662
9663/*
9664 * ALTER TABLE ADD CONSTRAINT USING INDEX
9665 *
9666 * Returns the address of the new constraint.
9667 */
9668static ObjectAddress
9670 IndexStmt *stmt, LOCKMODE lockmode)
9671{
9672 Oid index_oid = stmt->indexOid;
9673 Relation indexRel;
9674 char *indexName;
9675 IndexInfo *indexInfo;
9676 char *constraintName;
9677 char constraintType;
9678 ObjectAddress address;
9679 bits16 flags;
9680
9682 Assert(OidIsValid(index_oid));
9683 Assert(stmt->isconstraint);
9684
9685 /*
9686 * Doing this on partitioned tables is not a simple feature to implement,
9687 * so let's punt for now.
9688 */
9689 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9690 ereport(ERROR,
9691 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9692 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9693
9694 indexRel = index_open(index_oid, AccessShareLock);
9695
9696 indexName = pstrdup(RelationGetRelationName(indexRel));
9697
9698 indexInfo = BuildIndexInfo(indexRel);
9699
9700 /* this should have been checked at parse time */
9701 if (!indexInfo->ii_Unique)
9702 elog(ERROR, "index \"%s\" is not unique", indexName);
9703
9704 /*
9705 * Determine name to assign to constraint. We require a constraint to
9706 * have the same name as the underlying index; therefore, use the index's
9707 * existing name as the default constraint name, and if the user
9708 * explicitly gives some other name for the constraint, rename the index
9709 * to match.
9710 */
9711 constraintName = stmt->idxname;
9712 if (constraintName == NULL)
9713 constraintName = indexName;
9714 else if (strcmp(constraintName, indexName) != 0)
9715 {
9717 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9718 indexName, constraintName)));
9719 RenameRelationInternal(index_oid, constraintName, false, true);
9720 }
9721
9722 /* Extra checks needed if making primary key */
9723 if (stmt->primary)
9724 index_check_primary_key(rel, indexInfo, true, stmt);
9725
9726 /* Note we currently don't support EXCLUSION constraints here */
9727 if (stmt->primary)
9728 constraintType = CONSTRAINT_PRIMARY;
9729 else
9730 constraintType = CONSTRAINT_UNIQUE;
9731
9732 /* Create the catalog entries for the constraint */
9735 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9736 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9738
9739 address = index_constraint_create(rel,
9740 index_oid,
9741 InvalidOid,
9742 indexInfo,
9743 constraintName,
9744 constraintType,
9745 flags,
9747 false); /* is_internal */
9748
9749 index_close(indexRel, NoLock);
9750
9751 return address;
9752}
9753
9754/*
9755 * ALTER TABLE ADD CONSTRAINT
9756 *
9757 * Return value is the address of the new constraint; if no constraint was
9758 * added, InvalidObjectAddress is returned.
9759 */
9760static ObjectAddress
9762 Constraint *newConstraint, bool recurse, bool is_readd,
9763 LOCKMODE lockmode)
9764{
9766
9767 Assert(IsA(newConstraint, Constraint));
9768
9769 /*
9770 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9771 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9772 * parse_utilcmd.c).
9773 */
9774 switch (newConstraint->contype)
9775 {
9776 case CONSTR_CHECK:
9777 case CONSTR_NOTNULL:
9778 address =
9779 ATAddCheckNNConstraint(wqueue, tab, rel,
9780 newConstraint, recurse, false, is_readd,
9781 lockmode);
9782 break;
9783
9784 case CONSTR_FOREIGN:
9785
9786 /*
9787 * Assign or validate constraint name
9788 */
9789 if (newConstraint->conname)
9790 {
9792 RelationGetRelid(rel),
9793 newConstraint->conname))
9794 ereport(ERROR,
9796 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9797 newConstraint->conname,
9799 }
9800 else
9801 newConstraint->conname =
9804 "fkey",
9806 NIL);
9807
9808 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9809 newConstraint,
9810 recurse, false,
9811 lockmode);
9812 break;
9813
9814 default:
9815 elog(ERROR, "unrecognized constraint type: %d",
9816 (int) newConstraint->contype);
9817 }
9818
9819 return address;
9820}
9821
9822/*
9823 * Generate the column-name portion of the constraint name for a new foreign
9824 * key given the list of column names that reference the referenced
9825 * table. This will be passed to ChooseConstraintName along with the parent
9826 * table name and the "fkey" suffix.
9827 *
9828 * We know that less than NAMEDATALEN characters will actually be used, so we
9829 * can truncate the result once we've generated that many.
9830 *
9831 * XXX see also ChooseExtendedStatisticNameAddition and
9832 * ChooseIndexNameAddition.
9833 */
9834static char *
9836{
9837 char buf[NAMEDATALEN * 2];
9838 int buflen = 0;
9839 ListCell *lc;
9840
9841 buf[0] = '\0';
9842 foreach(lc, colnames)
9843 {
9844 const char *name = strVal(lfirst(lc));
9845
9846 if (buflen > 0)
9847 buf[buflen++] = '_'; /* insert _ between names */
9848
9849 /*
9850 * At this point we have buflen <= NAMEDATALEN. name should be less
9851 * than NAMEDATALEN already, but use strlcpy for paranoia.
9852 */
9853 strlcpy(buf + buflen, name, NAMEDATALEN);
9854 buflen += strlen(buf + buflen);
9855 if (buflen >= NAMEDATALEN)
9856 break;
9857 }
9858 return pstrdup(buf);
9859}
9860
9861/*
9862 * Add a check or not-null constraint to a single table and its children.
9863 * Returns the address of the constraint added to the parent relation,
9864 * if one gets added, or InvalidObjectAddress otherwise.
9865 *
9866 * Subroutine for ATExecAddConstraint.
9867 *
9868 * We must recurse to child tables during execution, rather than using
9869 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9870 * constraints *must* be given the same name, else they won't be seen as
9871 * related later. If the user didn't explicitly specify a name, then
9872 * AddRelationNewConstraints would normally assign different names to the
9873 * child constraints. To fix that, we must capture the name assigned at
9874 * the parent table and pass that down.
9875 */
9876static ObjectAddress
9878 Constraint *constr, bool recurse, bool recursing,
9879 bool is_readd, LOCKMODE lockmode)
9880{
9881 List *newcons;
9882 ListCell *lcon;
9883 List *children;
9884 ListCell *child;
9886
9887 /* Guard against stack overflow due to overly deep inheritance tree. */
9889
9890 /* At top level, permission check was done in ATPrepCmd, else do it */
9891 if (recursing)
9894
9895 /*
9896 * Call AddRelationNewConstraints to do the work, making sure it works on
9897 * a copy of the Constraint so transformExpr can't modify the original. It
9898 * returns a list of cooked constraints.
9899 *
9900 * If the constraint ends up getting merged with a pre-existing one, it's
9901 * omitted from the returned list, which is what we want: we do not need
9902 * to do any validation work. That can only happen at child tables,
9903 * though, since we disallow merging at the top level.
9904 */
9905 newcons = AddRelationNewConstraints(rel, NIL,
9906 list_make1(copyObject(constr)),
9907 recursing || is_readd, /* allow_merge */
9908 !recursing, /* is_local */
9909 is_readd, /* is_internal */
9910 NULL); /* queryString not available
9911 * here */
9912
9913 /* we don't expect more than one constraint here */
9914 Assert(list_length(newcons) <= 1);
9915
9916 /* Add each to-be-validated constraint to Phase 3's queue */
9917 foreach(lcon, newcons)
9918 {
9919 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9920
9921 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9922 {
9923 NewConstraint *newcon;
9924
9925 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9926 newcon->name = ccon->name;
9927 newcon->contype = ccon->contype;
9928 newcon->qual = ccon->expr;
9929
9930 tab->constraints = lappend(tab->constraints, newcon);
9931 }
9932
9933 /* Save the actually assigned name if it was defaulted */
9934 if (constr->conname == NULL)
9935 constr->conname = ccon->name;
9936
9937 /*
9938 * If adding a valid not-null constraint, set the pg_attribute flag
9939 * and tell phase 3 to verify existing rows, if needed. For an
9940 * invalid constraint, just set attnotnull, without queueing
9941 * verification.
9942 */
9943 if (constr->contype == CONSTR_NOTNULL)
9944 set_attnotnull(wqueue, rel, ccon->attnum,
9945 !constr->skip_validation,
9946 !constr->skip_validation);
9947
9948 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9949 }
9950
9951 /* At this point we must have a locked-down name to use */
9952 Assert(newcons == NIL || constr->conname != NULL);
9953
9954 /* Advance command counter in case same table is visited multiple times */
9956
9957 /*
9958 * If the constraint got merged with an existing constraint, we're done.
9959 * We mustn't recurse to child tables in this case, because they've
9960 * already got the constraint, and visiting them again would lead to an
9961 * incorrect value for coninhcount.
9962 */
9963 if (newcons == NIL)
9964 return address;
9965
9966 /*
9967 * If adding a NO INHERIT constraint, no need to find our children.
9968 */
9969 if (constr->is_no_inherit)
9970 return address;
9971
9972 /*
9973 * Propagate to children as appropriate. Unlike most other ALTER
9974 * routines, we have to do this one level of recursion at a time; we can't
9975 * use find_all_inheritors to do it in one pass.
9976 */
9977 children =
9979
9980 /*
9981 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9982 * constraint creation only if there are no children currently. Error out
9983 * otherwise.
9984 */
9985 if (!recurse && children != NIL)
9986 ereport(ERROR,
9987 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9988 errmsg("constraint must be added to child tables too")));
9989
9990 /*
9991 * Recurse to create the constraint on each child.
9992 */
9993 foreach(child, children)
9994 {
9995 Oid childrelid = lfirst_oid(child);
9996 Relation childrel;
9997 AlteredTableInfo *childtab;
9998
9999 /* find_inheritance_children already got lock */
10000 childrel = table_open(childrelid, NoLock);
10001 CheckAlterTableIsSafe(childrel);
10002
10003 /* Find or create work queue entry for this table */
10004 childtab = ATGetQueueEntry(wqueue, childrel);
10005
10006 /* Recurse to this child */
10007 ATAddCheckNNConstraint(wqueue, childtab, childrel,
10008 constr, recurse, true, is_readd, lockmode);
10009
10010 table_close(childrel, NoLock);
10011 }
10012
10013 return address;
10014}
10015
10016/*
10017 * Add a foreign-key constraint to a single table; return the new constraint's
10018 * address.
10019 *
10020 * Subroutine for ATExecAddConstraint. Must already hold exclusive
10021 * lock on the rel, and have done appropriate validity checks for it.
10022 * We do permissions checks here, however.
10023 *
10024 * When the referenced or referencing tables (or both) are partitioned,
10025 * multiple pg_constraint rows are required -- one for each partitioned table
10026 * and each partition on each side (fortunately, not one for every combination
10027 * thereof). We also need action triggers on each leaf partition on the
10028 * referenced side, and check triggers on each leaf partition on the
10029 * referencing side.
10030 */
10031static ObjectAddress
10033 Constraint *fkconstraint,
10034 bool recurse, bool recursing, LOCKMODE lockmode)
10035{
10036 Relation pkrel;
10037 int16 pkattnum[INDEX_MAX_KEYS] = {0};
10038 int16 fkattnum[INDEX_MAX_KEYS] = {0};
10039 Oid pktypoid[INDEX_MAX_KEYS] = {0};
10040 Oid fktypoid[INDEX_MAX_KEYS] = {0};
10041 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
10042 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
10043 Oid opclasses[INDEX_MAX_KEYS] = {0};
10044 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
10045 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
10046 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
10047 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
10048 bool with_period;
10049 bool pk_has_without_overlaps;
10050 int i;
10051 int numfks,
10052 numpks,
10053 numfkdelsetcols;
10054 Oid indexOid;
10055 bool old_check_ok;
10056 ObjectAddress address;
10057 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
10058
10059 /*
10060 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10061 * delete rows out from under us.
10062 */
10063 if (OidIsValid(fkconstraint->old_pktable_oid))
10064 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
10065 else
10066 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
10067
10068 /*
10069 * Validity checks (permission checks wait till we have the column
10070 * numbers)
10071 */
10072 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10073 ereport(ERROR,
10074 errcode(ERRCODE_WRONG_OBJECT_TYPE),
10075 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10077 RelationGetRelationName(pkrel)));
10078
10079 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10080 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10081 ereport(ERROR,
10082 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10083 errmsg("referenced relation \"%s\" is not a table",
10084 RelationGetRelationName(pkrel))));
10085
10087 ereport(ERROR,
10088 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
10089 errmsg("permission denied: \"%s\" is a system catalog",
10090 RelationGetRelationName(pkrel))));
10091
10092 /*
10093 * References from permanent or unlogged tables to temp tables, and from
10094 * permanent tables to unlogged tables, are disallowed because the
10095 * referenced data can vanish out from under us. References from temp
10096 * tables to any other table type are also disallowed, because other
10097 * backends might need to run the RI triggers on the perm table, but they
10098 * can't reliably see tuples in the local buffers of other backends.
10099 */
10100 switch (rel->rd_rel->relpersistence)
10101 {
10102 case RELPERSISTENCE_PERMANENT:
10103 if (!RelationIsPermanent(pkrel))
10104 ereport(ERROR,
10105 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10106 errmsg("constraints on permanent tables may reference only permanent tables")));
10107 break;
10108 case RELPERSISTENCE_UNLOGGED:
10109 if (!RelationIsPermanent(pkrel)
10110 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10111 ereport(ERROR,
10112 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10113 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10114 break;
10115 case RELPERSISTENCE_TEMP:
10116 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10117 ereport(ERROR,
10118 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10119 errmsg("constraints on temporary tables may reference only temporary tables")));
10120 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10121 ereport(ERROR,
10122 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10123 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10124 break;
10125 }
10126
10127 /*
10128 * Look up the referencing attributes to make sure they exist, and record
10129 * their attnums and type and collation OIDs.
10130 */
10132 fkconstraint->fk_attrs,
10133 fkattnum, fktypoid, fkcolloid);
10134 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10135 if (with_period && !fkconstraint->fk_with_period)
10136 ereport(ERROR,
10137 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10138 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10139
10140 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10141 fkconstraint->fk_del_set_cols,
10142 fkdelsetcols, NULL, NULL);
10143 numfkdelsetcols = validateFkOnDeleteSetColumns(numfks, fkattnum,
10144 numfkdelsetcols,
10145 fkdelsetcols,
10146 fkconstraint->fk_del_set_cols);
10147
10148 /*
10149 * If the attribute list for the referenced table was omitted, lookup the
10150 * definition of the primary key and use it. Otherwise, validate the
10151 * supplied attribute list. In either case, discover the index OID and
10152 * index opclasses, and the attnums and type and collation OIDs of the
10153 * attributes.
10154 */
10155 if (fkconstraint->pk_attrs == NIL)
10156 {
10157 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10158 &fkconstraint->pk_attrs,
10159 pkattnum, pktypoid, pkcolloid,
10160 opclasses, &pk_has_without_overlaps);
10161
10162 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10163 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10164 ereport(ERROR,
10165 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10166 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10167 }
10168 else
10169 {
10171 fkconstraint->pk_attrs,
10172 pkattnum, pktypoid, pkcolloid);
10173
10174 /* Since we got pk_attrs, one should be a period. */
10175 if (with_period && !fkconstraint->pk_with_period)
10176 ereport(ERROR,
10177 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10178 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10179
10180 /* Look for an index matching the column list */
10181 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10182 with_period, opclasses, &pk_has_without_overlaps);
10183 }
10184
10185 /*
10186 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10187 * must use PERIOD.
10188 */
10189 if (pk_has_without_overlaps && !with_period)
10190 ereport(ERROR,
10191 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10192 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10193
10194 /*
10195 * Now we can check permissions.
10196 */
10197 checkFkeyPermissions(pkrel, pkattnum, numpks);
10198
10199 /*
10200 * Check some things for generated columns.
10201 */
10202 for (i = 0; i < numfks; i++)
10203 {
10204 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10205
10206 if (attgenerated)
10207 {
10208 /*
10209 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10210 */
10211 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10212 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10213 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10214 ereport(ERROR,
10215 (errcode(ERRCODE_SYNTAX_ERROR),
10216 errmsg("invalid %s action for foreign key constraint containing generated column",
10217 "ON UPDATE")));
10218 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10220 ereport(ERROR,
10221 (errcode(ERRCODE_SYNTAX_ERROR),
10222 errmsg("invalid %s action for foreign key constraint containing generated column",
10223 "ON DELETE")));
10224 }
10225
10226 /*
10227 * FKs on virtual columns are not supported. This would require
10228 * various additional support in ri_triggers.c, including special
10229 * handling in ri_NullCheck(), ri_KeysEqual(),
10230 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10231 * as NULL there). Also not really practical as long as you can't
10232 * index virtual columns.
10233 */
10234 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10235 ereport(ERROR,
10236 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10237 errmsg("foreign key constraints on virtual generated columns are not supported")));
10238 }
10239
10240 /*
10241 * Some actions are currently unsupported for foreign keys using PERIOD.
10242 */
10243 if (fkconstraint->fk_with_period)
10244 {
10245 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10246 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10247 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10249 ereport(ERROR,
10250 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10251 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10252 "ON UPDATE"));
10253
10254 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10255 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10256 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10258 ereport(ERROR,
10259 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10260 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10261 "ON DELETE"));
10262 }
10263
10264 /*
10265 * Look up the equality operators to use in the constraint.
10266 *
10267 * Note that we have to be careful about the difference between the actual
10268 * PK column type and the opclass' declared input type, which might be
10269 * only binary-compatible with it. The declared opcintype is the right
10270 * thing to probe pg_amop with.
10271 */
10272 if (numfks != numpks)
10273 ereport(ERROR,
10274 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10275 errmsg("number of referencing and referenced columns for foreign key disagree")));
10276
10277 /*
10278 * On the strength of a previous constraint, we might avoid scanning
10279 * tables to validate this one. See below.
10280 */
10281 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10282 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10283
10284 for (i = 0; i < numpks; i++)
10285 {
10286 Oid pktype = pktypoid[i];
10287 Oid fktype = fktypoid[i];
10288 Oid fktyped;
10289 Oid pkcoll = pkcolloid[i];
10290 Oid fkcoll = fkcolloid[i];
10291 HeapTuple cla_ht;
10292 Form_pg_opclass cla_tup;
10293 Oid amid;
10294 Oid opfamily;
10295 Oid opcintype;
10296 bool for_overlaps;
10297 CompareType cmptype;
10298 Oid pfeqop;
10299 Oid ppeqop;
10300 Oid ffeqop;
10301 int16 eqstrategy;
10302 Oid pfeqop_right;
10303
10304 /* We need several fields out of the pg_opclass entry */
10305 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10306 if (!HeapTupleIsValid(cla_ht))
10307 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10308 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10309 amid = cla_tup->opcmethod;
10310 opfamily = cla_tup->opcfamily;
10311 opcintype = cla_tup->opcintype;
10312 ReleaseSysCache(cla_ht);
10313
10314 /*
10315 * Get strategy number from index AM.
10316 *
10317 * For a normal foreign-key constraint, this should not fail, since we
10318 * already checked that the index is unique and should therefore have
10319 * appropriate equal operators. For a period foreign key, this could
10320 * fail if we selected a non-matching exclusion constraint earlier.
10321 * (XXX Maybe we should do these lookups earlier so we don't end up
10322 * doing that.)
10323 */
10324 for_overlaps = with_period && i == numpks - 1;
10325 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10326 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10327 if (eqstrategy == InvalidStrategy)
10328 ereport(ERROR,
10329 errcode(ERRCODE_UNDEFINED_OBJECT),
10330 for_overlaps
10331 ? errmsg("could not identify an overlaps operator for foreign key")
10332 : errmsg("could not identify an equality operator for foreign key"),
10333 errdetail("Could not translate compare type %d for operator family \"%s\", input type %s, access method \"%s\".",
10334 cmptype, get_opfamily_name(opfamily, false), format_type_be(opcintype), get_am_name(amid)));
10335
10336 /*
10337 * There had better be a primary equality operator for the index.
10338 * We'll use it for PK = PK comparisons.
10339 */
10340 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10341 eqstrategy);
10342
10343 if (!OidIsValid(ppeqop))
10344 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10345 eqstrategy, opcintype, opcintype, opfamily);
10346
10347 /*
10348 * Are there equality operators that take exactly the FK type? Assume
10349 * we should look through any domain here.
10350 */
10351 fktyped = getBaseType(fktype);
10352
10353 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10354 eqstrategy);
10355 if (OidIsValid(pfeqop))
10356 {
10357 pfeqop_right = fktyped;
10358 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10359 eqstrategy);
10360 }
10361 else
10362 {
10363 /* keep compiler quiet */
10364 pfeqop_right = InvalidOid;
10365 ffeqop = InvalidOid;
10366 }
10367
10368 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10369 {
10370 /*
10371 * Otherwise, look for an implicit cast from the FK type to the
10372 * opcintype, and if found, use the primary equality operator.
10373 * This is a bit tricky because opcintype might be a polymorphic
10374 * type such as ANYARRAY or ANYENUM; so what we have to test is
10375 * whether the two actual column types can be concurrently cast to
10376 * that type. (Otherwise, we'd fail to reject combinations such
10377 * as int[] and point[].)
10378 */
10379 Oid input_typeids[2];
10380 Oid target_typeids[2];
10381
10382 input_typeids[0] = pktype;
10383 input_typeids[1] = fktype;
10384 target_typeids[0] = opcintype;
10385 target_typeids[1] = opcintype;
10386 if (can_coerce_type(2, input_typeids, target_typeids,
10388 {
10389 pfeqop = ffeqop = ppeqop;
10390 pfeqop_right = opcintype;
10391 }
10392 }
10393
10394 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10395 ereport(ERROR,
10396 (errcode(ERRCODE_DATATYPE_MISMATCH),
10397 errmsg("foreign key constraint \"%s\" cannot be implemented",
10398 fkconstraint->conname),
10399 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10400 "are of incompatible types: %s and %s.",
10401 strVal(list_nth(fkconstraint->fk_attrs, i)),
10402 strVal(list_nth(fkconstraint->pk_attrs, i)),
10403 format_type_be(fktype),
10404 format_type_be(pktype))));
10405
10406 /*
10407 * This shouldn't be possible, but better check to make sure we have a
10408 * consistent state for the check below.
10409 */
10410 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10411 elog(ERROR, "key columns are not both collatable");
10412
10413 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10414 {
10415 bool pkcolldet;
10416 bool fkcolldet;
10417
10418 pkcolldet = get_collation_isdeterministic(pkcoll);
10419 fkcolldet = get_collation_isdeterministic(fkcoll);
10420
10421 /*
10422 * SQL requires that both collations are the same. This is
10423 * because we need a consistent notion of equality on both
10424 * columns. We relax this by allowing different collations if
10425 * they are both deterministic. (This is also for backward
10426 * compatibility, because PostgreSQL has always allowed this.)
10427 */
10428 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10429 ereport(ERROR,
10430 (errcode(ERRCODE_COLLATION_MISMATCH),
10431 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10432 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10433 "have incompatible collations: \"%s\" and \"%s\". "
10434 "If either collation is nondeterministic, then both collations have to be the same.",
10435 strVal(list_nth(fkconstraint->fk_attrs, i)),
10436 strVal(list_nth(fkconstraint->pk_attrs, i)),
10437 get_collation_name(fkcoll),
10438 get_collation_name(pkcoll))));
10439 }
10440
10441 if (old_check_ok)
10442 {
10443 /*
10444 * When a pfeqop changes, revalidate the constraint. We could
10445 * permit intra-opfamily changes, but that adds subtle complexity
10446 * without any concrete benefit for core types. We need not
10447 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10448 */
10449 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10450 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10451 old_pfeqop_item);
10452 }
10453 if (old_check_ok)
10454 {
10455 Oid old_fktype;
10456 Oid new_fktype;
10457 CoercionPathType old_pathtype;
10458 CoercionPathType new_pathtype;
10459 Oid old_castfunc;
10460 Oid new_castfunc;
10461 Oid old_fkcoll;
10462 Oid new_fkcoll;
10464 fkattnum[i] - 1);
10465
10466 /*
10467 * Identify coercion pathways from each of the old and new FK-side
10468 * column types to the right (foreign) operand type of the pfeqop.
10469 * We may assume that pg_constraint.conkey is not changing.
10470 */
10471 old_fktype = attr->atttypid;
10472 new_fktype = fktype;
10473 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10474 &old_castfunc);
10475 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10476 &new_castfunc);
10477
10478 old_fkcoll = attr->attcollation;
10479 new_fkcoll = fkcoll;
10480
10481 /*
10482 * Upon a change to the cast from the FK column to its pfeqop
10483 * operand, revalidate the constraint. For this evaluation, a
10484 * binary coercion cast is equivalent to no cast at all. While
10485 * type implementors should design implicit casts with an eye
10486 * toward consistency of operations like equality, we cannot
10487 * assume here that they have done so.
10488 *
10489 * A function with a polymorphic argument could change behavior
10490 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10491 * when the cast destination is polymorphic, we only avoid
10492 * revalidation if the input type has not changed at all. Given
10493 * just the core data types and operator classes, this requirement
10494 * prevents no would-be optimizations.
10495 *
10496 * If the cast converts from a base type to a domain thereon, then
10497 * that domain type must be the opcintype of the unique index.
10498 * Necessarily, the primary key column must then be of the domain
10499 * type. Since the constraint was previously valid, all values on
10500 * the foreign side necessarily exist on the primary side and in
10501 * turn conform to the domain. Consequently, we need not treat
10502 * domains specially here.
10503 *
10504 * If the collation changes, revalidation is required, unless both
10505 * collations are deterministic, because those share the same
10506 * notion of equality (because texteq reduces to bitwise
10507 * equality).
10508 *
10509 * We need not directly consider the PK type. It's necessarily
10510 * binary coercible to the opcintype of the unique index column,
10511 * and ri_triggers.c will only deal with PK datums in terms of
10512 * that opcintype. Changing the opcintype also changes pfeqop.
10513 */
10514 old_check_ok = (new_pathtype == old_pathtype &&
10515 new_castfunc == old_castfunc &&
10516 (!IsPolymorphicType(pfeqop_right) ||
10517 new_fktype == old_fktype) &&
10518 (new_fkcoll == old_fkcoll ||
10519 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10520 }
10521
10522 pfeqoperators[i] = pfeqop;
10523 ppeqoperators[i] = ppeqop;
10524 ffeqoperators[i] = ffeqop;
10525 }
10526
10527 /*
10528 * For FKs with PERIOD we need additional operators to check whether the
10529 * referencing row's range is contained by the aggregated ranges of the
10530 * referenced row(s). For rangetypes and multirangetypes this is
10531 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10532 * support for now. FKs will look these up at "runtime", but we should
10533 * make sure the lookup works here, even if we don't use the values.
10534 */
10535 if (with_period)
10536 {
10537 Oid periodoperoid;
10538 Oid aggedperiodoperoid;
10539 Oid intersectoperoid;
10540
10541 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid,
10542 &intersectoperoid);
10543 }
10544
10545 /* First, create the constraint catalog entry itself. */
10547 fkconstraint->conname, fkconstraint, rel, pkrel,
10548 indexOid,
10549 InvalidOid, /* no parent constraint */
10550 numfks,
10551 pkattnum,
10552 fkattnum,
10553 pfeqoperators,
10554 ppeqoperators,
10555 ffeqoperators,
10556 numfkdelsetcols,
10557 fkdelsetcols,
10558 false,
10559 with_period);
10560
10561 /* Next process the action triggers at the referenced side and recurse */
10562 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10563 indexOid,
10564 address.objectId,
10565 numfks,
10566 pkattnum,
10567 fkattnum,
10568 pfeqoperators,
10569 ppeqoperators,
10570 ffeqoperators,
10571 numfkdelsetcols,
10572 fkdelsetcols,
10573 old_check_ok,
10575 with_period);
10576
10577 /* Lastly create the check triggers at the referencing side and recurse */
10578 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10579 indexOid,
10580 address.objectId,
10581 numfks,
10582 pkattnum,
10583 fkattnum,
10584 pfeqoperators,
10585 ppeqoperators,
10586 ffeqoperators,
10587 numfkdelsetcols,
10588 fkdelsetcols,
10589 old_check_ok,
10590 lockmode,
10592 with_period);
10593
10594 /*
10595 * Done. Close pk table, but keep lock until we've committed.
10596 */
10597 table_close(pkrel, NoLock);
10598
10599 return address;
10600}
10601
10602/*
10603 * validateFkOnDeleteSetColumns
10604 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10605 * column lists are valid.
10606 *
10607 * If there are duplicates in the fksetcolsattnums[] array, this silently
10608 * removes the dups. The new count of numfksetcols is returned.
10609 */
10610static int
10611validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10612 int numfksetcols, int16 *fksetcolsattnums,
10613 List *fksetcols)
10614{
10615 int numcolsout = 0;
10616
10617 for (int i = 0; i < numfksetcols; i++)
10618 {
10619 int16 setcol_attnum = fksetcolsattnums[i];
10620 bool seen = false;
10621
10622 /* Make sure it's in fkattnums[] */
10623 for (int j = 0; j < numfks; j++)
10624 {
10625 if (fkattnums[j] == setcol_attnum)
10626 {
10627 seen = true;
10628 break;
10629 }
10630 }
10631
10632 if (!seen)
10633 {
10634 char *col = strVal(list_nth(fksetcols, i));
10635
10636 ereport(ERROR,
10637 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10638 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10639 }
10640
10641 /* Now check for dups */
10642 seen = false;
10643 for (int j = 0; j < numcolsout; j++)
10644 {
10645 if (fksetcolsattnums[j] == setcol_attnum)
10646 {
10647 seen = true;
10648 break;
10649 }
10650 }
10651 if (!seen)
10652 fksetcolsattnums[numcolsout++] = setcol_attnum;
10653 }
10654 return numcolsout;
10655}
10656
10657/*
10658 * addFkConstraint
10659 * Install pg_constraint entries to implement a foreign key constraint.
10660 * Caller must separately invoke addFkRecurseReferenced and
10661 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10662 * and (for partitioned tables) recurse to partitions.
10663 *
10664 * fkside: the side of the FK (or both) to create. Caller should
10665 * call addFkRecurseReferenced if this is addFkReferencedSide,
10666 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10667 * addFkBothSides.
10668 * constraintname: the base name for the constraint being added,
10669 * copied to fkconstraint->conname if the latter is not set
10670 * fkconstraint: the constraint being added
10671 * rel: the root referencing relation
10672 * pkrel: the referenced relation; might be a partition, if recursing
10673 * indexOid: the OID of the index (on pkrel) implementing this constraint
10674 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10675 * top-level constraint
10676 * numfks: the number of columns in the foreign key
10677 * pkattnum: the attnum array of referenced attributes
10678 * fkattnum: the attnum array of referencing attributes
10679 * pf/pp/ffeqoperators: OID array of operators between columns
10680 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10681 * (...) clause
10682 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10683 * NULL/DEFAULT clause
10684 * with_period: true if this is a temporal FK
10685 */
10686static ObjectAddress
10688 char *constraintname, Constraint *fkconstraint,
10689 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10690 int numfks, int16 *pkattnum,
10691 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10692 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10693 bool is_internal, bool with_period)
10694{
10695 ObjectAddress address;
10696 Oid constrOid;
10697 char *conname;
10698 bool conislocal;
10699 int16 coninhcount;
10700 bool connoinherit;
10701
10702 /*
10703 * Verify relkind for each referenced partition. At the top level, this
10704 * is redundant with a previous check, but we need it when recursing.
10705 */
10706 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10707 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10708 ereport(ERROR,
10709 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10710 errmsg("referenced relation \"%s\" is not a table",
10711 RelationGetRelationName(pkrel))));
10712
10713 /*
10714 * Caller supplies us with a constraint name; however, it may be used in
10715 * this partition, so come up with a different one in that case. Unless
10716 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10717 * supplied name with an underscore and digit(s) appended.
10718 */
10720 RelationGetRelid(rel),
10721 constraintname))
10722 conname = ChooseConstraintName(constraintname,
10723 NULL,
10724 "",
10726 else
10727 conname = constraintname;
10728
10729 if (fkconstraint->conname == NULL)
10730 fkconstraint->conname = pstrdup(conname);
10731
10732 if (OidIsValid(parentConstr))
10733 {
10734 conislocal = false;
10735 coninhcount = 1;
10736 connoinherit = false;
10737 }
10738 else
10739 {
10740 conislocal = true;
10741 coninhcount = 0;
10742
10743 /*
10744 * always inherit for partitioned tables, never for legacy inheritance
10745 */
10746 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10747 }
10748
10749 /*
10750 * Record the FK constraint in pg_constraint.
10751 */
10752 constrOid = CreateConstraintEntry(conname,
10754 CONSTRAINT_FOREIGN,
10755 fkconstraint->deferrable,
10756 fkconstraint->initdeferred,
10757 fkconstraint->is_enforced,
10758 fkconstraint->initially_valid,
10759 parentConstr,
10760 RelationGetRelid(rel),
10761 fkattnum,
10762 numfks,
10763 numfks,
10764 InvalidOid, /* not a domain constraint */
10765 indexOid,
10766 RelationGetRelid(pkrel),
10767 pkattnum,
10768 pfeqoperators,
10769 ppeqoperators,
10770 ffeqoperators,
10771 numfks,
10772 fkconstraint->fk_upd_action,
10773 fkconstraint->fk_del_action,
10774 fkdelsetcols,
10775 numfkdelsetcols,
10776 fkconstraint->fk_matchtype,
10777 NULL, /* no exclusion constraint */
10778 NULL, /* no check constraint */
10779 NULL,
10780 conislocal, /* islocal */
10781 coninhcount, /* inhcount */
10782 connoinherit, /* conNoInherit */
10783 with_period, /* conPeriod */
10784 is_internal); /* is_internal */
10785
10786 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10787
10788 /*
10789 * In partitioning cases, create the dependency entries for this
10790 * constraint. (For non-partitioned cases, relevant entries were created
10791 * by CreateConstraintEntry.)
10792 *
10793 * On the referenced side, we need the constraint to have an internal
10794 * dependency on its parent constraint; this means that this constraint
10795 * cannot be dropped on its own -- only through the parent constraint. It
10796 * also means the containing partition cannot be dropped on its own, but
10797 * it can be detached, at which point this dependency is removed (after
10798 * verifying that no rows are referenced via this FK.)
10799 *
10800 * When processing the referencing side, we link the constraint via the
10801 * special partitioning dependencies: the parent constraint is the primary
10802 * dependent, and the partition on which the foreign key exists is the
10803 * secondary dependency. That way, this constraint is dropped if either
10804 * of these objects is.
10805 *
10806 * Note that this is only necessary for the subsidiary pg_constraint rows
10807 * in partitions; the topmost row doesn't need any of this.
10808 */
10809 if (OidIsValid(parentConstr))
10810 {
10811 ObjectAddress referenced;
10812
10813 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10814
10815 Assert(fkside != addFkBothSides);
10816 if (fkside == addFkReferencedSide)
10817 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10818 else
10819 {
10820 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10821 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10822 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10823 }
10824 }
10825
10826 /* make new constraint visible, in case we add more */
10828
10829 return address;
10830}
10831
10832/*
10833 * addFkRecurseReferenced
10834 * Recursive helper for the referenced side of foreign key creation,
10835 * which creates the action triggers and recurses
10836 *
10837 * If the referenced relation is a plain relation, create the necessary action
10838 * triggers that implement the constraint. If the referenced relation is a
10839 * partitioned table, then we create a pg_constraint row referencing the parent
10840 * of the referencing side for it and recurse on this routine for each
10841 * partition.
10842 *
10843 * fkconstraint: the constraint being added
10844 * rel: the root referencing relation
10845 * pkrel: the referenced relation; might be a partition, if recursing
10846 * indexOid: the OID of the index (on pkrel) implementing this constraint
10847 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10848 * top-level constraint
10849 * numfks: the number of columns in the foreign key
10850 * pkattnum: the attnum array of referenced attributes
10851 * fkattnum: the attnum array of referencing attributes
10852 * numfkdelsetcols: the number of columns in the ON DELETE SET
10853 * NULL/DEFAULT (...) clause
10854 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10855 * NULL/DEFAULT clause
10856 * pf/pp/ffeqoperators: OID array of operators between columns
10857 * old_check_ok: true if this constraint replaces an existing one that
10858 * was already validated (thus this one doesn't need validation)
10859 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10860 * partition, the OIDs of the parent action triggers for DELETE and
10861 * UPDATE respectively.
10862 * with_period: true if this is a temporal FK
10863 */
10864static void
10866 Relation pkrel, Oid indexOid, Oid parentConstr,
10867 int numfks,
10868 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10869 Oid *ppeqoperators, Oid *ffeqoperators,
10870 int numfkdelsetcols, int16 *fkdelsetcols,
10871 bool old_check_ok,
10872 Oid parentDelTrigger, Oid parentUpdTrigger,
10873 bool with_period)
10874{
10875 Oid deleteTriggerOid = InvalidOid,
10876 updateTriggerOid = InvalidOid;
10877
10880
10881 /*
10882 * Create action triggers to enforce the constraint, or skip them if the
10883 * constraint is NOT ENFORCED.
10884 */
10885 if (fkconstraint->is_enforced)
10887 RelationGetRelid(pkrel),
10888 fkconstraint,
10889 parentConstr, indexOid,
10890 parentDelTrigger, parentUpdTrigger,
10891 &deleteTriggerOid, &updateTriggerOid);
10892
10893 /*
10894 * If the referenced table is partitioned, recurse on ourselves to handle
10895 * each partition. We need one pg_constraint row created for each
10896 * partition in addition to the pg_constraint row for the parent table.
10897 */
10898 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10899 {
10900 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10901
10902 for (int i = 0; i < pd->nparts; i++)
10903 {
10904 Relation partRel;
10905 AttrMap *map;
10906 AttrNumber *mapped_pkattnum;
10907 Oid partIndexId;
10908 ObjectAddress address;
10909
10910 /* XXX would it be better to acquire these locks beforehand? */
10911 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10912
10913 /*
10914 * Map the attribute numbers in the referenced side of the FK
10915 * definition to match the partition's column layout.
10916 */
10918 RelationGetDescr(pkrel),
10919 false);
10920 if (map)
10921 {
10922 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10923 for (int j = 0; j < numfks; j++)
10924 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10925 }
10926 else
10927 mapped_pkattnum = pkattnum;
10928
10929 /* Determine the index to use at this level */
10930 partIndexId = index_get_partition(partRel, indexOid);
10931 if (!OidIsValid(partIndexId))
10932 elog(ERROR, "index for %u not found in partition %s",
10933 indexOid, RelationGetRelationName(partRel));
10934
10935 /* Create entry at this level ... */
10937 fkconstraint->conname, fkconstraint, rel,
10938 partRel, partIndexId, parentConstr,
10939 numfks, mapped_pkattnum,
10940 fkattnum, pfeqoperators, ppeqoperators,
10941 ffeqoperators, numfkdelsetcols,
10942 fkdelsetcols, true, with_period);
10943 /* ... and recurse to our children */
10944 addFkRecurseReferenced(fkconstraint, rel, partRel,
10945 partIndexId, address.objectId, numfks,
10946 mapped_pkattnum, fkattnum,
10947 pfeqoperators, ppeqoperators, ffeqoperators,
10948 numfkdelsetcols, fkdelsetcols,
10949 old_check_ok,
10950 deleteTriggerOid, updateTriggerOid,
10951 with_period);
10952
10953 /* Done -- clean up (but keep the lock) */
10954 table_close(partRel, NoLock);
10955 if (map)
10956 {
10957 pfree(mapped_pkattnum);
10958 free_attrmap(map);
10959 }
10960 }
10961 }
10962}
10963
10964/*
10965 * addFkRecurseReferencing
10966 * Recursive helper for the referencing side of foreign key creation,
10967 * which creates the check triggers and recurses
10968 *
10969 * If the referencing relation is a plain relation, create the necessary check
10970 * triggers that implement the constraint, and set up for Phase 3 constraint
10971 * verification. If the referencing relation is a partitioned table, then
10972 * we create a pg_constraint row for it and recurse on this routine for each
10973 * partition.
10974 *
10975 * We assume that the referenced relation is locked against concurrent
10976 * deletions. If it's a partitioned relation, every partition must be so
10977 * locked.
10978 *
10979 * wqueue: the ALTER TABLE work queue; NULL when not running as part
10980 * of an ALTER TABLE sequence.
10981 * fkconstraint: the constraint being added
10982 * rel: the referencing relation; might be a partition, if recursing
10983 * pkrel: the root referenced relation
10984 * indexOid: the OID of the index (on pkrel) implementing this constraint
10985 * parentConstr: the OID of the parent constraint (there is always one)
10986 * numfks: the number of columns in the foreign key
10987 * pkattnum: the attnum array of referenced attributes
10988 * fkattnum: the attnum array of referencing attributes
10989 * pf/pp/ffeqoperators: OID array of operators between columns
10990 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10991 * (...) clause
10992 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10993 * NULL/DEFAULT clause
10994 * old_check_ok: true if this constraint replaces an existing one that
10995 * was already validated (thus this one doesn't need validation)
10996 * lockmode: the lockmode to acquire on partitions when recursing
10997 * parentInsTrigger and parentUpdTrigger: when being recursively called on
10998 * a partition, the OIDs of the parent check triggers for INSERT and
10999 * UPDATE respectively.
11000 * with_period: true if this is a temporal FK
11001 */
11002static void
11004 Relation pkrel, Oid indexOid, Oid parentConstr,
11005 int numfks, int16 *pkattnum, int16 *fkattnum,
11006 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
11007 int numfkdelsetcols, int16 *fkdelsetcols,
11008 bool old_check_ok, LOCKMODE lockmode,
11009 Oid parentInsTrigger, Oid parentUpdTrigger,
11010 bool with_period)
11011{
11012 Oid insertTriggerOid = InvalidOid,
11013 updateTriggerOid = InvalidOid;
11014
11015 Assert(OidIsValid(parentConstr));
11018
11019 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11020 ereport(ERROR,
11021 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11022 errmsg("foreign key constraints are not supported on foreign tables")));
11023
11024 /*
11025 * Add check triggers if the constraint is ENFORCED, and if needed,
11026 * schedule them to be checked in Phase 3.
11027 *
11028 * If the relation is partitioned, drill down to do it to its partitions.
11029 */
11030 if (fkconstraint->is_enforced)
11032 RelationGetRelid(pkrel),
11033 fkconstraint,
11034 parentConstr,
11035 indexOid,
11036 parentInsTrigger, parentUpdTrigger,
11037 &insertTriggerOid, &updateTriggerOid);
11038
11039 if (rel->rd_rel->relkind == RELKIND_RELATION)
11040 {
11041 /*
11042 * Tell Phase 3 to check that the constraint is satisfied by existing
11043 * rows. We can skip this during table creation, when constraint is
11044 * specified as NOT ENFORCED, or when requested explicitly by
11045 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11046 * recreating a constraint following a SET DATA TYPE operation that
11047 * did not impugn its validity.
11048 */
11049 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11050 fkconstraint->is_enforced)
11051 {
11052 NewConstraint *newcon;
11053 AlteredTableInfo *tab;
11054
11055 tab = ATGetQueueEntry(wqueue, rel);
11056
11057 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11058 newcon->name = get_constraint_name(parentConstr);
11059 newcon->contype = CONSTR_FOREIGN;
11060 newcon->refrelid = RelationGetRelid(pkrel);
11061 newcon->refindid = indexOid;
11062 newcon->conid = parentConstr;
11063 newcon->conwithperiod = fkconstraint->fk_with_period;
11064 newcon->qual = (Node *) fkconstraint;
11065
11066 tab->constraints = lappend(tab->constraints, newcon);
11067 }
11068 }
11069 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11070 {
11072 Relation trigrel;
11073
11074 /*
11075 * Triggers of the foreign keys will be manipulated a bunch of times
11076 * in the loop below. To avoid repeatedly opening/closing the trigger
11077 * catalog relation, we open it here and pass it to the subroutines
11078 * called below.
11079 */
11080 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11081
11082 /*
11083 * Recurse to take appropriate action on each partition; either we
11084 * find an existing constraint to reparent to ours, or we create a new
11085 * one.
11086 */
11087 for (int i = 0; i < pd->nparts; i++)
11088 {
11089 Relation partition = table_open(pd->oids[i], lockmode);
11090 List *partFKs;
11091 AttrMap *attmap;
11092 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
11093 bool attached;
11094 ObjectAddress address;
11095
11096 CheckAlterTableIsSafe(partition);
11097
11098 attmap = build_attrmap_by_name(RelationGetDescr(partition),
11099 RelationGetDescr(rel),
11100 false);
11101 for (int j = 0; j < numfks; j++)
11102 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11103
11104 /* Check whether an existing constraint can be repurposed */
11105 partFKs = copyObject(RelationGetFKeyList(partition));
11106 attached = false;
11107 foreach_node(ForeignKeyCacheInfo, fk, partFKs)
11108 {
11110 fk,
11111 partition,
11112 parentConstr,
11113 numfks,
11114 mapped_fkattnum,
11115 pkattnum,
11116 pfeqoperators,
11117 insertTriggerOid,
11118 updateTriggerOid,
11119 trigrel))
11120 {
11121 attached = true;
11122 break;
11123 }
11124 }
11125 if (attached)
11126 {
11127 table_close(partition, NoLock);
11128 continue;
11129 }
11130
11131 /*
11132 * No luck finding a good constraint to reuse; create our own.
11133 */
11135 fkconstraint->conname, fkconstraint,
11136 partition, pkrel, indexOid, parentConstr,
11137 numfks, pkattnum,
11138 mapped_fkattnum, pfeqoperators,
11139 ppeqoperators, ffeqoperators,
11140 numfkdelsetcols, fkdelsetcols, true,
11141 with_period);
11142
11143 /* call ourselves to finalize the creation and we're done */
11144 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
11145 indexOid,
11146 address.objectId,
11147 numfks,
11148 pkattnum,
11149 mapped_fkattnum,
11150 pfeqoperators,
11151 ppeqoperators,
11152 ffeqoperators,
11153 numfkdelsetcols,
11154 fkdelsetcols,
11155 old_check_ok,
11156 lockmode,
11157 insertTriggerOid,
11158 updateTriggerOid,
11159 with_period);
11160
11161 table_close(partition, NoLock);
11162 }
11163
11164 table_close(trigrel, RowExclusiveLock);
11165 }
11166}
11167
11168/*
11169 * CloneForeignKeyConstraints
11170 * Clone foreign keys from a partitioned table to a newly acquired
11171 * partition.
11172 *
11173 * partitionRel is a partition of parentRel, so we can be certain that it has
11174 * the same columns with the same datatypes. The columns may be in different
11175 * order, though.
11176 *
11177 * wqueue must be passed to set up phase 3 constraint checking, unless the
11178 * referencing-side partition is known to be empty (such as in CREATE TABLE /
11179 * PARTITION OF).
11180 */
11181static void
11183 Relation partitionRel)
11184{
11185 /* This only works for declarative partitioning */
11186 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11187
11188 /*
11189 * First, clone constraints where the parent is on the referencing side.
11190 */
11191 CloneFkReferencing(wqueue, parentRel, partitionRel);
11192
11193 /*
11194 * Clone constraints for which the parent is on the referenced side.
11195 */
11196 CloneFkReferenced(parentRel, partitionRel);
11197}
11198
11199/*
11200 * CloneFkReferenced
11201 * Subroutine for CloneForeignKeyConstraints
11202 *
11203 * Find all the FKs that have the parent relation on the referenced side;
11204 * clone those constraints to the given partition. This is to be called
11205 * when the partition is being created or attached.
11206 *
11207 * This recurses to partitions, if the relation being attached is partitioned.
11208 * Recursion is done by calling addFkRecurseReferenced.
11209 */
11210static void
11211CloneFkReferenced(Relation parentRel, Relation partitionRel)
11212{
11213 Relation pg_constraint;
11214 AttrMap *attmap;
11215 ListCell *cell;
11216 SysScanDesc scan;
11217 ScanKeyData key[2];
11218 HeapTuple tuple;
11219 List *clone = NIL;
11220 Relation trigrel;
11221
11222 /*
11223 * Search for any constraints where this partition's parent is in the
11224 * referenced side. However, we must not clone any constraint whose
11225 * parent constraint is also going to be cloned, to avoid duplicates. So
11226 * do it in two steps: first construct the list of constraints to clone,
11227 * then go over that list cloning those whose parents are not in the list.
11228 * (We must not rely on the parent being seen first, since the catalog
11229 * scan could return children first.)
11230 */
11231 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11232 ScanKeyInit(&key[0],
11233 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11234 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11235 ScanKeyInit(&key[1],
11236 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11237 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11238 /* This is a seqscan, as we don't have a usable index ... */
11239 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11240 NULL, 2, key);
11241 while ((tuple = systable_getnext(scan)) != NULL)
11242 {
11243 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11244
11245 clone = lappend_oid(clone, constrForm->oid);
11246 }
11247 systable_endscan(scan);
11248 table_close(pg_constraint, RowShareLock);
11249
11250 /*
11251 * Triggers of the foreign keys will be manipulated a bunch of times in
11252 * the loop below. To avoid repeatedly opening/closing the trigger
11253 * catalog relation, we open it here and pass it to the subroutines called
11254 * below.
11255 */
11256 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11257
11258 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11259 RelationGetDescr(parentRel),
11260 false);
11261 foreach(cell, clone)
11262 {
11263 Oid constrOid = lfirst_oid(cell);
11264 Form_pg_constraint constrForm;
11265 Relation fkRel;
11266 Oid indexOid;
11267 Oid partIndexId;
11268 int numfks;
11269 AttrNumber conkey[INDEX_MAX_KEYS];
11270 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11271 AttrNumber confkey[INDEX_MAX_KEYS];
11272 Oid conpfeqop[INDEX_MAX_KEYS];
11273 Oid conppeqop[INDEX_MAX_KEYS];
11274 Oid conffeqop[INDEX_MAX_KEYS];
11275 int numfkdelsetcols;
11276 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11277 Constraint *fkconstraint;
11278 ObjectAddress address;
11279 Oid deleteTriggerOid = InvalidOid,
11280 updateTriggerOid = InvalidOid;
11281
11282 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11283 if (!HeapTupleIsValid(tuple))
11284 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11285 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11286
11287 /*
11288 * As explained above: don't try to clone a constraint for which we're
11289 * going to clone the parent.
11290 */
11291 if (list_member_oid(clone, constrForm->conparentid))
11292 {
11293 ReleaseSysCache(tuple);
11294 continue;
11295 }
11296
11297 /* We need the same lock level that CreateTrigger will acquire */
11298 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11299
11300 indexOid = constrForm->conindid;
11302 &numfks,
11303 conkey,
11304 confkey,
11305 conpfeqop,
11306 conppeqop,
11307 conffeqop,
11308 &numfkdelsetcols,
11309 confdelsetcols);
11310
11311 for (int i = 0; i < numfks; i++)
11312 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11313
11314 fkconstraint = makeNode(Constraint);
11315 fkconstraint->contype = CONSTRAINT_FOREIGN;
11316 fkconstraint->conname = NameStr(constrForm->conname);
11317 fkconstraint->deferrable = constrForm->condeferrable;
11318 fkconstraint->initdeferred = constrForm->condeferred;
11319 fkconstraint->location = -1;
11320 fkconstraint->pktable = NULL;
11321 /* ->fk_attrs determined below */
11322 fkconstraint->pk_attrs = NIL;
11323 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11324 fkconstraint->fk_upd_action = constrForm->confupdtype;
11325 fkconstraint->fk_del_action = constrForm->confdeltype;
11326 fkconstraint->fk_del_set_cols = NIL;
11327 fkconstraint->old_conpfeqop = NIL;
11328 fkconstraint->old_pktable_oid = InvalidOid;
11329 fkconstraint->is_enforced = constrForm->conenforced;
11330 fkconstraint->skip_validation = false;
11331 fkconstraint->initially_valid = constrForm->convalidated;
11332
11333 /* set up colnames that are used to generate the constraint name */
11334 for (int i = 0; i < numfks; i++)
11335 {
11337
11338 att = TupleDescAttr(RelationGetDescr(fkRel),
11339 conkey[i] - 1);
11340 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11341 makeString(NameStr(att->attname)));
11342 }
11343
11344 /*
11345 * Add the new foreign key constraint pointing to the new partition.
11346 * Because this new partition appears in the referenced side of the
11347 * constraint, we don't need to set up for Phase 3 check.
11348 */
11349 partIndexId = index_get_partition(partitionRel, indexOid);
11350 if (!OidIsValid(partIndexId))
11351 elog(ERROR, "index for %u not found in partition %s",
11352 indexOid, RelationGetRelationName(partitionRel));
11353
11354 /*
11355 * Get the "action" triggers belonging to the constraint to pass as
11356 * parent OIDs for similar triggers that will be created on the
11357 * partition in addFkRecurseReferenced().
11358 */
11359 if (constrForm->conenforced)
11360 GetForeignKeyActionTriggers(trigrel, constrOid,
11361 constrForm->confrelid, constrForm->conrelid,
11362 &deleteTriggerOid, &updateTriggerOid);
11363
11364 /* Add this constraint ... */
11366 fkconstraint->conname, fkconstraint, fkRel,
11367 partitionRel, partIndexId, constrOid,
11368 numfks, mapped_confkey,
11369 conkey, conpfeqop, conppeqop, conffeqop,
11370 numfkdelsetcols, confdelsetcols, false,
11371 constrForm->conperiod);
11372 /* ... and recurse */
11373 addFkRecurseReferenced(fkconstraint,
11374 fkRel,
11375 partitionRel,
11376 partIndexId,
11377 address.objectId,
11378 numfks,
11379 mapped_confkey,
11380 conkey,
11381 conpfeqop,
11382 conppeqop,
11383 conffeqop,
11384 numfkdelsetcols,
11385 confdelsetcols,
11386 true,
11387 deleteTriggerOid,
11388 updateTriggerOid,
11389 constrForm->conperiod);
11390
11391 table_close(fkRel, NoLock);
11392 ReleaseSysCache(tuple);
11393 }
11394
11395 table_close(trigrel, RowExclusiveLock);
11396}
11397
11398/*
11399 * CloneFkReferencing
11400 * Subroutine for CloneForeignKeyConstraints
11401 *
11402 * For each FK constraint of the parent relation in the given list, find an
11403 * equivalent constraint in its partition relation that can be reparented;
11404 * if one cannot be found, create a new constraint in the partition as its
11405 * child.
11406 *
11407 * If wqueue is given, it is used to set up phase-3 verification for each
11408 * cloned constraint; omit it if such verification is not needed
11409 * (example: the partition is being created anew).
11410 */
11411static void
11412CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11413{
11414 AttrMap *attmap;
11415 List *partFKs;
11416 List *clone = NIL;
11417 ListCell *cell;
11418 Relation trigrel;
11419
11420 /* obtain a list of constraints that we need to clone */
11421 foreach(cell, RelationGetFKeyList(parentRel))
11422 {
11423 ForeignKeyCacheInfo *fk = lfirst(cell);
11424
11425 /*
11426 * Refuse to attach a table as partition that this partitioned table
11427 * already has a foreign key to. This isn't useful schema, which is
11428 * proven by the fact that there have been no user complaints that
11429 * it's already impossible to achieve this in the opposite direction,
11430 * i.e., creating a foreign key that references a partition. This
11431 * restriction allows us to dodge some complexities around
11432 * pg_constraint and pg_trigger row creations that would be needed
11433 * during ATTACH/DETACH for this kind of relationship.
11434 */
11435 if (fk->confrelid == RelationGetRelid(partRel))
11436 ereport(ERROR,
11437 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11438 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11439 RelationGetRelationName(partRel),
11441
11442 clone = lappend_oid(clone, fk->conoid);
11443 }
11444
11445 /*
11446 * Silently do nothing if there's nothing to do. In particular, this
11447 * avoids throwing a spurious error for foreign tables.
11448 */
11449 if (clone == NIL)
11450 return;
11451
11452 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11453 ereport(ERROR,
11454 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11455 errmsg("foreign key constraints are not supported on foreign tables")));
11456
11457 /*
11458 * Triggers of the foreign keys will be manipulated a bunch of times in
11459 * the loop below. To avoid repeatedly opening/closing the trigger
11460 * catalog relation, we open it here and pass it to the subroutines called
11461 * below.
11462 */
11463 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11464
11465 /*
11466 * The constraint key may differ, if the columns in the partition are
11467 * different. This map is used to convert them.
11468 */
11469 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11470 RelationGetDescr(parentRel),
11471 false);
11472
11473 partFKs = copyObject(RelationGetFKeyList(partRel));
11474
11475 foreach(cell, clone)
11476 {
11477 Oid parentConstrOid = lfirst_oid(cell);
11478 Form_pg_constraint constrForm;
11479 Relation pkrel;
11480 HeapTuple tuple;
11481 int numfks;
11482 AttrNumber conkey[INDEX_MAX_KEYS];
11483 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11484 AttrNumber confkey[INDEX_MAX_KEYS];
11485 Oid conpfeqop[INDEX_MAX_KEYS];
11486 Oid conppeqop[INDEX_MAX_KEYS];
11487 Oid conffeqop[INDEX_MAX_KEYS];
11488 int numfkdelsetcols;
11489 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11490 Constraint *fkconstraint;
11491 bool attached;
11492 Oid indexOid;
11493 ObjectAddress address;
11494 ListCell *lc;
11495 Oid insertTriggerOid = InvalidOid,
11496 updateTriggerOid = InvalidOid;
11497 bool with_period;
11498
11499 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11500 if (!HeapTupleIsValid(tuple))
11501 elog(ERROR, "cache lookup failed for constraint %u",
11502 parentConstrOid);
11503 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11504
11505 /* Don't clone constraints whose parents are being cloned */
11506 if (list_member_oid(clone, constrForm->conparentid))
11507 {
11508 ReleaseSysCache(tuple);
11509 continue;
11510 }
11511
11512 /*
11513 * Need to prevent concurrent deletions. If pkrel is a partitioned
11514 * relation, that means to lock all partitions.
11515 */
11516 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11517 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11519 ShareRowExclusiveLock, NULL);
11520
11521 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11522 conpfeqop, conppeqop, conffeqop,
11523 &numfkdelsetcols, confdelsetcols);
11524 for (int i = 0; i < numfks; i++)
11525 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11526
11527 /*
11528 * Get the "check" triggers belonging to the constraint, if it is
11529 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11530 * created on the partition in addFkRecurseReferencing(). They are
11531 * also passed to tryAttachPartitionForeignKey() below to simply
11532 * assign as parents to the partition's existing "check" triggers,
11533 * that is, if the corresponding constraints is deemed attachable to
11534 * the parent constraint.
11535 */
11536 if (constrForm->conenforced)
11537 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11538 constrForm->confrelid, constrForm->conrelid,
11539 &insertTriggerOid, &updateTriggerOid);
11540
11541 /*
11542 * Before creating a new constraint, see whether any existing FKs are
11543 * fit for the purpose. If one is, attach the parent constraint to
11544 * it, and don't clone anything. This way we avoid the expensive
11545 * verification step and don't end up with a duplicate FK, and we
11546 * don't need to recurse to partitions for this constraint.
11547 */
11548 attached = false;
11549 foreach(lc, partFKs)
11550 {
11552
11554 fk,
11555 partRel,
11556 parentConstrOid,
11557 numfks,
11558 mapped_conkey,
11559 confkey,
11560 conpfeqop,
11561 insertTriggerOid,
11562 updateTriggerOid,
11563 trigrel))
11564 {
11565 attached = true;
11566 table_close(pkrel, NoLock);
11567 break;
11568 }
11569 }
11570 if (attached)
11571 {
11572 ReleaseSysCache(tuple);
11573 continue;
11574 }
11575
11576 /* No dice. Set up to create our own constraint */
11577 fkconstraint = makeNode(Constraint);
11578 fkconstraint->contype = CONSTRAINT_FOREIGN;
11579 /* ->conname determined below */
11580 fkconstraint->deferrable = constrForm->condeferrable;
11581 fkconstraint->initdeferred = constrForm->condeferred;
11582 fkconstraint->location = -1;
11583 fkconstraint->pktable = NULL;
11584 /* ->fk_attrs determined below */
11585 fkconstraint->pk_attrs = NIL;
11586 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11587 fkconstraint->fk_upd_action = constrForm->confupdtype;
11588 fkconstraint->fk_del_action = constrForm->confdeltype;
11589 fkconstraint->fk_del_set_cols = NIL;
11590 fkconstraint->old_conpfeqop = NIL;
11591 fkconstraint->old_pktable_oid = InvalidOid;
11592 fkconstraint->is_enforced = constrForm->conenforced;
11593 fkconstraint->skip_validation = false;
11594 fkconstraint->initially_valid = constrForm->convalidated;
11595 for (int i = 0; i < numfks; i++)
11596 {
11598
11599 att = TupleDescAttr(RelationGetDescr(partRel),
11600 mapped_conkey[i] - 1);
11601 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11602 makeString(NameStr(att->attname)));
11603 }
11604
11605 indexOid = constrForm->conindid;
11606 with_period = constrForm->conperiod;
11607
11608 /* Create the pg_constraint entry at this level */
11610 NameStr(constrForm->conname), fkconstraint,
11611 partRel, pkrel, indexOid, parentConstrOid,
11612 numfks, confkey,
11613 mapped_conkey, conpfeqop,
11614 conppeqop, conffeqop,
11615 numfkdelsetcols, confdelsetcols,
11616 false, with_period);
11617
11618 /* Done with the cloned constraint's tuple */
11619 ReleaseSysCache(tuple);
11620
11621 /* Create the check triggers, and recurse to partitions, if any */
11623 fkconstraint,
11624 partRel,
11625 pkrel,
11626 indexOid,
11627 address.objectId,
11628 numfks,
11629 confkey,
11630 mapped_conkey,
11631 conpfeqop,
11632 conppeqop,
11633 conffeqop,
11634 numfkdelsetcols,
11635 confdelsetcols,
11636 false, /* no old check exists */
11638 insertTriggerOid,
11639 updateTriggerOid,
11640 with_period);
11641 table_close(pkrel, NoLock);
11642 }
11643
11644 table_close(trigrel, RowExclusiveLock);
11645}
11646
11647/*
11648 * When the parent of a partition receives [the referencing side of] a foreign
11649 * key, we must propagate that foreign key to the partition. However, the
11650 * partition might already have an equivalent foreign key; this routine
11651 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11652 * by the other parameters. If they are equivalent, create the link between
11653 * the two constraints and return true.
11654 *
11655 * If the given FK does not match the one defined by rest of the params,
11656 * return false.
11657 */
11658static bool
11661 Relation partition,
11662 Oid parentConstrOid,
11663 int numfks,
11664 AttrNumber *mapped_conkey,
11665 AttrNumber *confkey,
11666 Oid *conpfeqop,
11667 Oid parentInsTrigger,
11668 Oid parentUpdTrigger,
11669 Relation trigrel)
11670{
11671 HeapTuple parentConstrTup;
11672 Form_pg_constraint parentConstr;
11673 HeapTuple partcontup;
11674 Form_pg_constraint partConstr;
11675
11676 parentConstrTup = SearchSysCache1(CONSTROID,
11677 ObjectIdGetDatum(parentConstrOid));
11678 if (!HeapTupleIsValid(parentConstrTup))
11679 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11680 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11681
11682 /*
11683 * Do some quick & easy initial checks. If any of these fail, we cannot
11684 * use this constraint.
11685 */
11686 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11687 {
11688 ReleaseSysCache(parentConstrTup);
11689 return false;
11690 }
11691 for (int i = 0; i < numfks; i++)
11692 {
11693 if (fk->conkey[i] != mapped_conkey[i] ||
11694 fk->confkey[i] != confkey[i] ||
11695 fk->conpfeqop[i] != conpfeqop[i])
11696 {
11697 ReleaseSysCache(parentConstrTup);
11698 return false;
11699 }
11700 }
11701
11702 /* Looks good so far; perform more extensive checks. */
11703 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
11704 if (!HeapTupleIsValid(partcontup))
11705 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11706 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11707
11708 /*
11709 * An error should be raised if the constraint enforceability is
11710 * different. Returning false without raising an error, as we do for other
11711 * attributes, could lead to a duplicate constraint with the same
11712 * enforceability as the parent. While this may be acceptable, it may not
11713 * be ideal. Therefore, it's better to raise an error and allow the user
11714 * to correct the enforceability before proceeding.
11715 */
11716 if (partConstr->conenforced != parentConstr->conenforced)
11717 ereport(ERROR,
11718 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
11719 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11720 NameStr(parentConstr->conname),
11721 NameStr(partConstr->conname),
11722 RelationGetRelationName(partition))));
11723
11724 if (OidIsValid(partConstr->conparentid) ||
11725 partConstr->condeferrable != parentConstr->condeferrable ||
11726 partConstr->condeferred != parentConstr->condeferred ||
11727 partConstr->confupdtype != parentConstr->confupdtype ||
11728 partConstr->confdeltype != parentConstr->confdeltype ||
11729 partConstr->confmatchtype != parentConstr->confmatchtype)
11730 {
11731 ReleaseSysCache(parentConstrTup);
11732 ReleaseSysCache(partcontup);
11733 return false;
11734 }
11735
11736 ReleaseSysCache(parentConstrTup);
11737 ReleaseSysCache(partcontup);
11738
11739 /* Looks good! Attach this constraint. */
11740 AttachPartitionForeignKey(wqueue, partition, fk->conoid,
11741 parentConstrOid, parentInsTrigger,
11742 parentUpdTrigger, trigrel);
11743
11744 return true;
11745}
11746
11747/*
11748 * AttachPartitionForeignKey
11749 *
11750 * The subroutine for tryAttachPartitionForeignKey performs the final tasks of
11751 * attaching the constraint, removing redundant triggers and entries from
11752 * pg_constraint, and setting the constraint's parent.
11753 */
11754static void
11756 Relation partition,
11757 Oid partConstrOid,
11758 Oid parentConstrOid,
11759 Oid parentInsTrigger,
11760 Oid parentUpdTrigger,
11761 Relation trigrel)
11762{
11763 HeapTuple parentConstrTup;
11764 Form_pg_constraint parentConstr;
11765 HeapTuple partcontup;
11766 Form_pg_constraint partConstr;
11767 bool queueValidation;
11768 Oid partConstrFrelid;
11769 Oid partConstrRelid;
11770 bool parentConstrIsEnforced;
11771
11772 /* Fetch the parent constraint tuple */
11773 parentConstrTup = SearchSysCache1(CONSTROID,
11774 ObjectIdGetDatum(parentConstrOid));
11775 if (!HeapTupleIsValid(parentConstrTup))
11776 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11777 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11778 parentConstrIsEnforced = parentConstr->conenforced;
11779
11780 /* Fetch the child constraint tuple */
11781 partcontup = SearchSysCache1(CONSTROID,
11782 ObjectIdGetDatum(partConstrOid));
11783 if (!HeapTupleIsValid(partcontup))
11784 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11785 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11786 partConstrFrelid = partConstr->confrelid;
11787 partConstrRelid = partConstr->conrelid;
11788
11789 /*
11790 * If the referenced table is partitioned, then the partition we're
11791 * attaching now has extra pg_constraint rows and action triggers that are
11792 * no longer needed. Remove those.
11793 */
11794 if (get_rel_relkind(partConstrFrelid) == RELKIND_PARTITIONED_TABLE)
11795 {
11796 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11797
11798 RemoveInheritedConstraint(pg_constraint, trigrel, partConstrOid,
11799 partConstrRelid);
11800
11801 table_close(pg_constraint, RowShareLock);
11802 }
11803
11804 /*
11805 * Will we need to validate this constraint? A valid parent constraint
11806 * implies that all child constraints have been validated, so if this one
11807 * isn't, we must trigger phase 3 validation.
11808 */
11809 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11810
11811 ReleaseSysCache(partcontup);
11812 ReleaseSysCache(parentConstrTup);
11813
11814 /*
11815 * The action triggers in the new partition become redundant -- the parent
11816 * table already has equivalent ones, and those will be able to reach the
11817 * partition. Remove the ones in the partition. We identify them because
11818 * they have our constraint OID, as well as being on the referenced rel.
11819 */
11820 DropForeignKeyConstraintTriggers(trigrel, partConstrOid, partConstrFrelid,
11821 partConstrRelid);
11822
11823 ConstraintSetParentConstraint(partConstrOid, parentConstrOid,
11824 RelationGetRelid(partition));
11825
11826 /*
11827 * Like the constraint, attach partition's "check" triggers to the
11828 * corresponding parent triggers if the constraint is ENFORCED. NOT
11829 * ENFORCED constraints do not have these triggers.
11830 */
11831 if (parentConstrIsEnforced)
11832 {
11833 Oid insertTriggerOid,
11834 updateTriggerOid;
11835
11837 partConstrOid, partConstrFrelid, partConstrRelid,
11838 &insertTriggerOid, &updateTriggerOid);
11839 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11840 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11841 RelationGetRelid(partition));
11842 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11843 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11844 RelationGetRelid(partition));
11845 }
11846
11847 /*
11848 * We updated this pg_constraint row above to set its parent; validating
11849 * it will cause its convalidated flag to change, so we need CCI here. In
11850 * addition, we need it unconditionally for the rare case where the parent
11851 * table has *two* identical constraints; when reaching this function for
11852 * the second one, we must have made our changes visible, otherwise we
11853 * would try to attach both to this one.
11854 */
11856
11857 /* If validation is needed, put it in the queue now. */
11858 if (queueValidation)
11859 {
11860 Relation conrel;
11861
11862 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11863
11864 partcontup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(partConstrOid));
11865 if (!HeapTupleIsValid(partcontup))
11866 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11867
11868 /* Use the same lock as for AT_ValidateConstraint */
11869 QueueFKConstraintValidation(wqueue, conrel, partition, partcontup,
11871 ReleaseSysCache(partcontup);
11873 }
11874}
11875
11876/*
11877 * RemoveInheritedConstraint
11878 *
11879 * Removes the constraint and its associated trigger from the specified
11880 * relation, which inherited the given constraint.
11881 */
11882static void
11884 Oid conrelid)
11885{
11886 ObjectAddresses *objs;
11887 HeapTuple consttup;
11889 SysScanDesc scan;
11890 HeapTuple trigtup;
11891
11893 Anum_pg_constraint_conrelid,
11894 BTEqualStrategyNumber, F_OIDEQ,
11895 ObjectIdGetDatum(conrelid));
11896
11897 scan = systable_beginscan(conrel,
11898 ConstraintRelidTypidNameIndexId,
11899 true, NULL, 1, &key);
11900 objs = new_object_addresses();
11901 while ((consttup = systable_getnext(scan)) != NULL)
11902 {
11903 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11904
11905 if (conform->conparentid != conoid)
11906 continue;
11907 else
11908 {
11909 ObjectAddress addr;
11910 SysScanDesc scan2;
11911 ScanKeyData key2;
11913
11914 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11915 add_exact_object_address(&addr, objs);
11916
11917 /*
11918 * First we must delete the dependency record that binds the
11919 * constraint records together.
11920 */
11921 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11922 conform->oid,
11924 ConstraintRelationId,
11925 conoid);
11926 Assert(n == 1); /* actually only one is expected */
11927
11928 /*
11929 * Now search for the triggers for this constraint and set them up
11930 * for deletion too
11931 */
11932 ScanKeyInit(&key2,
11933 Anum_pg_trigger_tgconstraint,
11934 BTEqualStrategyNumber, F_OIDEQ,
11935 ObjectIdGetDatum(conform->oid));
11936 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11937 true, NULL, 1, &key2);
11938 while ((trigtup = systable_getnext(scan2)) != NULL)
11939 {
11940 ObjectAddressSet(addr, TriggerRelationId,
11941 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11942 add_exact_object_address(&addr, objs);
11943 }
11944 systable_endscan(scan2);
11945 }
11946 }
11947 /* make the dependency deletions visible */
11951 systable_endscan(scan);
11952}
11953
11954/*
11955 * DropForeignKeyConstraintTriggers
11956 *
11957 * The subroutine for tryAttachPartitionForeignKey handles the deletion of
11958 * action triggers for the foreign key constraint.
11959 *
11960 * If valid confrelid and conrelid values are not provided, the respective
11961 * trigger check will be skipped, and the trigger will be considered for
11962 * removal.
11963 */
11964static void
11966 Oid conrelid)
11967{
11969 SysScanDesc scan;
11970 HeapTuple trigtup;
11971
11973 Anum_pg_trigger_tgconstraint,
11974 BTEqualStrategyNumber, F_OIDEQ,
11975 ObjectIdGetDatum(conoid));
11976 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11977 NULL, 1, &key);
11978 while ((trigtup = systable_getnext(scan)) != NULL)
11979 {
11980 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11981 ObjectAddress trigger;
11982
11983 /* Invalid if trigger is not for a referential integrity constraint */
11984 if (!OidIsValid(trgform->tgconstrrelid))
11985 continue;
11986 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
11987 continue;
11988 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
11989 continue;
11990
11991 /* We should be dropping trigger related to foreign key constraint */
11992 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
11993 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
11994 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
11995 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
11996 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
11997 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
11998 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
11999 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12000 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12001 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12002 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12003 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12004
12005 /*
12006 * The constraint is originally set up to contain this trigger as an
12007 * implementation object, so there's a dependency record that links
12008 * the two; however, since the trigger is no longer needed, we remove
12009 * the dependency link in order to be able to drop the trigger while
12010 * keeping the constraint intact.
12011 */
12012 deleteDependencyRecordsFor(TriggerRelationId,
12013 trgform->oid,
12014 false);
12015 /* make dependency deletion visible to performDeletion */
12017 ObjectAddressSet(trigger, TriggerRelationId,
12018 trgform->oid);
12019 performDeletion(&trigger, DROP_RESTRICT, 0);
12020 /* make trigger drop visible, in case the loop iterates */
12022 }
12023
12024 systable_endscan(scan);
12025}
12026
12027/*
12028 * GetForeignKeyActionTriggers
12029 * Returns delete and update "action" triggers of the given relation
12030 * belonging to the given constraint
12031 */
12032static void
12034 Oid conoid, Oid confrelid, Oid conrelid,
12035 Oid *deleteTriggerOid,
12036 Oid *updateTriggerOid)
12037{
12039 SysScanDesc scan;
12040 HeapTuple trigtup;
12041
12042 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
12044 Anum_pg_trigger_tgconstraint,
12045 BTEqualStrategyNumber, F_OIDEQ,
12046 ObjectIdGetDatum(conoid));
12047
12048 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12049 NULL, 1, &key);
12050 while ((trigtup = systable_getnext(scan)) != NULL)
12051 {
12052 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12053
12054 if (trgform->tgconstrrelid != conrelid)
12055 continue;
12056 if (trgform->tgrelid != confrelid)
12057 continue;
12058 /* Only ever look at "action" triggers on the PK side. */
12059 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
12060 continue;
12061 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12062 {
12063 Assert(*deleteTriggerOid == InvalidOid);
12064 *deleteTriggerOid = trgform->oid;
12065 }
12066 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12067 {
12068 Assert(*updateTriggerOid == InvalidOid);
12069 *updateTriggerOid = trgform->oid;
12070 }
12071#ifndef USE_ASSERT_CHECKING
12072 /* In an assert-enabled build, continue looking to find duplicates */
12073 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
12074 break;
12075#endif
12076 }
12077
12078 if (!OidIsValid(*deleteTriggerOid))
12079 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12080 conoid);
12081 if (!OidIsValid(*updateTriggerOid))
12082 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12083 conoid);
12084
12085 systable_endscan(scan);
12086}
12087
12088/*
12089 * GetForeignKeyCheckTriggers
12090 * Returns insert and update "check" triggers of the given relation
12091 * belonging to the given constraint
12092 */
12093static void
12095 Oid conoid, Oid confrelid, Oid conrelid,
12096 Oid *insertTriggerOid,
12097 Oid *updateTriggerOid)
12098{
12100 SysScanDesc scan;
12101 HeapTuple trigtup;
12102
12103 *insertTriggerOid = *updateTriggerOid = InvalidOid;
12105 Anum_pg_trigger_tgconstraint,
12106 BTEqualStrategyNumber, F_OIDEQ,
12107 ObjectIdGetDatum(conoid));
12108
12109 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
12110 NULL, 1, &key);
12111 while ((trigtup = systable_getnext(scan)) != NULL)
12112 {
12113 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
12114
12115 if (trgform->tgconstrrelid != confrelid)
12116 continue;
12117 if (trgform->tgrelid != conrelid)
12118 continue;
12119 /* Only ever look at "check" triggers on the FK side. */
12120 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
12121 continue;
12122 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12123 {
12124 Assert(*insertTriggerOid == InvalidOid);
12125 *insertTriggerOid = trgform->oid;
12126 }
12127 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12128 {
12129 Assert(*updateTriggerOid == InvalidOid);
12130 *updateTriggerOid = trgform->oid;
12131 }
12132#ifndef USE_ASSERT_CHECKING
12133 /* In an assert-enabled build, continue looking to find duplicates. */
12134 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
12135 break;
12136#endif
12137 }
12138
12139 if (!OidIsValid(*insertTriggerOid))
12140 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12141 conoid);
12142 if (!OidIsValid(*updateTriggerOid))
12143 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12144 conoid);
12145
12146 systable_endscan(scan);
12147}
12148
12149/*
12150 * ALTER TABLE ALTER CONSTRAINT
12151 *
12152 * Update the attributes of a constraint.
12153 *
12154 * Currently only works for Foreign Key and not null constraints.
12155 *
12156 * If the constraint is modified, returns its address; otherwise, return
12157 * InvalidObjectAddress.
12158 */
12159static ObjectAddress
12161 bool recurse, LOCKMODE lockmode)
12162{
12163 Relation conrel;
12164 Relation tgrel;
12165 SysScanDesc scan;
12166 ScanKeyData skey[3];
12167 HeapTuple contuple;
12168 Form_pg_constraint currcon;
12169 ObjectAddress address;
12170
12171 /*
12172 * Disallow altering ONLY a partitioned table, as it would make no sense.
12173 * This is okay for legacy inheritance.
12174 */
12175 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12176 ereport(ERROR,
12177 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12178 errmsg("constraint must be altered in child tables too"),
12179 errhint("Do not specify the ONLY keyword."));
12180
12181
12182 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12183 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
12184
12185 /*
12186 * Find and check the target constraint
12187 */
12188 ScanKeyInit(&skey[0],
12189 Anum_pg_constraint_conrelid,
12190 BTEqualStrategyNumber, F_OIDEQ,
12192 ScanKeyInit(&skey[1],
12193 Anum_pg_constraint_contypid,
12194 BTEqualStrategyNumber, F_OIDEQ,
12196 ScanKeyInit(&skey[2],
12197 Anum_pg_constraint_conname,
12198 BTEqualStrategyNumber, F_NAMEEQ,
12199 CStringGetDatum(cmdcon->conname));
12200 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12201 true, NULL, 3, skey);
12202
12203 /* There can be at most one matching row */
12204 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
12205 ereport(ERROR,
12206 (errcode(ERRCODE_UNDEFINED_OBJECT),
12207 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12208 cmdcon->conname, RelationGetRelationName(rel))));
12209
12210 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12211 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12212 ereport(ERROR,
12213 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12214 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12215 cmdcon->conname, RelationGetRelationName(rel))));
12216 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12217 ereport(ERROR,
12218 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12219 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12220 cmdcon->conname, RelationGetRelationName(rel))));
12221 if (cmdcon->alterInheritability &&
12222 currcon->contype != CONSTRAINT_NOTNULL)
12223 ereport(ERROR,
12224 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12225 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12226 cmdcon->conname, RelationGetRelationName(rel)));
12227
12228 /* Refuse to modify inheritability of inherited constraints */
12229 if (cmdcon->alterInheritability &&
12230 cmdcon->noinherit && currcon->coninhcount > 0)
12231 ereport(ERROR,
12232 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12233 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12234 NameStr(currcon->conname),
12236
12237 /*
12238 * If it's not the topmost constraint, raise an error.
12239 *
12240 * Altering a non-topmost constraint leaves some triggers untouched, since
12241 * they are not directly connected to this constraint; also, pg_dump would
12242 * ignore the deferrability status of the individual constraint, since it
12243 * only dumps topmost constraints. Avoid these problems by refusing this
12244 * operation and telling the user to alter the parent constraint instead.
12245 */
12246 if (OidIsValid(currcon->conparentid))
12247 {
12248 HeapTuple tp;
12249 Oid parent = currcon->conparentid;
12250 char *ancestorname = NULL;
12251 char *ancestortable = NULL;
12252
12253 /* Loop to find the topmost constraint */
12254 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
12255 {
12257
12258 /* If no parent, this is the constraint we want */
12259 if (!OidIsValid(contup->conparentid))
12260 {
12261 ancestorname = pstrdup(NameStr(contup->conname));
12262 ancestortable = get_rel_name(contup->conrelid);
12263 ReleaseSysCache(tp);
12264 break;
12265 }
12266
12267 parent = contup->conparentid;
12268 ReleaseSysCache(tp);
12269 }
12270
12271 ereport(ERROR,
12272 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12273 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12274 cmdcon->conname, RelationGetRelationName(rel)),
12275 ancestorname && ancestortable ?
12276 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12277 cmdcon->conname, ancestorname, ancestortable) : 0,
12278 errhint("You may alter the constraint it derives from instead.")));
12279 }
12280
12281 address = InvalidObjectAddress;
12282
12283 /*
12284 * Do the actual catalog work, and recurse if necessary.
12285 */
12286 if (ATExecAlterConstraintInternal(wqueue, cmdcon, conrel, tgrel, rel,
12287 contuple, recurse, lockmode))
12288 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
12289
12290 systable_endscan(scan);
12291
12294
12295 return address;
12296}
12297
12298/*
12299 * A subroutine of ATExecAlterConstraint that calls the respective routines for
12300 * altering constraint's enforceability, deferrability or inheritability.
12301 */
12302static bool
12304 Relation conrel, Relation tgrel, Relation rel,
12305 HeapTuple contuple, bool recurse,
12306 LOCKMODE lockmode)
12307{
12308 Form_pg_constraint currcon;
12309 bool changed = false;
12310 List *otherrelids = NIL;
12311
12312 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12313
12314 /*
12315 * Do the catalog work for the enforceability or deferrability change,
12316 * recurse if necessary.
12317 *
12318 * Note that even if deferrability is requested to be altered along with
12319 * enforceability, we don't need to explicitly update multiple entries in
12320 * pg_trigger related to deferrability.
12321 *
12322 * Modifying enforceability involves either creating or dropping the
12323 * trigger, during which the deferrability setting will be adjusted
12324 * automatically.
12325 */
12326 if (cmdcon->alterEnforceability &&
12327 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel,
12328 currcon->conrelid, currcon->confrelid,
12329 contuple, lockmode, InvalidOid,
12331 changed = true;
12332
12333 else if (cmdcon->alterDeferrability &&
12334 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, rel,
12335 contuple, recurse, &otherrelids,
12336 lockmode))
12337 {
12338 /*
12339 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12340 * the relations having the constraint itself; here we also invalidate
12341 * for relations that have any triggers that are part of the
12342 * constraint.
12343 */
12344 foreach_oid(relid, otherrelids)
12346
12347 changed = true;
12348 }
12349
12350 /*
12351 * Do the catalog work for the inheritability change.
12352 */
12353 if (cmdcon->alterInheritability &&
12354 ATExecAlterConstrInheritability(wqueue, cmdcon, conrel, rel, contuple,
12355 lockmode))
12356 changed = true;
12357
12358 return changed;
12359}
12360
12361/*
12362 * Returns true if the constraint's enforceability is altered.
12363 *
12364 * Depending on whether the constraint is being set to ENFORCED or NOT
12365 * ENFORCED, it creates or drops the trigger accordingly.
12366 *
12367 * Note that we must recurse even when trying to change a constraint to not
12368 * enforced if it is already not enforced, in case descendant constraints
12369 * might be enforced and need to be changed to not enforced. Conversely, we
12370 * should do nothing if a constraint is being set to enforced and is already
12371 * enforced, as descendant constraints cannot be different in that case.
12372 */
12373static bool
12375 Relation conrel, Relation tgrel,
12376 Oid fkrelid, Oid pkrelid,
12377 HeapTuple contuple, LOCKMODE lockmode,
12378 Oid ReferencedParentDelTrigger,
12379 Oid ReferencedParentUpdTrigger,
12380 Oid ReferencingParentInsTrigger,
12381 Oid ReferencingParentUpdTrigger)
12382{
12383 Form_pg_constraint currcon;
12384 Oid conoid;
12385 Relation rel;
12386 bool changed = false;
12387
12388 /* Since this function recurses, it could be driven to stack overflow */
12390
12391 Assert(cmdcon->alterEnforceability);
12392
12393 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12394 conoid = currcon->oid;
12395
12396 /* Should be foreign key constraint */
12397 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12398
12399 rel = table_open(currcon->conrelid, lockmode);
12400
12401 if (currcon->conenforced != cmdcon->is_enforced)
12402 {
12403 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12404 changed = true;
12405 }
12406
12407 /* Drop triggers */
12408 if (!cmdcon->is_enforced)
12409 {
12410 /*
12411 * When setting a constraint to NOT ENFORCED, the constraint triggers
12412 * need to be dropped. Therefore, we must process the child relations
12413 * first, followed by the parent, to account for dependencies.
12414 */
12415 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12416 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12417 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12418 fkrelid, pkrelid, contuple,
12419 lockmode, InvalidOid, InvalidOid,
12421
12422 /* Drop all the triggers */
12424 }
12425 else if (changed) /* Create triggers */
12426 {
12427 Oid ReferencedDelTriggerOid = InvalidOid,
12428 ReferencedUpdTriggerOid = InvalidOid,
12429 ReferencingInsTriggerOid = InvalidOid,
12430 ReferencingUpdTriggerOid = InvalidOid;
12431
12432 /* Prepare the minimal information required for trigger creation. */
12433 Constraint *fkconstraint = makeNode(Constraint);
12434
12435 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12436 fkconstraint->fk_matchtype = currcon->confmatchtype;
12437 fkconstraint->fk_upd_action = currcon->confupdtype;
12438 fkconstraint->fk_del_action = currcon->confdeltype;
12439
12440 /* Create referenced triggers */
12441 if (currcon->conrelid == fkrelid)
12442 createForeignKeyActionTriggers(currcon->conrelid,
12443 currcon->confrelid,
12444 fkconstraint,
12445 conoid,
12446 currcon->conindid,
12447 ReferencedParentDelTrigger,
12448 ReferencedParentUpdTrigger,
12449 &ReferencedDelTriggerOid,
12450 &ReferencedUpdTriggerOid);
12451
12452 /* Create referencing triggers */
12453 if (currcon->confrelid == pkrelid)
12454 createForeignKeyCheckTriggers(currcon->conrelid,
12455 pkrelid,
12456 fkconstraint,
12457 conoid,
12458 currcon->conindid,
12459 ReferencingParentInsTrigger,
12460 ReferencingParentUpdTrigger,
12461 &ReferencingInsTriggerOid,
12462 &ReferencingUpdTriggerOid);
12463
12464 /*
12465 * Tell Phase 3 to check that the constraint is satisfied by existing
12466 * rows.
12467 */
12468 if (rel->rd_rel->relkind == RELKIND_RELATION)
12469 {
12470 AlteredTableInfo *tab;
12471 NewConstraint *newcon;
12472
12473 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12474 newcon->name = fkconstraint->conname;
12475 newcon->contype = CONSTR_FOREIGN;
12476 newcon->refrelid = currcon->confrelid;
12477 newcon->refindid = currcon->conindid;
12478 newcon->conid = currcon->oid;
12479 newcon->qual = (Node *) fkconstraint;
12480
12481 /* Find or create work queue entry for this table */
12482 tab = ATGetQueueEntry(wqueue, rel);
12483 tab->constraints = lappend(tab->constraints, newcon);
12484 }
12485
12486 /*
12487 * If the table at either end of the constraint is partitioned, we
12488 * need to recurse and create triggers for each constraint that is a
12489 * child of this one.
12490 */
12491 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12492 get_rel_relkind(currcon->confrelid) == RELKIND_PARTITIONED_TABLE)
12493 AlterConstrEnforceabilityRecurse(wqueue, cmdcon, conrel, tgrel,
12494 fkrelid, pkrelid, contuple,
12495 lockmode, ReferencedDelTriggerOid,
12496 ReferencedUpdTriggerOid,
12497 ReferencingInsTriggerOid,
12498 ReferencingUpdTriggerOid);
12499 }
12500
12501 table_close(rel, NoLock);
12502
12503 return changed;
12504}
12505
12506/*
12507 * Returns true if the constraint's deferrability is altered.
12508 *
12509 * *otherrelids is appended OIDs of relations containing affected triggers.
12510 *
12511 * Note that we must recurse even when the values are correct, in case
12512 * indirect descendants have had their constraints altered locally.
12513 * (This could be avoided if we forbade altering constraints in partitions
12514 * but existing releases don't do that.)
12515 */
12516static bool
12518 Relation conrel, Relation tgrel, Relation rel,
12519 HeapTuple contuple, bool recurse,
12520 List **otherrelids, LOCKMODE lockmode)
12521{
12522 Form_pg_constraint currcon;
12523 Oid refrelid;
12524 bool changed = false;
12525
12526 /* since this function recurses, it could be driven to stack overflow */
12528
12529 Assert(cmdcon->alterDeferrability);
12530
12531 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12532 refrelid = currcon->confrelid;
12533
12534 /* Should be foreign key constraint */
12535 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12536
12537 /*
12538 * If called to modify a constraint that's already in the desired state,
12539 * silently do nothing.
12540 */
12541 if (currcon->condeferrable != cmdcon->deferrable ||
12542 currcon->condeferred != cmdcon->initdeferred)
12543 {
12544 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12545 changed = true;
12546
12547 /*
12548 * Now we need to update the multiple entries in pg_trigger that
12549 * implement the constraint.
12550 */
12551 AlterConstrTriggerDeferrability(currcon->oid, tgrel, rel,
12552 cmdcon->deferrable,
12553 cmdcon->initdeferred, otherrelids);
12554 }
12555
12556 /*
12557 * If the table at either end of the constraint is partitioned, we need to
12558 * handle every constraint that is a child of this one.
12559 */
12560 if (recurse && changed &&
12561 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12562 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE))
12563 AlterConstrDeferrabilityRecurse(wqueue, cmdcon, conrel, tgrel, rel,
12564 contuple, recurse, otherrelids,
12565 lockmode);
12566
12567 return changed;
12568}
12569
12570/*
12571 * Returns true if the constraint's inheritability is altered.
12572 */
12573static bool
12575 Relation conrel, Relation rel,
12576 HeapTuple contuple, LOCKMODE lockmode)
12577{
12578 Form_pg_constraint currcon;
12579 AttrNumber colNum;
12580 char *colName;
12581 List *children;
12582
12583 Assert(cmdcon->alterInheritability);
12584
12585 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12586
12587 /* The current implementation only works for NOT NULL constraints */
12588 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12589
12590 /*
12591 * If called to modify a constraint that's already in the desired state,
12592 * silently do nothing.
12593 */
12594 if (cmdcon->noinherit == currcon->connoinherit)
12595 return false;
12596
12597 AlterConstrUpdateConstraintEntry(cmdcon, conrel, contuple);
12599
12600 /* Fetch the column number and name */
12601 colNum = extractNotNullColumn(contuple);
12602 colName = get_attname(currcon->conrelid, colNum, false);
12603
12604 /*
12605 * Propagate the change to children. For this subcommand type we don't
12606 * recursively affect children, just the immediate level.
12607 */
12609 lockmode);
12610 foreach_oid(childoid, children)
12611 {
12612 ObjectAddress addr;
12613
12614 if (cmdcon->noinherit)
12615 {
12616 HeapTuple childtup;
12617 Form_pg_constraint childcon;
12618
12619 childtup = findNotNullConstraint(childoid, colName);
12620 if (!childtup)
12621 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12622 colName, childoid);
12623 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12624 Assert(childcon->coninhcount > 0);
12625 childcon->coninhcount--;
12626 childcon->conislocal = true;
12627 CatalogTupleUpdate(conrel, &childtup->t_self, childtup);
12628 heap_freetuple(childtup);
12629 }
12630 else
12631 {
12632 Relation childrel = table_open(childoid, NoLock);
12633
12634 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12635 colName, true, true, lockmode);
12636 if (OidIsValid(addr.objectId))
12638 table_close(childrel, NoLock);
12639 }
12640 }
12641
12642 return true;
12643}
12644
12645/*
12646 * A subroutine of ATExecAlterConstrDeferrability that updated constraint
12647 * trigger's deferrability.
12648 *
12649 * The arguments to this function have the same meaning as the arguments to
12650 * ATExecAlterConstrDeferrability.
12651 */
12652static void
12654 bool deferrable, bool initdeferred,
12655 List **otherrelids)
12656{
12657 HeapTuple tgtuple;
12658 ScanKeyData tgkey;
12659 SysScanDesc tgscan;
12660
12661 ScanKeyInit(&tgkey,
12662 Anum_pg_trigger_tgconstraint,
12663 BTEqualStrategyNumber, F_OIDEQ,
12664 ObjectIdGetDatum(conoid));
12665 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
12666 NULL, 1, &tgkey);
12667 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
12668 {
12669 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
12670 Form_pg_trigger copy_tg;
12671 HeapTuple tgCopyTuple;
12672
12673 /*
12674 * Remember OIDs of other relation(s) involved in FK constraint.
12675 * (Note: it's likely that we could skip forcing a relcache inval for
12676 * other rels that don't have a trigger whose properties change, but
12677 * let's be conservative.)
12678 */
12679 if (tgform->tgrelid != RelationGetRelid(rel))
12680 *otherrelids = list_append_unique_oid(*otherrelids,
12681 tgform->tgrelid);
12682
12683 /*
12684 * Update enable status and deferrability of RI_FKey_noaction_del,
12685 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12686 * triggers, but not others; see createForeignKeyActionTriggers and
12687 * CreateFKCheckTrigger.
12688 */
12689 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12690 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12691 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12692 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12693 continue;
12694
12695 tgCopyTuple = heap_copytuple(tgtuple);
12696 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
12697
12698 copy_tg->tgdeferrable = deferrable;
12699 copy_tg->tginitdeferred = initdeferred;
12700 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
12701
12702 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
12703
12704 heap_freetuple(tgCopyTuple);
12705 }
12706
12707 systable_endscan(tgscan);
12708}
12709
12710/*
12711 * Invokes ATExecAlterConstrEnforceability for each constraint that is a child of
12712 * the specified constraint.
12713 *
12714 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12715 * list of child relations and recursing; instead it uses the conparentid
12716 * relationships. This may need to be reconsidered.
12717 *
12718 * The arguments to this function have the same meaning as the arguments to
12719 * ATExecAlterConstrEnforceability.
12720 */
12721static void
12723 Relation conrel, Relation tgrel,
12724 Oid fkrelid, Oid pkrelid,
12725 HeapTuple contuple, LOCKMODE lockmode,
12726 Oid ReferencedParentDelTrigger,
12727 Oid ReferencedParentUpdTrigger,
12728 Oid ReferencingParentInsTrigger,
12729 Oid ReferencingParentUpdTrigger)
12730{
12731 Form_pg_constraint currcon;
12732 Oid conoid;
12733 ScanKeyData pkey;
12734 SysScanDesc pscan;
12735 HeapTuple childtup;
12736
12737 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12738 conoid = currcon->oid;
12739
12740 ScanKeyInit(&pkey,
12741 Anum_pg_constraint_conparentid,
12742 BTEqualStrategyNumber, F_OIDEQ,
12743 ObjectIdGetDatum(conoid));
12744
12745 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12746 true, NULL, 1, &pkey);
12747
12748 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12749 ATExecAlterConstrEnforceability(wqueue, cmdcon, conrel, tgrel, fkrelid,
12750 pkrelid, childtup, lockmode,
12751 ReferencedParentDelTrigger,
12752 ReferencedParentUpdTrigger,
12753 ReferencingParentInsTrigger,
12754 ReferencingParentUpdTrigger);
12755
12756 systable_endscan(pscan);
12757}
12758
12759/*
12760 * Invokes ATExecAlterConstrDeferrability for each constraint that is a child of
12761 * the specified constraint.
12762 *
12763 * Note that this doesn't handle recursion the normal way, viz. by scanning the
12764 * list of child relations and recursing; instead it uses the conparentid
12765 * relationships. This may need to be reconsidered.
12766 *
12767 * The arguments to this function have the same meaning as the arguments to
12768 * ATExecAlterConstrDeferrability.
12769 */
12770static void
12772 Relation conrel, Relation tgrel, Relation rel,
12773 HeapTuple contuple, bool recurse,
12774 List **otherrelids, LOCKMODE lockmode)
12775{
12776 Form_pg_constraint currcon;
12777 Oid conoid;
12778 ScanKeyData pkey;
12779 SysScanDesc pscan;
12780 HeapTuple childtup;
12781
12782 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
12783 conoid = currcon->oid;
12784
12785 ScanKeyInit(&pkey,
12786 Anum_pg_constraint_conparentid,
12787 BTEqualStrategyNumber, F_OIDEQ,
12788 ObjectIdGetDatum(conoid));
12789
12790 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
12791 true, NULL, 1, &pkey);
12792
12793 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
12794 {
12795 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
12796 Relation childrel;
12797
12798 childrel = table_open(childcon->conrelid, lockmode);
12799
12800 ATExecAlterConstrDeferrability(wqueue, cmdcon, conrel, tgrel, childrel,
12801 childtup, recurse, otherrelids, lockmode);
12802 table_close(childrel, NoLock);
12803 }
12804
12805 systable_endscan(pscan);
12806}
12807
12808/*
12809 * Update the constraint entry for the given ATAlterConstraint command, and
12810 * invoke the appropriate hooks.
12811 */
12812static void
12814 HeapTuple contuple)
12815{
12816 HeapTuple copyTuple;
12817 Form_pg_constraint copy_con;
12818
12819 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
12820 cmdcon->alterInheritability);
12821
12822 copyTuple = heap_copytuple(contuple);
12823 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12824
12825 if (cmdcon->alterEnforceability)
12826 {
12827 copy_con->conenforced = cmdcon->is_enforced;
12828
12829 /*
12830 * NB: The convalidated status is irrelevant when the constraint is
12831 * set to NOT ENFORCED, but for consistency, it should still be set
12832 * appropriately. Similarly, if the constraint is later changed to
12833 * ENFORCED, validation will be performed during phase 3, so it makes
12834 * sense to mark it as valid in that case.
12835 */
12836 copy_con->convalidated = cmdcon->is_enforced;
12837 }
12838 if (cmdcon->alterDeferrability)
12839 {
12840 copy_con->condeferrable = cmdcon->deferrable;
12841 copy_con->condeferred = cmdcon->initdeferred;
12842 }
12843 if (cmdcon->alterInheritability)
12844 copy_con->connoinherit = cmdcon->noinherit;
12845
12846 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12847 InvokeObjectPostAlterHook(ConstraintRelationId, copy_con->oid, 0);
12848
12849 /* Make new constraint flags visible to others */
12850 CacheInvalidateRelcacheByRelid(copy_con->conrelid);
12851
12852 heap_freetuple(copyTuple);
12853}
12854
12855/*
12856 * ALTER TABLE VALIDATE CONSTRAINT
12857 *
12858 * XXX The reason we handle recursion here rather than at Phase 1 is because
12859 * there's no good way to skip recursing when handling foreign keys: there is
12860 * no need to lock children in that case, yet we wouldn't be able to avoid
12861 * doing so at that level.
12862 *
12863 * Return value is the address of the validated constraint. If the constraint
12864 * was already validated, InvalidObjectAddress is returned.
12865 */
12866static ObjectAddress
12867ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12868 bool recurse, bool recursing, LOCKMODE lockmode)
12869{
12870 Relation conrel;
12871 SysScanDesc scan;
12872 ScanKeyData skey[3];
12873 HeapTuple tuple;
12875 ObjectAddress address;
12876
12877 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12878
12879 /*
12880 * Find and check the target constraint
12881 */
12882 ScanKeyInit(&skey[0],
12883 Anum_pg_constraint_conrelid,
12884 BTEqualStrategyNumber, F_OIDEQ,
12886 ScanKeyInit(&skey[1],
12887 Anum_pg_constraint_contypid,
12888 BTEqualStrategyNumber, F_OIDEQ,
12890 ScanKeyInit(&skey[2],
12891 Anum_pg_constraint_conname,
12892 BTEqualStrategyNumber, F_NAMEEQ,
12893 CStringGetDatum(constrName));
12894 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12895 true, NULL, 3, skey);
12896
12897 /* There can be at most one matching row */
12898 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12899 ereport(ERROR,
12900 (errcode(ERRCODE_UNDEFINED_OBJECT),
12901 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12902 constrName, RelationGetRelationName(rel))));
12903
12904 con = (Form_pg_constraint) GETSTRUCT(tuple);
12905 if (con->contype != CONSTRAINT_FOREIGN &&
12906 con->contype != CONSTRAINT_CHECK &&
12907 con->contype != CONSTRAINT_NOTNULL)
12908 ereport(ERROR,
12909 errcode(ERRCODE_WRONG_OBJECT_TYPE),
12910 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key, check, or not-null constraint",
12911 constrName, RelationGetRelationName(rel)));
12912
12913 if (!con->conenforced)
12914 ereport(ERROR,
12915 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12916 errmsg("cannot validate NOT ENFORCED constraint")));
12917
12918 if (!con->convalidated)
12919 {
12920 if (con->contype == CONSTRAINT_FOREIGN)
12921 {
12922 QueueFKConstraintValidation(wqueue, conrel, rel, tuple, lockmode);
12923 }
12924 else if (con->contype == CONSTRAINT_CHECK)
12925 {
12926 QueueCheckConstraintValidation(wqueue, conrel, rel, constrName,
12927 tuple, recurse, recursing, lockmode);
12928 }
12929 else if (con->contype == CONSTRAINT_NOTNULL)
12930 {
12931 QueueNNConstraintValidation(wqueue, conrel, rel,
12932 tuple, recurse, recursing, lockmode);
12933 }
12934
12935 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12936 }
12937 else
12938 address = InvalidObjectAddress; /* already validated */
12939
12940 systable_endscan(scan);
12941
12943
12944 return address;
12945}
12946
12947/*
12948 * QueueFKConstraintValidation
12949 *
12950 * Add an entry to the wqueue to validate the given foreign key constraint in
12951 * Phase 3 and update the convalidated field in the pg_constraint catalog
12952 * for the specified relation and all its children.
12953 */
12954static void
12956 HeapTuple contuple, LOCKMODE lockmode)
12957{
12959 AlteredTableInfo *tab;
12960 HeapTuple copyTuple;
12961 Form_pg_constraint copy_con;
12962
12963 con = (Form_pg_constraint) GETSTRUCT(contuple);
12964 Assert(con->contype == CONSTRAINT_FOREIGN);
12965 Assert(!con->convalidated);
12966
12967 if (rel->rd_rel->relkind == RELKIND_RELATION)
12968 {
12969 NewConstraint *newcon;
12970 Constraint *fkconstraint;
12971
12972 /* Queue validation for phase 3 */
12973 fkconstraint = makeNode(Constraint);
12974 /* for now this is all we need */
12975 fkconstraint->conname = pstrdup(NameStr(con->conname));
12976
12977 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12978 newcon->name = fkconstraint->conname;
12979 newcon->contype = CONSTR_FOREIGN;
12980 newcon->refrelid = con->confrelid;
12981 newcon->refindid = con->conindid;
12982 newcon->conid = con->oid;
12983 newcon->qual = (Node *) fkconstraint;
12984
12985 /* Find or create work queue entry for this table */
12986 tab = ATGetQueueEntry(wqueue, rel);
12987 tab->constraints = lappend(tab->constraints, newcon);
12988 }
12989
12990 /*
12991 * If the table at either end of the constraint is partitioned, we need to
12992 * recurse and handle every constraint that is a child of this constraint.
12993 */
12994 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12995 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
12996 {
12997 ScanKeyData pkey;
12998 SysScanDesc pscan;
12999 HeapTuple childtup;
13000
13001 ScanKeyInit(&pkey,
13002 Anum_pg_constraint_conparentid,
13003 BTEqualStrategyNumber, F_OIDEQ,
13004 ObjectIdGetDatum(con->oid));
13005
13006 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
13007 true, NULL, 1, &pkey);
13008
13009 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
13010 {
13011 Form_pg_constraint childcon;
13012 Relation childrel;
13013
13014 childcon = (Form_pg_constraint) GETSTRUCT(childtup);
13015
13016 /*
13017 * If the child constraint has already been validated, no further
13018 * action is required for it or its descendants, as they are all
13019 * valid.
13020 */
13021 if (childcon->convalidated)
13022 continue;
13023
13024 childrel = table_open(childcon->conrelid, lockmode);
13025
13026 QueueFKConstraintValidation(wqueue, conrel, childrel, childtup,
13027 lockmode);
13028 table_close(childrel, NoLock);
13029 }
13030
13031 systable_endscan(pscan);
13032 }
13033
13034 /*
13035 * Now update the catalog, while we have the door open.
13036 */
13037 copyTuple = heap_copytuple(contuple);
13038 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13039 copy_con->convalidated = true;
13040 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13041
13042 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13043
13044 heap_freetuple(copyTuple);
13045}
13046
13047/*
13048 * QueueCheckConstraintValidation
13049 *
13050 * Add an entry to the wqueue to validate the given check constraint in Phase 3
13051 * and update the convalidated field in the pg_constraint catalog for the
13052 * specified relation and all its inheriting children.
13053 */
13054static void
13056 char *constrName, HeapTuple contuple,
13057 bool recurse, bool recursing, LOCKMODE lockmode)
13058{
13060 AlteredTableInfo *tab;
13061 HeapTuple copyTuple;
13062 Form_pg_constraint copy_con;
13063
13064 List *children = NIL;
13065 ListCell *child;
13066 NewConstraint *newcon;
13067 Datum val;
13068 char *conbin;
13069
13070 con = (Form_pg_constraint) GETSTRUCT(contuple);
13071 Assert(con->contype == CONSTRAINT_CHECK);
13072
13073 /*
13074 * If we're recursing, the parent has already done this, so skip it. Also,
13075 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13076 * for it in the children.
13077 */
13078 if (!recursing && !con->connoinherit)
13079 children = find_all_inheritors(RelationGetRelid(rel),
13080 lockmode, NULL);
13081
13082 /*
13083 * For CHECK constraints, we must ensure that we only mark the constraint
13084 * as validated on the parent if it's already validated on the children.
13085 *
13086 * We recurse before validating on the parent, to reduce risk of
13087 * deadlocks.
13088 */
13089 foreach(child, children)
13090 {
13091 Oid childoid = lfirst_oid(child);
13092 Relation childrel;
13093
13094 if (childoid == RelationGetRelid(rel))
13095 continue;
13096
13097 /*
13098 * If we are told not to recurse, there had better not be any child
13099 * tables, because we can't mark the constraint on the parent valid
13100 * unless it is valid for all child tables.
13101 */
13102 if (!recurse)
13103 ereport(ERROR,
13104 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13105 errmsg("constraint must be validated on child tables too")));
13106
13107 /* find_all_inheritors already got lock */
13108 childrel = table_open(childoid, NoLock);
13109
13110 ATExecValidateConstraint(wqueue, childrel, constrName, false,
13111 true, lockmode);
13112 table_close(childrel, NoLock);
13113 }
13114
13115 /* Queue validation for phase 3 */
13116 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
13117 newcon->name = constrName;
13118 newcon->contype = CONSTR_CHECK;
13119 newcon->refrelid = InvalidOid;
13120 newcon->refindid = InvalidOid;
13121 newcon->conid = con->oid;
13122
13123 val = SysCacheGetAttrNotNull(CONSTROID, contuple,
13124 Anum_pg_constraint_conbin);
13125 conbin = TextDatumGetCString(val);
13126 newcon->qual = expand_generated_columns_in_expr(stringToNode(conbin), rel, 1);
13127
13128 /* Find or create work queue entry for this table */
13129 tab = ATGetQueueEntry(wqueue, rel);
13130 tab->constraints = lappend(tab->constraints, newcon);
13131
13132 /*
13133 * Invalidate relcache so that others see the new validated constraint.
13134 */
13136
13137 /*
13138 * Now update the catalog, while we have the door open.
13139 */
13140 copyTuple = heap_copytuple(contuple);
13141 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13142 copy_con->convalidated = true;
13143 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13144
13145 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13146
13147 heap_freetuple(copyTuple);
13148}
13149
13150/*
13151 * QueueNNConstraintValidation
13152 *
13153 * Add an entry to the wqueue to validate the given not-null constraint in
13154 * Phase 3 and update the convalidated field in the pg_constraint catalog for
13155 * the specified relation and all its inheriting children.
13156 */
13157static void
13159 HeapTuple contuple, bool recurse, bool recursing,
13160 LOCKMODE lockmode)
13161{
13163 AlteredTableInfo *tab;
13164 HeapTuple copyTuple;
13165 Form_pg_constraint copy_con;
13166 List *children = NIL;
13168 char *colname;
13169
13170 con = (Form_pg_constraint) GETSTRUCT(contuple);
13171 Assert(con->contype == CONSTRAINT_NOTNULL);
13172
13173 attnum = extractNotNullColumn(contuple);
13174
13175 /*
13176 * If we're recursing, we've already done this for parent, so skip it.
13177 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13178 * look for it in the children.
13179 *
13180 * We recurse before validating on the parent, to reduce risk of
13181 * deadlocks.
13182 */
13183 if (!recursing && !con->connoinherit)
13184 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13185
13186 colname = get_attname(RelationGetRelid(rel), attnum, false);
13187 foreach_oid(childoid, children)
13188 {
13189 Relation childrel;
13190 HeapTuple contup;
13191 Form_pg_constraint childcon;
13192 char *conname;
13193
13194 if (childoid == RelationGetRelid(rel))
13195 continue;
13196
13197 /*
13198 * If we are told not to recurse, there had better not be any child
13199 * tables, because we can't mark the constraint on the parent valid
13200 * unless it is valid for all child tables.
13201 */
13202 if (!recurse)
13203 ereport(ERROR,
13204 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13205 errmsg("constraint must be validated on child tables too"));
13206
13207 /*
13208 * The column on child might have a different attnum, so search by
13209 * column name.
13210 */
13211 contup = findNotNullConstraint(childoid, colname);
13212 if (!contup)
13213 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13214 colname, get_rel_name(childoid));
13215 childcon = (Form_pg_constraint) GETSTRUCT(contup);
13216 if (childcon->convalidated)
13217 continue;
13218
13219 /* find_all_inheritors already got lock */
13220 childrel = table_open(childoid, NoLock);
13221 conname = pstrdup(NameStr(childcon->conname));
13222
13223 /* XXX improve ATExecValidateConstraint API to avoid double search */
13224 ATExecValidateConstraint(wqueue, childrel, conname,
13225 false, true, lockmode);
13226 table_close(childrel, NoLock);
13227 }
13228
13229 /* Set attnotnull appropriately without queueing another validation */
13230 set_attnotnull(NULL, rel, attnum, true, false);
13231
13232 tab = ATGetQueueEntry(wqueue, rel);
13233 tab->verify_new_notnull = true;
13234
13235 /*
13236 * Invalidate relcache so that others see the new validated constraint.
13237 */
13239
13240 /*
13241 * Now update the catalogs, while we have the door open.
13242 */
13243 copyTuple = heap_copytuple(contuple);
13244 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
13245 copy_con->convalidated = true;
13246 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
13247
13248 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
13249
13250 heap_freetuple(copyTuple);
13251}
13252
13253/*
13254 * transformColumnNameList - transform list of column names
13255 *
13256 * Lookup each name and return its attnum and, optionally, type and collation
13257 * OIDs
13258 *
13259 * Note: the name of this function suggests that it's general-purpose,
13260 * but actually it's only used to look up names appearing in foreign-key
13261 * clauses. The error messages would need work to use it in other cases,
13262 * and perhaps the validity checks as well.
13263 */
13264static int
13266 int16 *attnums, Oid *atttypids, Oid *attcollids)
13267{
13268 ListCell *l;
13269 int attnum;
13270
13271 attnum = 0;
13272 foreach(l, colList)
13273 {
13274 char *attname = strVal(lfirst(l));
13275 HeapTuple atttuple;
13276 Form_pg_attribute attform;
13277
13278 atttuple = SearchSysCacheAttName(relId, attname);
13279 if (!HeapTupleIsValid(atttuple))
13280 ereport(ERROR,
13281 (errcode(ERRCODE_UNDEFINED_COLUMN),
13282 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13283 attname)));
13284 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
13285 if (attform->attnum < 0)
13286 ereport(ERROR,
13287 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13288 errmsg("system columns cannot be used in foreign keys")));
13289 if (attnum >= INDEX_MAX_KEYS)
13290 ereport(ERROR,
13291 (errcode(ERRCODE_TOO_MANY_COLUMNS),
13292 errmsg("cannot have more than %d keys in a foreign key",
13293 INDEX_MAX_KEYS)));
13294 attnums[attnum] = attform->attnum;
13295 if (atttypids != NULL)
13296 atttypids[attnum] = attform->atttypid;
13297 if (attcollids != NULL)
13298 attcollids[attnum] = attform->attcollation;
13299 ReleaseSysCache(atttuple);
13300 attnum++;
13301 }
13302
13303 return attnum;
13304}
13305
13306/*
13307 * transformFkeyGetPrimaryKey -
13308 *
13309 * Look up the names, attnums, types, and collations of the primary key attributes
13310 * for the pkrel. Also return the index OID and index opclasses of the
13311 * index supporting the primary key. Also return whether the index has
13312 * WITHOUT OVERLAPS.
13313 *
13314 * All parameters except pkrel are output parameters. Also, the function
13315 * return value is the number of attributes in the primary key.
13316 *
13317 * Used when the column list in the REFERENCES specification is omitted.
13318 */
13319static int
13321 List **attnamelist,
13322 int16 *attnums, Oid *atttypids, Oid *attcollids,
13323 Oid *opclasses, bool *pk_has_without_overlaps)
13324{
13325 List *indexoidlist;
13326 ListCell *indexoidscan;
13327 HeapTuple indexTuple = NULL;
13328 Form_pg_index indexStruct = NULL;
13329 Datum indclassDatum;
13330 oidvector *indclass;
13331 int i;
13332
13333 /*
13334 * Get the list of index OIDs for the table from the relcache, and look up
13335 * each one in the pg_index syscache until we find one marked primary key
13336 * (hopefully there isn't more than one such). Insist it's valid, too.
13337 */
13338 *indexOid = InvalidOid;
13339
13340 indexoidlist = RelationGetIndexList(pkrel);
13341
13342 foreach(indexoidscan, indexoidlist)
13343 {
13344 Oid indexoid = lfirst_oid(indexoidscan);
13345
13346 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13347 if (!HeapTupleIsValid(indexTuple))
13348 elog(ERROR, "cache lookup failed for index %u", indexoid);
13349 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13350 if (indexStruct->indisprimary && indexStruct->indisvalid)
13351 {
13352 /*
13353 * Refuse to use a deferrable primary key. This is per SQL spec,
13354 * and there would be a lot of interesting semantic problems if we
13355 * tried to allow it.
13356 */
13357 if (!indexStruct->indimmediate)
13358 ereport(ERROR,
13359 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13360 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13361 RelationGetRelationName(pkrel))));
13362
13363 *indexOid = indexoid;
13364 break;
13365 }
13366 ReleaseSysCache(indexTuple);
13367 }
13368
13369 list_free(indexoidlist);
13370
13371 /*
13372 * Check that we found it
13373 */
13374 if (!OidIsValid(*indexOid))
13375 ereport(ERROR,
13376 (errcode(ERRCODE_UNDEFINED_OBJECT),
13377 errmsg("there is no primary key for referenced table \"%s\"",
13378 RelationGetRelationName(pkrel))));
13379
13380 /* Must get indclass the hard way */
13381 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13382 Anum_pg_index_indclass);
13383 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13384
13385 /*
13386 * Now build the list of PK attributes from the indkey definition (we
13387 * assume a primary key cannot have expressional elements)
13388 */
13389 *attnamelist = NIL;
13390 for (i = 0; i < indexStruct->indnkeyatts; i++)
13391 {
13392 int pkattno = indexStruct->indkey.values[i];
13393
13394 attnums[i] = pkattno;
13395 atttypids[i] = attnumTypeId(pkrel, pkattno);
13396 attcollids[i] = attnumCollationId(pkrel, pkattno);
13397 opclasses[i] = indclass->values[i];
13398 *attnamelist = lappend(*attnamelist,
13399 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
13400 }
13401
13402 *pk_has_without_overlaps = indexStruct->indisexclusion;
13403
13404 ReleaseSysCache(indexTuple);
13405
13406 return i;
13407}
13408
13409/*
13410 * transformFkeyCheckAttrs -
13411 *
13412 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
13413 * reference as part of a foreign key constraint.
13414 *
13415 * Returns the OID of the unique index supporting the constraint and
13416 * populates the caller-provided 'opclasses' array with the opclasses
13417 * associated with the index columns. Also sets whether the index
13418 * uses WITHOUT OVERLAPS.
13419 *
13420 * Raises an ERROR on validation failure.
13421 */
13422static Oid
13424 int numattrs, int16 *attnums,
13425 bool with_period, Oid *opclasses,
13426 bool *pk_has_without_overlaps)
13427{
13428 Oid indexoid = InvalidOid;
13429 bool found = false;
13430 bool found_deferrable = false;
13431 List *indexoidlist;
13432 ListCell *indexoidscan;
13433 int i,
13434 j;
13435
13436 /*
13437 * Reject duplicate appearances of columns in the referenced-columns list.
13438 * Such a case is forbidden by the SQL standard, and even if we thought it
13439 * useful to allow it, there would be ambiguity about how to match the
13440 * list to unique indexes (in particular, it'd be unclear which index
13441 * opclass goes with which FK column).
13442 */
13443 for (i = 0; i < numattrs; i++)
13444 {
13445 for (j = i + 1; j < numattrs; j++)
13446 {
13447 if (attnums[i] == attnums[j])
13448 ereport(ERROR,
13449 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13450 errmsg("foreign key referenced-columns list must not contain duplicates")));
13451 }
13452 }
13453
13454 /*
13455 * Get the list of index OIDs for the table from the relcache, and look up
13456 * each one in the pg_index syscache, and match unique indexes to the list
13457 * of attnums we are given.
13458 */
13459 indexoidlist = RelationGetIndexList(pkrel);
13460
13461 foreach(indexoidscan, indexoidlist)
13462 {
13463 HeapTuple indexTuple;
13464 Form_pg_index indexStruct;
13465
13466 indexoid = lfirst_oid(indexoidscan);
13467 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
13468 if (!HeapTupleIsValid(indexTuple))
13469 elog(ERROR, "cache lookup failed for index %u", indexoid);
13470 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
13471
13472 /*
13473 * Must have the right number of columns; must be unique (or if
13474 * temporal then exclusion instead) and not a partial index; forget it
13475 * if there are any expressions, too. Invalid indexes are out as well.
13476 */
13477 if (indexStruct->indnkeyatts == numattrs &&
13478 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13479 indexStruct->indisvalid &&
13480 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
13481 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
13482 {
13483 Datum indclassDatum;
13484 oidvector *indclass;
13485
13486 /* Must get indclass the hard way */
13487 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
13488 Anum_pg_index_indclass);
13489 indclass = (oidvector *) DatumGetPointer(indclassDatum);
13490
13491 /*
13492 * The given attnum list may match the index columns in any order.
13493 * Check for a match, and extract the appropriate opclasses while
13494 * we're at it.
13495 *
13496 * We know that attnums[] is duplicate-free per the test at the
13497 * start of this function, and we checked above that the number of
13498 * index columns agrees, so if we find a match for each attnums[]
13499 * entry then we must have a one-to-one match in some order.
13500 */
13501 for (i = 0; i < numattrs; i++)
13502 {
13503 found = false;
13504 for (j = 0; j < numattrs; j++)
13505 {
13506 if (attnums[i] == indexStruct->indkey.values[j])
13507 {
13508 opclasses[i] = indclass->values[j];
13509 found = true;
13510 break;
13511 }
13512 }
13513 if (!found)
13514 break;
13515 }
13516 /* The last attribute in the index must be the PERIOD FK part */
13517 if (found && with_period)
13518 {
13519 int16 periodattnum = attnums[numattrs - 1];
13520
13521 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13522 }
13523
13524 /*
13525 * Refuse to use a deferrable unique/primary key. This is per SQL
13526 * spec, and there would be a lot of interesting semantic problems
13527 * if we tried to allow it.
13528 */
13529 if (found && !indexStruct->indimmediate)
13530 {
13531 /*
13532 * Remember that we found an otherwise matching index, so that
13533 * we can generate a more appropriate error message.
13534 */
13535 found_deferrable = true;
13536 found = false;
13537 }
13538
13539 /* We need to know whether the index has WITHOUT OVERLAPS */
13540 if (found)
13541 *pk_has_without_overlaps = indexStruct->indisexclusion;
13542 }
13543 ReleaseSysCache(indexTuple);
13544 if (found)
13545 break;
13546 }
13547
13548 if (!found)
13549 {
13550 if (found_deferrable)
13551 ereport(ERROR,
13552 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13553 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13554 RelationGetRelationName(pkrel))));
13555 else
13556 ereport(ERROR,
13557 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
13558 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13559 RelationGetRelationName(pkrel))));
13560 }
13561
13562 list_free(indexoidlist);
13563
13564 return indexoid;
13565}
13566
13567/*
13568 * findFkeyCast -
13569 *
13570 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
13571 * Caller has equal regard for binary coercibility and for an exact match.
13572*/
13573static CoercionPathType
13574findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
13575{
13576 CoercionPathType ret;
13577
13578 if (targetTypeId == sourceTypeId)
13579 {
13581 *funcid = InvalidOid;
13582 }
13583 else
13584 {
13585 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
13586 COERCION_IMPLICIT, funcid);
13587 if (ret == COERCION_PATH_NONE)
13588 /* A previously-relied-upon cast is now gone. */
13589 elog(ERROR, "could not find cast from %u to %u",
13590 sourceTypeId, targetTypeId);
13591 }
13592
13593 return ret;
13594}
13595
13596/*
13597 * Permissions checks on the referenced table for ADD FOREIGN KEY
13598 *
13599 * Note: we have already checked that the user owns the referencing table,
13600 * else we'd have failed much earlier; no additional checks are needed for it.
13601 */
13602static void
13603checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
13604{
13605 Oid roleid = GetUserId();
13606 AclResult aclresult;
13607 int i;
13608
13609 /* Okay if we have relation-level REFERENCES permission */
13610 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
13612 if (aclresult == ACLCHECK_OK)
13613 return;
13614 /* Else we must have REFERENCES on each column */
13615 for (i = 0; i < natts; i++)
13616 {
13617 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
13618 roleid, ACL_REFERENCES);
13619 if (aclresult != ACLCHECK_OK)
13620 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
13622 }
13623}
13624
13625/*
13626 * Scan the existing rows in a table to verify they meet a proposed FK
13627 * constraint.
13628 *
13629 * Caller must have opened and locked both relations appropriately.
13630 */
13631static void
13633 Relation rel,
13634 Relation pkrel,
13635 Oid pkindOid,
13636 Oid constraintOid,
13637 bool hasperiod)
13638{
13639 TupleTableSlot *slot;
13640 TableScanDesc scan;
13641 Trigger trig = {0};
13642 Snapshot snapshot;
13643 MemoryContext oldcxt;
13644 MemoryContext perTupCxt;
13645
13647 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13648
13649 /*
13650 * Build a trigger call structure; we'll need it either way.
13651 */
13652 trig.tgoid = InvalidOid;
13653 trig.tgname = conname;
13655 trig.tgisinternal = true;
13656 trig.tgconstrrelid = RelationGetRelid(pkrel);
13657 trig.tgconstrindid = pkindOid;
13658 trig.tgconstraint = constraintOid;
13659 trig.tgdeferrable = false;
13660 trig.tginitdeferred = false;
13661 /* we needn't fill in remaining fields */
13662
13663 /*
13664 * See if we can do it with a single LEFT JOIN query. A false result
13665 * indicates we must proceed with the fire-the-trigger method. We can't do
13666 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13667 * left joins.
13668 */
13669 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13670 return;
13671
13672 /*
13673 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13674 * if that tuple had just been inserted. If any of those fail, it should
13675 * ereport(ERROR) and that's that.
13676 */
13677 snapshot = RegisterSnapshot(GetLatestSnapshot());
13678 slot = table_slot_create(rel, NULL);
13679 scan = table_beginscan(rel, snapshot, 0, NULL);
13680
13682 "validateForeignKeyConstraint",
13684 oldcxt = MemoryContextSwitchTo(perTupCxt);
13685
13686 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13687 {
13688 LOCAL_FCINFO(fcinfo, 0);
13689 TriggerData trigdata = {0};
13690
13692
13693 /*
13694 * Make a call to the trigger function
13695 *
13696 * No parameters are passed, but we do set a context
13697 */
13698 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
13699
13700 /*
13701 * We assume RI_FKey_check_ins won't look at flinfo...
13702 */
13703 trigdata.type = T_TriggerData;
13705 trigdata.tg_relation = rel;
13706 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
13707 trigdata.tg_trigslot = slot;
13708 trigdata.tg_trigger = &trig;
13709
13710 fcinfo->context = (Node *) &trigdata;
13711
13712 RI_FKey_check_ins(fcinfo);
13713
13714 MemoryContextReset(perTupCxt);
13715 }
13716
13717 MemoryContextSwitchTo(oldcxt);
13718 MemoryContextDelete(perTupCxt);
13719 table_endscan(scan);
13720 UnregisterSnapshot(snapshot);
13722}
13723
13724/*
13725 * CreateFKCheckTrigger
13726 * Creates the insert (on_insert=true) or update "check" trigger that
13727 * implements a given foreign key
13728 *
13729 * Returns the OID of the so created trigger.
13730 */
13731static Oid
13732CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13733 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
13734 bool on_insert)
13735{
13736 ObjectAddress trigAddress;
13737 CreateTrigStmt *fk_trigger;
13738
13739 /*
13740 * Note: for a self-referential FK (referencing and referenced tables are
13741 * the same), it is important that the ON UPDATE action fires before the
13742 * CHECK action, since both triggers will fire on the same row during an
13743 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13744 * state of the row. Triggers fire in name order, so we ensure this by
13745 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13746 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13747 */
13748 fk_trigger = makeNode(CreateTrigStmt);
13749 fk_trigger->replace = false;
13750 fk_trigger->isconstraint = true;
13751 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13752 fk_trigger->relation = NULL;
13753
13754 /* Either ON INSERT or ON UPDATE */
13755 if (on_insert)
13756 {
13757 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13758 fk_trigger->events = TRIGGER_TYPE_INSERT;
13759 }
13760 else
13761 {
13762 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13763 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13764 }
13765
13766 fk_trigger->args = NIL;
13767 fk_trigger->row = true;
13768 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13769 fk_trigger->columns = NIL;
13770 fk_trigger->whenClause = NULL;
13771 fk_trigger->transitionRels = NIL;
13772 fk_trigger->deferrable = fkconstraint->deferrable;
13773 fk_trigger->initdeferred = fkconstraint->initdeferred;
13774 fk_trigger->constrrel = NULL;
13775
13776 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
13777 constraintOid, indexOid, InvalidOid,
13778 parentTrigOid, NULL, true, false);
13779
13780 /* Make changes-so-far visible */
13782
13783 return trigAddress.objectId;
13784}
13785
13786/*
13787 * createForeignKeyActionTriggers
13788 * Create the referenced-side "action" triggers that implement a foreign
13789 * key.
13790 *
13791 * Returns the OIDs of the so created triggers in *deleteTrigOid and
13792 * *updateTrigOid.
13793 */
13794static void
13795createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
13796 Oid constraintOid, Oid indexOid,
13797 Oid parentDelTrigger, Oid parentUpdTrigger,
13798 Oid *deleteTrigOid, Oid *updateTrigOid)
13799{
13800 CreateTrigStmt *fk_trigger;
13801 ObjectAddress trigAddress;
13802
13803 /*
13804 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13805 * DELETE action on the referenced table.
13806 */
13807 fk_trigger = makeNode(CreateTrigStmt);
13808 fk_trigger->replace = false;
13809 fk_trigger->isconstraint = true;
13810 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13811 fk_trigger->relation = NULL;
13812 fk_trigger->args = NIL;
13813 fk_trigger->row = true;
13814 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13815 fk_trigger->events = TRIGGER_TYPE_DELETE;
13816 fk_trigger->columns = NIL;
13817 fk_trigger->whenClause = NULL;
13818 fk_trigger->transitionRels = NIL;
13819 fk_trigger->constrrel = NULL;
13820
13821 switch (fkconstraint->fk_del_action)
13822 {
13824 fk_trigger->deferrable = fkconstraint->deferrable;
13825 fk_trigger->initdeferred = fkconstraint->initdeferred;
13826 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13827 break;
13829 fk_trigger->deferrable = false;
13830 fk_trigger->initdeferred = false;
13831 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13832 break;
13834 fk_trigger->deferrable = false;
13835 fk_trigger->initdeferred = false;
13836 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13837 break;
13839 fk_trigger->deferrable = false;
13840 fk_trigger->initdeferred = false;
13841 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13842 break;
13844 fk_trigger->deferrable = false;
13845 fk_trigger->initdeferred = false;
13846 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13847 break;
13848 default:
13849 elog(ERROR, "unrecognized FK action type: %d",
13850 (int) fkconstraint->fk_del_action);
13851 break;
13852 }
13853
13854 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13855 constraintOid, indexOid, InvalidOid,
13856 parentDelTrigger, NULL, true, false);
13857 if (deleteTrigOid)
13858 *deleteTrigOid = trigAddress.objectId;
13859
13860 /* Make changes-so-far visible */
13862
13863 /*
13864 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13865 * UPDATE action on the referenced table.
13866 */
13867 fk_trigger = makeNode(CreateTrigStmt);
13868 fk_trigger->replace = false;
13869 fk_trigger->isconstraint = true;
13870 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13871 fk_trigger->relation = NULL;
13872 fk_trigger->args = NIL;
13873 fk_trigger->row = true;
13874 fk_trigger->timing = TRIGGER_TYPE_AFTER;
13875 fk_trigger->events = TRIGGER_TYPE_UPDATE;
13876 fk_trigger->columns = NIL;
13877 fk_trigger->whenClause = NULL;
13878 fk_trigger->transitionRels = NIL;
13879 fk_trigger->constrrel = NULL;
13880
13881 switch (fkconstraint->fk_upd_action)
13882 {
13884 fk_trigger->deferrable = fkconstraint->deferrable;
13885 fk_trigger->initdeferred = fkconstraint->initdeferred;
13886 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13887 break;
13889 fk_trigger->deferrable = false;
13890 fk_trigger->initdeferred = false;
13891 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13892 break;
13894 fk_trigger->deferrable = false;
13895 fk_trigger->initdeferred = false;
13896 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13897 break;
13899 fk_trigger->deferrable = false;
13900 fk_trigger->initdeferred = false;
13901 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13902 break;
13904 fk_trigger->deferrable = false;
13905 fk_trigger->initdeferred = false;
13906 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13907 break;
13908 default:
13909 elog(ERROR, "unrecognized FK action type: %d",
13910 (int) fkconstraint->fk_upd_action);
13911 break;
13912 }
13913
13914 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid,
13915 constraintOid, indexOid, InvalidOid,
13916 parentUpdTrigger, NULL, true, false);
13917 if (updateTrigOid)
13918 *updateTrigOid = trigAddress.objectId;
13919}
13920
13921/*
13922 * createForeignKeyCheckTriggers
13923 * Create the referencing-side "check" triggers that implement a foreign
13924 * key.
13925 *
13926 * Returns the OIDs of the so created triggers in *insertTrigOid and
13927 * *updateTrigOid.
13928 */
13929static void
13931 Constraint *fkconstraint, Oid constraintOid,
13932 Oid indexOid,
13933 Oid parentInsTrigger, Oid parentUpdTrigger,
13934 Oid *insertTrigOid, Oid *updateTrigOid)
13935{
13936 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13937 constraintOid, indexOid,
13938 parentInsTrigger, true);
13939 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
13940 constraintOid, indexOid,
13941 parentUpdTrigger, false);
13942}
13943
13944/*
13945 * ALTER TABLE DROP CONSTRAINT
13946 *
13947 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
13948 */
13949static void
13950ATExecDropConstraint(Relation rel, const char *constrName,
13951 DropBehavior behavior, bool recurse,
13952 bool missing_ok, LOCKMODE lockmode)
13953{
13954 Relation conrel;
13955 SysScanDesc scan;
13956 ScanKeyData skey[3];
13957 HeapTuple tuple;
13958 bool found = false;
13959
13960 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
13961
13962 /*
13963 * Find and drop the target constraint
13964 */
13965 ScanKeyInit(&skey[0],
13966 Anum_pg_constraint_conrelid,
13967 BTEqualStrategyNumber, F_OIDEQ,
13969 ScanKeyInit(&skey[1],
13970 Anum_pg_constraint_contypid,
13971 BTEqualStrategyNumber, F_OIDEQ,
13973 ScanKeyInit(&skey[2],
13974 Anum_pg_constraint_conname,
13975 BTEqualStrategyNumber, F_NAMEEQ,
13976 CStringGetDatum(constrName));
13977 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13978 true, NULL, 3, skey);
13979
13980 /* There can be at most one matching row */
13981 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
13982 {
13983 dropconstraint_internal(rel, tuple, behavior, recurse, false,
13984 missing_ok, lockmode);
13985 found = true;
13986 }
13987
13988 systable_endscan(scan);
13989
13990 if (!found)
13991 {
13992 if (!missing_ok)
13993 ereport(ERROR,
13994 errcode(ERRCODE_UNDEFINED_OBJECT),
13995 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13996 constrName, RelationGetRelationName(rel)));
13997 else
13999 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14000 constrName, RelationGetRelationName(rel)));
14001 }
14002
14004}
14005
14006/*
14007 * Remove a constraint, using its pg_constraint tuple
14008 *
14009 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
14010 * DROP NOT NULL.
14011 *
14012 * Returns the address of the constraint being removed.
14013 */
14014static ObjectAddress
14016 bool recurse, bool recursing, bool missing_ok,
14017 LOCKMODE lockmode)
14018{
14019 Relation conrel;
14021 ObjectAddress conobj;
14022 List *children;
14023 bool is_no_inherit_constraint = false;
14024 char *constrName;
14025 char *colname = NULL;
14026
14027 /* Guard against stack overflow due to overly deep inheritance tree. */
14029
14030 /* At top level, permission check was done in ATPrepCmd, else do it */
14031 if (recursing)
14034
14035 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
14036
14037 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
14038 constrName = NameStr(con->conname);
14039
14040 /* Don't allow drop of inherited constraints */
14041 if (con->coninhcount > 0 && !recursing)
14042 ereport(ERROR,
14043 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14044 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14045 constrName, RelationGetRelationName(rel))));
14046
14047 /*
14048 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14049 *
14050 * While doing that, we're in a good position to disallow dropping a not-
14051 * null constraint underneath a primary key, a replica identity index, or
14052 * a generated identity column.
14053 */
14054 if (con->contype == CONSTRAINT_NOTNULL)
14055 {
14056 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
14057 AttrNumber attnum = extractNotNullColumn(constraintTup);
14058 Bitmapset *pkattrs;
14059 Bitmapset *irattrs;
14060 HeapTuple atttup;
14061 Form_pg_attribute attForm;
14062
14063 /* save column name for recursion step */
14064 colname = get_attname(RelationGetRelid(rel), attnum, false);
14065
14066 /*
14067 * Disallow if it's in the primary key. For partitioned tables we
14068 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14069 * return NULL if the primary key is invalid; but we still need to
14070 * protect not-null constraints under such a constraint, so check the
14071 * slow way.
14072 */
14074
14075 if (pkattrs == NULL &&
14076 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14077 {
14078 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
14079
14080 if (OidIsValid(pkindex))
14081 {
14082 Relation pk = relation_open(pkindex, AccessShareLock);
14083
14084 pkattrs = NULL;
14085 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14086 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14087
14089 }
14090 }
14091
14092 if (pkattrs &&
14094 ereport(ERROR,
14095 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14096 errmsg("column \"%s\" is in a primary key",
14097 get_attname(RelationGetRelid(rel), attnum, false)));
14098
14099 /* Disallow if it's in the replica identity */
14102 ereport(ERROR,
14103 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14104 errmsg("column \"%s\" is in index used as replica identity",
14105 get_attname(RelationGetRelid(rel), attnum, false)));
14106
14107 /* Disallow if it's a GENERATED AS IDENTITY column */
14109 if (!HeapTupleIsValid(atttup))
14110 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14111 attnum, RelationGetRelid(rel));
14112 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
14113 if (attForm->attidentity != '\0')
14114 ereport(ERROR,
14115 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14116 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14118 false),
14120
14121 /* All good -- reset attnotnull if needed */
14122 if (attForm->attnotnull)
14123 {
14124 attForm->attnotnull = false;
14125 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
14126 }
14127
14129 }
14130
14131 is_no_inherit_constraint = con->connoinherit;
14132
14133 /*
14134 * If it's a foreign-key constraint, we'd better lock the referenced table
14135 * and check that that's not in use, just as we've already done for the
14136 * constrained table (else we might, eg, be dropping a trigger that has
14137 * unfired events). But we can/must skip that in the self-referential
14138 * case.
14139 */
14140 if (con->contype == CONSTRAINT_FOREIGN &&
14141 con->confrelid != RelationGetRelid(rel))
14142 {
14143 Relation frel;
14144
14145 /* Must match lock taken by RemoveTriggerById: */
14146 frel = table_open(con->confrelid, AccessExclusiveLock);
14148 table_close(frel, NoLock);
14149 }
14150
14151 /*
14152 * Perform the actual constraint deletion
14153 */
14154 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
14155 performDeletion(&conobj, behavior, 0);
14156
14157 /*
14158 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14159 * are dropped via the dependency mechanism, so we're done here.
14160 */
14161 if (con->contype != CONSTRAINT_CHECK &&
14162 con->contype != CONSTRAINT_NOTNULL &&
14163 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14164 {
14166 return conobj;
14167 }
14168
14169 /*
14170 * Propagate to children as appropriate. Unlike most other ALTER
14171 * routines, we have to do this one level of recursion at a time; we can't
14172 * use find_all_inheritors to do it in one pass.
14173 */
14174 if (!is_no_inherit_constraint)
14175 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14176 else
14177 children = NIL;
14178
14179 foreach_oid(childrelid, children)
14180 {
14181 Relation childrel;
14182 HeapTuple tuple;
14183 Form_pg_constraint childcon;
14184
14185 /* find_inheritance_children already got lock */
14186 childrel = table_open(childrelid, NoLock);
14187 CheckAlterTableIsSafe(childrel);
14188
14189 /*
14190 * We search for not-null constraints by column name, and others by
14191 * constraint name.
14192 */
14193 if (con->contype == CONSTRAINT_NOTNULL)
14194 {
14195 tuple = findNotNullConstraint(childrelid, colname);
14196 if (!HeapTupleIsValid(tuple))
14197 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14198 colname, RelationGetRelid(childrel));
14199 }
14200 else
14201 {
14202 SysScanDesc scan;
14203 ScanKeyData skey[3];
14204
14205 ScanKeyInit(&skey[0],
14206 Anum_pg_constraint_conrelid,
14207 BTEqualStrategyNumber, F_OIDEQ,
14208 ObjectIdGetDatum(childrelid));
14209 ScanKeyInit(&skey[1],
14210 Anum_pg_constraint_contypid,
14211 BTEqualStrategyNumber, F_OIDEQ,
14213 ScanKeyInit(&skey[2],
14214 Anum_pg_constraint_conname,
14215 BTEqualStrategyNumber, F_NAMEEQ,
14216 CStringGetDatum(constrName));
14217 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
14218 true, NULL, 3, skey);
14219 /* There can only be one, so no need to loop */
14220 tuple = systable_getnext(scan);
14221 if (!HeapTupleIsValid(tuple))
14222 ereport(ERROR,
14223 (errcode(ERRCODE_UNDEFINED_OBJECT),
14224 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14225 constrName,
14226 RelationGetRelationName(childrel))));
14227 tuple = heap_copytuple(tuple);
14228 systable_endscan(scan);
14229 }
14230
14231 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
14232
14233 /* Right now only CHECK and not-null constraints can be inherited */
14234 if (childcon->contype != CONSTRAINT_CHECK &&
14235 childcon->contype != CONSTRAINT_NOTNULL)
14236 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14237
14238 if (childcon->coninhcount <= 0) /* shouldn't happen */
14239 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14240 childrelid, NameStr(childcon->conname));
14241
14242 if (recurse)
14243 {
14244 /*
14245 * If the child constraint has other definition sources, just
14246 * decrement its inheritance count; if not, recurse to delete it.
14247 */
14248 if (childcon->coninhcount == 1 && !childcon->conislocal)
14249 {
14250 /* Time to delete this child constraint, too */
14251 dropconstraint_internal(childrel, tuple, behavior,
14252 recurse, true, missing_ok,
14253 lockmode);
14254 }
14255 else
14256 {
14257 /* Child constraint must survive my deletion */
14258 childcon->coninhcount--;
14259 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14260
14261 /* Make update visible */
14263 }
14264 }
14265 else
14266 {
14267 /*
14268 * If we were told to drop ONLY in this table (no recursion) and
14269 * there are no further parents for this constraint, we need to
14270 * mark the inheritors' constraints as locally defined rather than
14271 * inherited.
14272 */
14273 childcon->coninhcount--;
14274 if (childcon->coninhcount == 0)
14275 childcon->conislocal = true;
14276
14277 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14278
14279 /* Make update visible */
14281 }
14282
14283 heap_freetuple(tuple);
14284
14285 table_close(childrel, NoLock);
14286 }
14287
14289
14290 return conobj;
14291}
14292
14293/*
14294 * ALTER COLUMN TYPE
14295 *
14296 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
14297 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
14298 * transformed (and must be, because we rely on some transformed fields).
14299 *
14300 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
14301 * table will be done "in parallel" during phase 3, so all the USING
14302 * expressions should be parsed assuming the original column types. Also,
14303 * this allows a USING expression to refer to a field that will be dropped.
14304 *
14305 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
14306 * the first two execution steps in phase 2; they must not see the effects
14307 * of any other subcommand types, since the USING expressions are parsed
14308 * against the unmodified table's state.
14309 */
14310static void
14312 AlteredTableInfo *tab, Relation rel,
14313 bool recurse, bool recursing,
14314 AlterTableCmd *cmd, LOCKMODE lockmode,
14315 AlterTableUtilityContext *context)
14316{
14317 char *colName = cmd->name;
14318 ColumnDef *def = (ColumnDef *) cmd->def;
14319 TypeName *typeName = def->typeName;
14320 Node *transform = def->cooked_default;
14321 HeapTuple tuple;
14322 Form_pg_attribute attTup;
14324 Oid targettype;
14325 int32 targettypmod;
14326 Oid targetcollid;
14328 ParseState *pstate = make_parsestate(NULL);
14329 AclResult aclresult;
14330 bool is_expr;
14331
14332 pstate->p_sourcetext = context->queryString;
14333
14334 if (rel->rd_rel->reloftype && !recursing)
14335 ereport(ERROR,
14336 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14337 errmsg("cannot alter column type of typed table"),
14338 parser_errposition(pstate, def->location)));
14339
14340 /* lookup the attribute so we can check inheritance status */
14341 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14342 if (!HeapTupleIsValid(tuple))
14343 ereport(ERROR,
14344 (errcode(ERRCODE_UNDEFINED_COLUMN),
14345 errmsg("column \"%s\" of relation \"%s\" does not exist",
14346 colName, RelationGetRelationName(rel)),
14347 parser_errposition(pstate, def->location)));
14348 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
14349 attnum = attTup->attnum;
14350
14351 /* Can't alter a system attribute */
14352 if (attnum <= 0)
14353 ereport(ERROR,
14354 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14355 errmsg("cannot alter system column \"%s\"", colName),
14356 parser_errposition(pstate, def->location)));
14357
14358 /*
14359 * Cannot specify USING when altering type of a generated column, because
14360 * that would violate the generation expression.
14361 */
14362 if (attTup->attgenerated && def->cooked_default)
14363 ereport(ERROR,
14364 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
14365 errmsg("cannot specify USING when altering type of generated column"),
14366 errdetail("Column \"%s\" is a generated column.", colName),
14367 parser_errposition(pstate, def->location)));
14368
14369 /*
14370 * Don't alter inherited columns. At outer level, there had better not be
14371 * any inherited definition; when recursing, we assume this was checked at
14372 * the parent level (see below).
14373 */
14374 if (attTup->attinhcount > 0 && !recursing)
14375 ereport(ERROR,
14376 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14377 errmsg("cannot alter inherited column \"%s\"", colName),
14378 parser_errposition(pstate, def->location)));
14379
14380 /* Don't alter columns used in the partition key */
14381 if (has_partition_attrs(rel,
14383 &is_expr))
14384 ereport(ERROR,
14385 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14386 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14387 colName, RelationGetRelationName(rel)),
14388 parser_errposition(pstate, def->location)));
14389
14390 /* Look up the target type */
14391 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14392
14393 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
14394 if (aclresult != ACLCHECK_OK)
14395 aclcheck_error_type(aclresult, targettype);
14396
14397 /* And the collation */
14398 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14399
14400 /* make sure datatype is legal for a column */
14401 CheckAttributeType(colName, targettype, targetcollid,
14402 list_make1_oid(rel->rd_rel->reltype),
14403 0);
14404
14405 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14406 {
14407 /* do nothing */
14408 }
14409 else if (tab->relkind == RELKIND_RELATION ||
14410 tab->relkind == RELKIND_PARTITIONED_TABLE)
14411 {
14412 /*
14413 * Set up an expression to transform the old data value to the new
14414 * type. If a USING option was given, use the expression as
14415 * transformed by transformAlterTableStmt, else just take the old
14416 * value and try to coerce it. We do this first so that type
14417 * incompatibility can be detected before we waste effort, and because
14418 * we need the expression to be parsed against the original table row
14419 * type.
14420 */
14421 if (!transform)
14422 {
14423 transform = (Node *) makeVar(1, attnum,
14424 attTup->atttypid, attTup->atttypmod,
14425 attTup->attcollation,
14426 0);
14427 }
14428
14429 transform = coerce_to_target_type(pstate,
14430 transform, exprType(transform),
14431 targettype, targettypmod,
14434 -1);
14435 if (transform == NULL)
14436 {
14437 /* error text depends on whether USING was specified or not */
14438 if (def->cooked_default != NULL)
14439 ereport(ERROR,
14440 (errcode(ERRCODE_DATATYPE_MISMATCH),
14441 errmsg("result of USING clause for column \"%s\""
14442 " cannot be cast automatically to type %s",
14443 colName, format_type_be(targettype)),
14444 errhint("You might need to add an explicit cast.")));
14445 else
14446 ereport(ERROR,
14447 (errcode(ERRCODE_DATATYPE_MISMATCH),
14448 errmsg("column \"%s\" cannot be cast automatically to type %s",
14449 colName, format_type_be(targettype)),
14450 !attTup->attgenerated ?
14451 /* translator: USING is SQL, don't translate it */
14452 errhint("You might need to specify \"USING %s::%s\".",
14453 quote_identifier(colName),
14454 format_type_with_typemod(targettype,
14455 targettypmod)) : 0));
14456 }
14457
14458 /* Fix collations after all else */
14459 assign_expr_collations(pstate, transform);
14460
14461 /* Plan the expr now so we can accurately assess the need to rewrite. */
14462 transform = (Node *) expression_planner((Expr *) transform);
14463
14464 /*
14465 * Add a work queue item to make ATRewriteTable update the column
14466 * contents.
14467 */
14469 newval->attnum = attnum;
14470 newval->expr = (Expr *) transform;
14471 newval->is_generated = false;
14472
14473 tab->newvals = lappend(tab->newvals, newval);
14474 if (ATColumnChangeRequiresRewrite(transform, attnum))
14476 }
14477 else if (transform)
14478 ereport(ERROR,
14479 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14480 errmsg("\"%s\" is not a table",
14482
14483 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14484 {
14485 /*
14486 * For relations or columns without storage, do this check now.
14487 * Regular tables will check it later when the table is being
14488 * rewritten.
14489 */
14490 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14491 }
14492
14493 ReleaseSysCache(tuple);
14494
14495 /*
14496 * Recurse manually by queueing a new command for each child, if
14497 * necessary. We cannot apply ATSimpleRecursion here because we need to
14498 * remap attribute numbers in the USING expression, if any.
14499 *
14500 * If we are told not to recurse, there had better not be any child
14501 * tables; else the alter would put them out of step.
14502 */
14503 if (recurse)
14504 {
14505 Oid relid = RelationGetRelid(rel);
14506 List *child_oids,
14507 *child_numparents;
14508 ListCell *lo,
14509 *li;
14510
14511 child_oids = find_all_inheritors(relid, lockmode,
14512 &child_numparents);
14513
14514 /*
14515 * find_all_inheritors does the recursive search of the inheritance
14516 * hierarchy, so all we have to do is process all of the relids in the
14517 * list that it returns.
14518 */
14519 forboth(lo, child_oids, li, child_numparents)
14520 {
14521 Oid childrelid = lfirst_oid(lo);
14522 int numparents = lfirst_int(li);
14523 Relation childrel;
14524 HeapTuple childtuple;
14525 Form_pg_attribute childattTup;
14526
14527 if (childrelid == relid)
14528 continue;
14529
14530 /* find_all_inheritors already got lock */
14531 childrel = relation_open(childrelid, NoLock);
14532 CheckAlterTableIsSafe(childrel);
14533
14534 /*
14535 * Verify that the child doesn't have any inherited definitions of
14536 * this column that came from outside this inheritance hierarchy.
14537 * (renameatt makes a similar test, though in a different way
14538 * because of its different recursion mechanism.)
14539 */
14540 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
14541 colName);
14542 if (!HeapTupleIsValid(childtuple))
14543 ereport(ERROR,
14544 (errcode(ERRCODE_UNDEFINED_COLUMN),
14545 errmsg("column \"%s\" of relation \"%s\" does not exist",
14546 colName, RelationGetRelationName(childrel))));
14547 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
14548
14549 if (childattTup->attinhcount > numparents)
14550 ereport(ERROR,
14551 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14552 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14553 colName, RelationGetRelationName(childrel))));
14554
14555 ReleaseSysCache(childtuple);
14556
14557 /*
14558 * Remap the attribute numbers. If no USING expression was
14559 * specified, there is no need for this step.
14560 */
14561 if (def->cooked_default)
14562 {
14563 AttrMap *attmap;
14564 bool found_whole_row;
14565
14566 /* create a copy to scribble on */
14567 cmd = copyObject(cmd);
14568
14569 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
14570 RelationGetDescr(rel),
14571 false);
14572 ((ColumnDef *) cmd->def)->cooked_default =
14574 1, 0,
14575 attmap,
14576 InvalidOid, &found_whole_row);
14577 if (found_whole_row)
14578 ereport(ERROR,
14579 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14580 errmsg("cannot convert whole-row table reference"),
14581 errdetail("USING expression contains a whole-row table reference.")));
14582 pfree(attmap);
14583 }
14584 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14585 relation_close(childrel, NoLock);
14586 }
14587 }
14588 else if (!recursing &&
14590 ereport(ERROR,
14591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
14592 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14593 colName)));
14594
14595 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14596 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14597}
14598
14599/*
14600 * When the data type of a column is changed, a rewrite might not be required
14601 * if the new type is sufficiently identical to the old one, and the USING
14602 * clause isn't trying to insert some other value. It's safe to skip the
14603 * rewrite in these cases:
14604 *
14605 * - the old type is binary coercible to the new type
14606 * - the new type is an unconstrained domain over the old type
14607 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
14608 *
14609 * In the case of a constrained domain, we could get by with scanning the
14610 * table and checking the constraint rather than actually rewriting it, but we
14611 * don't currently try to do that.
14612 */
14613static bool
14615{
14616 Assert(expr != NULL);
14617
14618 for (;;)
14619 {
14620 /* only one varno, so no need to check that */
14621 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14622 return false;
14623 else if (IsA(expr, RelabelType))
14624 expr = (Node *) ((RelabelType *) expr)->arg;
14625 else if (IsA(expr, CoerceToDomain))
14626 {
14627 CoerceToDomain *d = (CoerceToDomain *) expr;
14628
14630 return true;
14631 expr = (Node *) d->arg;
14632 }
14633 else if (IsA(expr, FuncExpr))
14634 {
14635 FuncExpr *f = (FuncExpr *) expr;
14636
14637 switch (f->funcid)
14638 {
14639 case F_TIMESTAMPTZ_TIMESTAMP:
14640 case F_TIMESTAMP_TIMESTAMPTZ:
14642 return true;
14643 else
14644 expr = linitial(f->args);
14645 break;
14646 default:
14647 return true;
14648 }
14649 }
14650 else
14651 return true;
14652 }
14653}
14654
14655/*
14656 * ALTER COLUMN .. SET DATA TYPE
14657 *
14658 * Return the address of the modified column.
14659 */
14660static ObjectAddress
14662 AlterTableCmd *cmd, LOCKMODE lockmode)
14663{
14664 char *colName = cmd->name;
14665 ColumnDef *def = (ColumnDef *) cmd->def;
14666 TypeName *typeName = def->typeName;
14667 HeapTuple heapTup;
14668 Form_pg_attribute attTup,
14669 attOldTup;
14671 HeapTuple typeTuple;
14672 Form_pg_type tform;
14673 Oid targettype;
14674 int32 targettypmod;
14675 Oid targetcollid;
14676 Node *defaultexpr;
14677 Relation attrelation;
14678 Relation depRel;
14679 ScanKeyData key[3];
14680 SysScanDesc scan;
14681 HeapTuple depTup;
14682 ObjectAddress address;
14683
14684 /*
14685 * Clear all the missing values if we're rewriting the table, since this
14686 * renders them pointless.
14687 */
14688 if (tab->rewrite)
14689 {
14690 Relation newrel;
14691
14692 newrel = table_open(RelationGetRelid(rel), NoLock);
14693 RelationClearMissing(newrel);
14694 relation_close(newrel, NoLock);
14695 /* make sure we don't conflict with later attribute modifications */
14697 }
14698
14699 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
14700
14701 /* Look up the target column */
14702 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
14703 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
14704 ereport(ERROR,
14705 (errcode(ERRCODE_UNDEFINED_COLUMN),
14706 errmsg("column \"%s\" of relation \"%s\" does not exist",
14707 colName, RelationGetRelationName(rel))));
14708 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14709 attnum = attTup->attnum;
14710 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
14711
14712 /* Check for multiple ALTER TYPE on same column --- can't cope */
14713 if (attTup->atttypid != attOldTup->atttypid ||
14714 attTup->atttypmod != attOldTup->atttypmod)
14715 ereport(ERROR,
14716 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14717 errmsg("cannot alter type of column \"%s\" twice",
14718 colName)));
14719
14720 /* Look up the target type (should not fail, since prep found it) */
14721 typeTuple = typenameType(NULL, typeName, &targettypmod);
14722 tform = (Form_pg_type) GETSTRUCT(typeTuple);
14723 targettype = tform->oid;
14724 /* And the collation */
14725 targetcollid = GetColumnDefCollation(NULL, def, targettype);
14726
14727 /*
14728 * If there is a default expression for the column, get it and ensure we
14729 * can coerce it to the new datatype. (We must do this before changing
14730 * the column type, because build_column_default itself will try to
14731 * coerce, and will not issue the error message we want if it fails.)
14732 *
14733 * We remove any implicit coercion steps at the top level of the old
14734 * default expression; this has been agreed to satisfy the principle of
14735 * least surprise. (The conversion to the new column type should act like
14736 * it started from what the user sees as the stored expression, and the
14737 * implicit coercions aren't going to be shown.)
14738 */
14739 if (attTup->atthasdef)
14740 {
14741 defaultexpr = build_column_default(rel, attnum);
14742 Assert(defaultexpr);
14743 defaultexpr = strip_implicit_coercions(defaultexpr);
14744 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
14745 defaultexpr, exprType(defaultexpr),
14746 targettype, targettypmod,
14749 -1);
14750 if (defaultexpr == NULL)
14751 {
14752 if (attTup->attgenerated)
14753 ereport(ERROR,
14754 (errcode(ERRCODE_DATATYPE_MISMATCH),
14755 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
14756 colName, format_type_be(targettype))));
14757 else
14758 ereport(ERROR,
14759 (errcode(ERRCODE_DATATYPE_MISMATCH),
14760 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
14761 colName, format_type_be(targettype))));
14762 }
14763 }
14764 else
14765 defaultexpr = NULL;
14766
14767 /*
14768 * Find everything that depends on the column (constraints, indexes, etc),
14769 * and record enough information to let us recreate the objects.
14770 *
14771 * The actual recreation does not happen here, but only after we have
14772 * performed all the individual ALTER TYPE operations. We have to save
14773 * the info before executing ALTER TYPE, though, else the deparser will
14774 * get confused.
14775 */
14777
14778 /*
14779 * Now scan for dependencies of this column on other things. The only
14780 * things we should find are the dependency on the column datatype and
14781 * possibly a collation dependency. Those can be removed.
14782 */
14783 depRel = table_open(DependRelationId, RowExclusiveLock);
14784
14785 ScanKeyInit(&key[0],
14786 Anum_pg_depend_classid,
14787 BTEqualStrategyNumber, F_OIDEQ,
14788 ObjectIdGetDatum(RelationRelationId));
14789 ScanKeyInit(&key[1],
14790 Anum_pg_depend_objid,
14791 BTEqualStrategyNumber, F_OIDEQ,
14793 ScanKeyInit(&key[2],
14794 Anum_pg_depend_objsubid,
14795 BTEqualStrategyNumber, F_INT4EQ,
14797
14798 scan = systable_beginscan(depRel, DependDependerIndexId, true,
14799 NULL, 3, key);
14800
14801 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14802 {
14803 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14804 ObjectAddress foundObject;
14805
14806 foundObject.classId = foundDep->refclassid;
14807 foundObject.objectId = foundDep->refobjid;
14808 foundObject.objectSubId = foundDep->refobjsubid;
14809
14810 if (foundDep->deptype != DEPENDENCY_NORMAL)
14811 elog(ERROR, "found unexpected dependency type '%c'",
14812 foundDep->deptype);
14813 if (!(foundDep->refclassid == TypeRelationId &&
14814 foundDep->refobjid == attTup->atttypid) &&
14815 !(foundDep->refclassid == CollationRelationId &&
14816 foundDep->refobjid == attTup->attcollation))
14817 elog(ERROR, "found unexpected dependency for column: %s",
14818 getObjectDescription(&foundObject, false));
14819
14820 CatalogTupleDelete(depRel, &depTup->t_self);
14821 }
14822
14823 systable_endscan(scan);
14824
14826
14827 /*
14828 * Here we go --- change the recorded column type and collation. (Note
14829 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
14830 * fix up the missing value if any.
14831 */
14832 if (attTup->atthasmissing)
14833 {
14834 Datum missingval;
14835 bool missingNull;
14836
14837 /* if rewrite is true the missing value should already be cleared */
14838 Assert(tab->rewrite == 0);
14839
14840 /* Get the missing value datum */
14841 missingval = heap_getattr(heapTup,
14842 Anum_pg_attribute_attmissingval,
14843 attrelation->rd_att,
14844 &missingNull);
14845
14846 /* if it's a null array there is nothing to do */
14847
14848 if (!missingNull)
14849 {
14850 /*
14851 * Get the datum out of the array and repack it in a new array
14852 * built with the new type data. We assume that since the table
14853 * doesn't need rewriting, the actual Datum doesn't need to be
14854 * changed, only the array metadata.
14855 */
14856
14857 int one = 1;
14858 bool isNull;
14859 Datum valuesAtt[Natts_pg_attribute] = {0};
14860 bool nullsAtt[Natts_pg_attribute] = {0};
14861 bool replacesAtt[Natts_pg_attribute] = {0};
14862 HeapTuple newTup;
14863
14864 missingval = array_get_element(missingval,
14865 1,
14866 &one,
14867 0,
14868 attTup->attlen,
14869 attTup->attbyval,
14870 attTup->attalign,
14871 &isNull);
14872 missingval = PointerGetDatum(construct_array(&missingval,
14873 1,
14874 targettype,
14875 tform->typlen,
14876 tform->typbyval,
14877 tform->typalign));
14878
14879 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
14880 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
14881 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
14882
14883 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
14884 valuesAtt, nullsAtt, replacesAtt);
14885 heap_freetuple(heapTup);
14886 heapTup = newTup;
14887 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
14888 }
14889 }
14890
14891 attTup->atttypid = targettype;
14892 attTup->atttypmod = targettypmod;
14893 attTup->attcollation = targetcollid;
14894 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
14895 ereport(ERROR,
14896 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
14897 errmsg("too many array dimensions"));
14898 attTup->attndims = list_length(typeName->arrayBounds);
14899 attTup->attlen = tform->typlen;
14900 attTup->attbyval = tform->typbyval;
14901 attTup->attalign = tform->typalign;
14902 attTup->attstorage = tform->typstorage;
14903 attTup->attcompression = InvalidCompressionMethod;
14904
14905 ReleaseSysCache(typeTuple);
14906
14907 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
14908
14909 table_close(attrelation, RowExclusiveLock);
14910
14911 /* Install dependencies on new datatype and collation */
14914
14915 /*
14916 * Drop any pg_statistic entry for the column, since it's now wrong type
14917 */
14919
14920 InvokeObjectPostAlterHook(RelationRelationId,
14921 RelationGetRelid(rel), attnum);
14922
14923 /*
14924 * Update the default, if present, by brute force --- remove and re-add
14925 * the default. Probably unsafe to take shortcuts, since the new version
14926 * may well have additional dependencies. (It's okay to do this now,
14927 * rather than after other ALTER TYPE commands, since the default won't
14928 * depend on other column types.)
14929 */
14930 if (defaultexpr)
14931 {
14932 /*
14933 * If it's a GENERATED default, drop its dependency records, in
14934 * particular its INTERNAL dependency on the column, which would
14935 * otherwise cause dependency.c to refuse to perform the deletion.
14936 */
14937 if (attTup->attgenerated)
14938 {
14939 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14940
14941 if (!OidIsValid(attrdefoid))
14942 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14943 RelationGetRelid(rel), attnum);
14944 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14945 }
14946
14947 /*
14948 * Make updates-so-far visible, particularly the new pg_attribute row
14949 * which will be updated again.
14950 */
14952
14953 /*
14954 * We use RESTRICT here for safety, but at present we do not expect
14955 * anything to depend on the default.
14956 */
14958 true);
14959
14960 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
14961 }
14962
14963 ObjectAddressSubSet(address, RelationRelationId,
14964 RelationGetRelid(rel), attnum);
14965
14966 /* Cleanup */
14967 heap_freetuple(heapTup);
14968
14969 return address;
14970}
14971
14972/*
14973 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14974 * that depends on the column (constraints, indexes, etc), and record enough
14975 * information to let us recreate the objects.
14976 */
14977static void
14979 Relation rel, AttrNumber attnum, const char *colName)
14980{
14981 Relation depRel;
14982 ScanKeyData key[3];
14983 SysScanDesc scan;
14984 HeapTuple depTup;
14985
14986 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14987
14988 depRel = table_open(DependRelationId, RowExclusiveLock);
14989
14990 ScanKeyInit(&key[0],
14991 Anum_pg_depend_refclassid,
14992 BTEqualStrategyNumber, F_OIDEQ,
14993 ObjectIdGetDatum(RelationRelationId));
14994 ScanKeyInit(&key[1],
14995 Anum_pg_depend_refobjid,
14996 BTEqualStrategyNumber, F_OIDEQ,
14998 ScanKeyInit(&key[2],
14999 Anum_pg_depend_refobjsubid,
15000 BTEqualStrategyNumber, F_INT4EQ,
15002
15003 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15004 NULL, 3, key);
15005
15006 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
15007 {
15008 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
15009 ObjectAddress foundObject;
15010
15011 foundObject.classId = foundDep->classid;
15012 foundObject.objectId = foundDep->objid;
15013 foundObject.objectSubId = foundDep->objsubid;
15014
15015 switch (foundObject.classId)
15016 {
15017 case RelationRelationId:
15018 {
15019 char relKind = get_rel_relkind(foundObject.objectId);
15020
15021 if (relKind == RELKIND_INDEX ||
15022 relKind == RELKIND_PARTITIONED_INDEX)
15023 {
15024 Assert(foundObject.objectSubId == 0);
15025 RememberIndexForRebuilding(foundObject.objectId, tab);
15026 }
15027 else if (relKind == RELKIND_SEQUENCE)
15028 {
15029 /*
15030 * This must be a SERIAL column's sequence. We need
15031 * not do anything to it.
15032 */
15033 Assert(foundObject.objectSubId == 0);
15034 }
15035 else
15036 {
15037 /* Not expecting any other direct dependencies... */
15038 elog(ERROR, "unexpected object depending on column: %s",
15039 getObjectDescription(&foundObject, false));
15040 }
15041 break;
15042 }
15043
15044 case ConstraintRelationId:
15045 Assert(foundObject.objectSubId == 0);
15046 RememberConstraintForRebuilding(foundObject.objectId, tab);
15047 break;
15048
15049 case ProcedureRelationId:
15050
15051 /*
15052 * A new-style SQL function can depend on a column, if that
15053 * column is referenced in the parsed function body. Ideally
15054 * we'd automatically update the function by deparsing and
15055 * reparsing it, but that's risky and might well fail anyhow.
15056 * FIXME someday.
15057 *
15058 * This is only a problem for AT_AlterColumnType, not
15059 * AT_SetExpression.
15060 */
15061 if (subtype == AT_AlterColumnType)
15062 ereport(ERROR,
15063 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15064 errmsg("cannot alter type of a column used by a function or procedure"),
15065 errdetail("%s depends on column \"%s\"",
15066 getObjectDescription(&foundObject, false),
15067 colName)));
15068 break;
15069
15070 case RewriteRelationId:
15071
15072 /*
15073 * View/rule bodies have pretty much the same issues as
15074 * function bodies. FIXME someday.
15075 */
15076 if (subtype == AT_AlterColumnType)
15077 ereport(ERROR,
15078 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15079 errmsg("cannot alter type of a column used by a view or rule"),
15080 errdetail("%s depends on column \"%s\"",
15081 getObjectDescription(&foundObject, false),
15082 colName)));
15083 break;
15084
15085 case TriggerRelationId:
15086
15087 /*
15088 * A trigger can depend on a column because the column is
15089 * specified as an update target, or because the column is
15090 * used in the trigger's WHEN condition. The first case would
15091 * not require any extra work, but the second case would
15092 * require updating the WHEN expression, which has the same
15093 * issues as above. Since we can't easily tell which case
15094 * applies, we punt for both. FIXME someday.
15095 */
15096 if (subtype == AT_AlterColumnType)
15097 ereport(ERROR,
15098 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15099 errmsg("cannot alter type of a column used in a trigger definition"),
15100 errdetail("%s depends on column \"%s\"",
15101 getObjectDescription(&foundObject, false),
15102 colName)));
15103 break;
15104
15105 case PolicyRelationId:
15106
15107 /*
15108 * A policy can depend on a column because the column is
15109 * specified in the policy's USING or WITH CHECK qual
15110 * expressions. It might be possible to rewrite and recheck
15111 * the policy expression, but punt for now. It's certainly
15112 * easy enough to remove and recreate the policy; still, FIXME
15113 * someday.
15114 */
15115 if (subtype == AT_AlterColumnType)
15116 ereport(ERROR,
15117 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15118 errmsg("cannot alter type of a column used in a policy definition"),
15119 errdetail("%s depends on column \"%s\"",
15120 getObjectDescription(&foundObject, false),
15121 colName)));
15122 break;
15123
15124 case AttrDefaultRelationId:
15125 {
15127
15128 if (col.objectId == RelationGetRelid(rel) &&
15129 col.objectSubId == attnum)
15130 {
15131 /*
15132 * Ignore the column's own default expression. The
15133 * caller deals with it.
15134 */
15135 }
15136 else
15137 {
15138 /*
15139 * This must be a reference from the expression of a
15140 * generated column elsewhere in the same table.
15141 * Changing the type/generated expression of a column
15142 * that is used by a generated column is not allowed
15143 * by SQL standard, so just punt for now. It might be
15144 * doable with some thinking and effort.
15145 */
15146 if (subtype == AT_AlterColumnType)
15147 ereport(ERROR,
15148 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15149 errmsg("cannot alter type of a column used by a generated column"),
15150 errdetail("Column \"%s\" is used by generated column \"%s\".",
15151 colName,
15153 col.objectSubId,
15154 false))));
15155 }
15156 break;
15157 }
15158
15159 case StatisticExtRelationId:
15160
15161 /*
15162 * Give the extended-stats machinery a chance to fix anything
15163 * that this column type change would break.
15164 */
15165 RememberStatisticsForRebuilding(foundObject.objectId, tab);
15166 break;
15167
15168 case PublicationRelRelationId:
15169
15170 /*
15171 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15172 * clause. Same issues as above. FIXME someday.
15173 */
15174 if (subtype == AT_AlterColumnType)
15175 ereport(ERROR,
15176 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15177 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15178 errdetail("%s depends on column \"%s\"",
15179 getObjectDescription(&foundObject, false),
15180 colName)));
15181 break;
15182
15183 default:
15184
15185 /*
15186 * We don't expect any other sorts of objects to depend on a
15187 * column.
15188 */
15189 elog(ERROR, "unexpected object depending on column: %s",
15190 getObjectDescription(&foundObject, false));
15191 break;
15192 }
15193 }
15194
15195 systable_endscan(scan);
15196 table_close(depRel, NoLock);
15197}
15198
15199/*
15200 * Subroutine for ATExecAlterColumnType: remember that a replica identity
15201 * needs to be reset.
15202 */
15203static void
15205{
15206 if (!get_index_isreplident(indoid))
15207 return;
15208
15209 if (tab->replicaIdentityIndex)
15210 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15211
15212 tab->replicaIdentityIndex = get_rel_name(indoid);
15213}
15214
15215/*
15216 * Subroutine for ATExecAlterColumnType: remember any clustered index.
15217 */
15218static void
15220{
15221 if (!get_index_isclustered(indoid))
15222 return;
15223
15224 if (tab->clusterOnIndex)
15225 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15226
15227 tab->clusterOnIndex = get_rel_name(indoid);
15228}
15229
15230/*
15231 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
15232 * to be rebuilt (which we might already know).
15233 */
15234static void
15236{
15237 /*
15238 * This de-duplication check is critical for two independent reasons: we
15239 * mustn't try to recreate the same constraint twice, and if a constraint
15240 * depends on more than one column whose type is to be altered, we must
15241 * capture its definition string before applying any of the column type
15242 * changes. ruleutils.c will get confused if we ask again later.
15243 */
15244 if (!list_member_oid(tab->changedConstraintOids, conoid))
15245 {
15246 /* OK, capture the constraint's existing definition string */
15247 char *defstring = pg_get_constraintdef_command(conoid);
15248 Oid indoid;
15249
15250 /*
15251 * It is critical to create not-null constraints ahead of primary key
15252 * indexes; otherwise, the not-null constraint would be created by the
15253 * primary key, and the constraint name would be wrong.
15254 */
15255 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
15256 {
15257 tab->changedConstraintOids = lcons_oid(conoid,
15259 tab->changedConstraintDefs = lcons(defstring,
15261 }
15262 else
15263 {
15264
15266 conoid);
15268 defstring);
15269 }
15270
15271 /*
15272 * For the index of a constraint, if any, remember if it is used for
15273 * the table's replica identity or if it is a clustered index, so that
15274 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15275 * those properties.
15276 */
15277 indoid = get_constraint_index(conoid);
15278 if (OidIsValid(indoid))
15279 {
15281 RememberClusterOnForRebuilding(indoid, tab);
15282 }
15283 }
15284}
15285
15286/*
15287 * Subroutine for ATExecAlterColumnType: remember that an index needs
15288 * to be rebuilt (which we might already know).
15289 */
15290static void
15292{
15293 /*
15294 * This de-duplication check is critical for two independent reasons: we
15295 * mustn't try to recreate the same index twice, and if an index depends
15296 * on more than one column whose type is to be altered, we must capture
15297 * its definition string before applying any of the column type changes.
15298 * ruleutils.c will get confused if we ask again later.
15299 */
15300 if (!list_member_oid(tab->changedIndexOids, indoid))
15301 {
15302 /*
15303 * Before adding it as an index-to-rebuild, we'd better see if it
15304 * belongs to a constraint, and if so rebuild the constraint instead.
15305 * Typically this check fails, because constraint indexes normally
15306 * have only dependencies on their constraint. But it's possible for
15307 * such an index to also have direct dependencies on table columns,
15308 * for example with a partial exclusion constraint.
15309 */
15310 Oid conoid = get_index_constraint(indoid);
15311
15312 if (OidIsValid(conoid))
15313 {
15315 }
15316 else
15317 {
15318 /* OK, capture the index's existing definition string */
15319 char *defstring = pg_get_indexdef_string(indoid);
15320
15322 indoid);
15324 defstring);
15325
15326 /*
15327 * Remember if this index is used for the table's replica identity
15328 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15329 * can queue up commands necessary to restore those properties.
15330 */
15332 RememberClusterOnForRebuilding(indoid, tab);
15333 }
15334 }
15335}
15336
15337/*
15338 * Subroutine for ATExecAlterColumnType: remember that a statistics object
15339 * needs to be rebuilt (which we might already know).
15340 */
15341static void
15343{
15344 /*
15345 * This de-duplication check is critical for two independent reasons: we
15346 * mustn't try to recreate the same statistics object twice, and if the
15347 * statistics object depends on more than one column whose type is to be
15348 * altered, we must capture its definition string before applying any of
15349 * the type changes. ruleutils.c will get confused if we ask again later.
15350 */
15351 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
15352 {
15353 /* OK, capture the statistics object's existing definition string */
15354 char *defstring = pg_get_statisticsobjdef_string(stxoid);
15355
15357 stxoid);
15359 defstring);
15360 }
15361}
15362
15363/*
15364 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
15365 * operations for a particular relation. We have to drop and recreate all the
15366 * indexes and constraints that depend on the altered columns. We do the
15367 * actual dropping here, but re-creation is managed by adding work queue
15368 * entries to do those steps later.
15369 */
15370static void
15372{
15373 ObjectAddress obj;
15374 ObjectAddresses *objects;
15375 ListCell *def_item;
15376 ListCell *oid_item;
15377
15378 /*
15379 * Collect all the constraints and indexes to drop so we can process them
15380 * in a single call. That way we don't have to worry about dependencies
15381 * among them.
15382 */
15383 objects = new_object_addresses();
15384
15385 /*
15386 * Re-parse the index and constraint definitions, and attach them to the
15387 * appropriate work queue entries. We do this before dropping because in
15388 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
15389 * lock on the table the constraint is attached to, and we need to get
15390 * that before reparsing/dropping.
15391 *
15392 * We can't rely on the output of deparsing to tell us which relation to
15393 * operate on, because concurrent activity might have made the name
15394 * resolve differently. Instead, we've got to use the OID of the
15395 * constraint or index we're processing to figure out which relation to
15396 * operate on.
15397 */
15398 forboth(oid_item, tab->changedConstraintOids,
15399 def_item, tab->changedConstraintDefs)
15400 {
15401 Oid oldId = lfirst_oid(oid_item);
15402 HeapTuple tup;
15404 Oid relid;
15405 Oid confrelid;
15406 char contype;
15407 bool conislocal;
15408
15409 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15410 if (!HeapTupleIsValid(tup)) /* should not happen */
15411 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15412 con = (Form_pg_constraint) GETSTRUCT(tup);
15413 if (OidIsValid(con->conrelid))
15414 relid = con->conrelid;
15415 else
15416 {
15417 /* must be a domain constraint */
15418 relid = get_typ_typrelid(getBaseType(con->contypid));
15419 if (!OidIsValid(relid))
15420 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15421 }
15422 confrelid = con->confrelid;
15423 contype = con->contype;
15424 conislocal = con->conislocal;
15425 ReleaseSysCache(tup);
15426
15427 ObjectAddressSet(obj, ConstraintRelationId, oldId);
15428 add_exact_object_address(&obj, objects);
15429
15430 /*
15431 * If the constraint is inherited (only), we don't want to inject a
15432 * new definition here; it'll get recreated when
15433 * ATAddCheckNNConstraint recurses from adding the parent table's
15434 * constraint. But we had to carry the info this far so that we can
15435 * drop the constraint below.
15436 */
15437 if (!conislocal)
15438 continue;
15439
15440 /*
15441 * When rebuilding an FK constraint that references the table we're
15442 * modifying, we might not yet have any lock on the FK's table, so get
15443 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
15444 * step, so there's no value in asking for anything weaker.
15445 */
15446 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
15448
15449 ATPostAlterTypeParse(oldId, relid, confrelid,
15450 (char *) lfirst(def_item),
15451 wqueue, lockmode, tab->rewrite);
15452 }
15453 forboth(oid_item, tab->changedIndexOids,
15454 def_item, tab->changedIndexDefs)
15455 {
15456 Oid oldId = lfirst_oid(oid_item);
15457 Oid relid;
15458
15459 relid = IndexGetRelation(oldId, false);
15460 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15461 (char *) lfirst(def_item),
15462 wqueue, lockmode, tab->rewrite);
15463
15464 ObjectAddressSet(obj, RelationRelationId, oldId);
15465 add_exact_object_address(&obj, objects);
15466 }
15467
15468 /* add dependencies for new statistics */
15469 forboth(oid_item, tab->changedStatisticsOids,
15470 def_item, tab->changedStatisticsDefs)
15471 {
15472 Oid oldId = lfirst_oid(oid_item);
15473 Oid relid;
15474
15475 relid = StatisticsGetRelation(oldId, false);
15476 ATPostAlterTypeParse(oldId, relid, InvalidOid,
15477 (char *) lfirst(def_item),
15478 wqueue, lockmode, tab->rewrite);
15479
15480 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
15481 add_exact_object_address(&obj, objects);
15482 }
15483
15484 /*
15485 * Queue up command to restore replica identity index marking
15486 */
15487 if (tab->replicaIdentityIndex)
15488 {
15491
15492 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15493 subcmd->name = tab->replicaIdentityIndex;
15495 cmd->def = (Node *) subcmd;
15496
15497 /* do it after indexes and constraints */
15498 tab->subcmds[AT_PASS_OLD_CONSTR] =
15499 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15500 }
15501
15502 /*
15503 * Queue up command to restore marking of index used for cluster.
15504 */
15505 if (tab->clusterOnIndex)
15506 {
15508
15509 cmd->subtype = AT_ClusterOn;
15510 cmd->name = tab->clusterOnIndex;
15511
15512 /* do it after indexes and constraints */
15513 tab->subcmds[AT_PASS_OLD_CONSTR] =
15514 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
15515 }
15516
15517 /*
15518 * It should be okay to use DROP_RESTRICT here, since nothing else should
15519 * be depending on these objects.
15520 */
15522
15523 free_object_addresses(objects);
15524
15525 /*
15526 * The objects will get recreated during subsequent passes over the work
15527 * queue.
15528 */
15529}
15530
15531/*
15532 * Parse the previously-saved definition string for a constraint, index or
15533 * statistics object against the newly-established column data type(s), and
15534 * queue up the resulting command parsetrees for execution.
15535 *
15536 * This might fail if, for example, you have a WHERE clause that uses an
15537 * operator that's not available for the new column type.
15538 */
15539static void
15540ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
15541 List **wqueue, LOCKMODE lockmode, bool rewrite)
15542{
15543 List *raw_parsetree_list;
15544 List *querytree_list;
15545 ListCell *list_item;
15546 Relation rel;
15547
15548 /*
15549 * We expect that we will get only ALTER TABLE and CREATE INDEX
15550 * statements. Hence, there is no need to pass them through
15551 * parse_analyze_*() or the rewriter, but instead we need to pass them
15552 * through parse_utilcmd.c to make them ready for execution.
15553 */
15554 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
15555 querytree_list = NIL;
15556 foreach(list_item, raw_parsetree_list)
15557 {
15558 RawStmt *rs = lfirst_node(RawStmt, list_item);
15559 Node *stmt = rs->stmt;
15560
15561 if (IsA(stmt, IndexStmt))
15562 querytree_list = lappend(querytree_list,
15563 transformIndexStmt(oldRelId,
15564 (IndexStmt *) stmt,
15565 cmd));
15566 else if (IsA(stmt, AlterTableStmt))
15567 {
15568 List *beforeStmts;
15569 List *afterStmts;
15570
15571 stmt = (Node *) transformAlterTableStmt(oldRelId,
15572 (AlterTableStmt *) stmt,
15573 cmd,
15574 &beforeStmts,
15575 &afterStmts);
15576 querytree_list = list_concat(querytree_list, beforeStmts);
15577 querytree_list = lappend(querytree_list, stmt);
15578 querytree_list = list_concat(querytree_list, afterStmts);
15579 }
15580 else if (IsA(stmt, CreateStatsStmt))
15581 querytree_list = lappend(querytree_list,
15582 transformStatsStmt(oldRelId,
15584 cmd));
15585 else
15586 querytree_list = lappend(querytree_list, stmt);
15587 }
15588
15589 /* Caller should already have acquired whatever lock we need. */
15590 rel = relation_open(oldRelId, NoLock);
15591
15592 /*
15593 * Attach each generated command to the proper place in the work queue.
15594 * Note this could result in creation of entirely new work-queue entries.
15595 *
15596 * Also note that we have to tweak the command subtypes, because it turns
15597 * out that re-creation of indexes and constraints has to act a bit
15598 * differently from initial creation.
15599 */
15600 foreach(list_item, querytree_list)
15601 {
15602 Node *stm = (Node *) lfirst(list_item);
15603 AlteredTableInfo *tab;
15604
15605 tab = ATGetQueueEntry(wqueue, rel);
15606
15607 if (IsA(stm, IndexStmt))
15608 {
15609 IndexStmt *stmt = (IndexStmt *) stm;
15610 AlterTableCmd *newcmd;
15611
15612 if (!rewrite)
15613 TryReuseIndex(oldId, stmt);
15614 stmt->reset_default_tblspc = true;
15615 /* keep the index's comment */
15616 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15617
15618 newcmd = makeNode(AlterTableCmd);
15619 newcmd->subtype = AT_ReAddIndex;
15620 newcmd->def = (Node *) stmt;
15622 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
15623 }
15624 else if (IsA(stm, AlterTableStmt))
15625 {
15627 ListCell *lcmd;
15628
15629 foreach(lcmd, stmt->cmds)
15630 {
15632
15633 if (cmd->subtype == AT_AddIndex)
15634 {
15635 IndexStmt *indstmt;
15636 Oid indoid;
15637
15638 indstmt = castNode(IndexStmt, cmd->def);
15639 indoid = get_constraint_index(oldId);
15640
15641 if (!rewrite)
15642 TryReuseIndex(indoid, indstmt);
15643 /* keep any comment on the index */
15644 indstmt->idxcomment = GetComment(indoid,
15645 RelationRelationId, 0);
15646 indstmt->reset_default_tblspc = true;
15647
15648 cmd->subtype = AT_ReAddIndex;
15650 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15651
15652 /* recreate any comment on the constraint */
15655 oldId,
15656 rel,
15657 NIL,
15658 indstmt->idxname);
15659 }
15660 else if (cmd->subtype == AT_AddConstraint)
15661 {
15662 Constraint *con = castNode(Constraint, cmd->def);
15663
15664 con->old_pktable_oid = refRelId;
15665 /* rewriting neither side of a FK */
15666 if (con->contype == CONSTR_FOREIGN &&
15667 !rewrite && tab->rewrite == 0)
15668 TryReuseForeignKey(oldId, con);
15669 con->reset_default_tblspc = true;
15673
15674 /*
15675 * Recreate any comment on the constraint. If we have
15676 * recreated a primary key, then transformTableConstraint
15677 * has added an unnamed not-null constraint here; skip
15678 * this in that case.
15679 */
15680 if (con->conname)
15683 oldId,
15684 rel,
15685 NIL,
15686 con->conname);
15687 else
15688 Assert(con->contype == CONSTR_NOTNULL);
15689 }
15690 else
15691 elog(ERROR, "unexpected statement subtype: %d",
15692 (int) cmd->subtype);
15693 }
15694 }
15695 else if (IsA(stm, AlterDomainStmt))
15696 {
15698
15699 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
15700 {
15701 Constraint *con = castNode(Constraint, stmt->def);
15703
15705 cmd->def = (Node *) stmt;
15708
15709 /* recreate any comment on the constraint */
15712 oldId,
15713 NULL,
15714 stmt->typeName,
15715 con->conname);
15716 }
15717 else
15718 elog(ERROR, "unexpected statement subtype: %d",
15719 (int) stmt->subtype);
15720 }
15721 else if (IsA(stm, CreateStatsStmt))
15722 {
15724 AlterTableCmd *newcmd;
15725
15726 /* keep the statistics object's comment */
15727 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15728
15729 newcmd = makeNode(AlterTableCmd);
15730 newcmd->subtype = AT_ReAddStatistics;
15731 newcmd->def = (Node *) stmt;
15732 tab->subcmds[AT_PASS_MISC] =
15733 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
15734 }
15735 else
15736 elog(ERROR, "unexpected statement type: %d",
15737 (int) nodeTag(stm));
15738 }
15739
15740 relation_close(rel, NoLock);
15741}
15742
15743/*
15744 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
15745 * for a table or domain constraint that is being rebuilt.
15746 *
15747 * objid is the OID of the constraint.
15748 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
15749 * as a string list) for a domain constraint.
15750 * (We could dig that info, as well as the conname, out of the pg_constraint
15751 * entry; but callers already have them so might as well pass them.)
15752 */
15753static void
15755 Relation rel, List *domname,
15756 const char *conname)
15757{
15758 CommentStmt *cmd;
15759 char *comment_str;
15760 AlterTableCmd *newcmd;
15761
15762 /* Look for comment for object wanted, and leave if none */
15763 comment_str = GetComment(objid, ConstraintRelationId, 0);
15764 if (comment_str == NULL)
15765 return;
15766
15767 /* Build CommentStmt node, copying all input data for safety */
15768 cmd = makeNode(CommentStmt);
15769 if (rel)
15770 {
15772 cmd->object = (Node *)
15775 makeString(pstrdup(conname)));
15776 }
15777 else
15778 {
15780 cmd->object = (Node *)
15782 makeString(pstrdup(conname)));
15783 }
15784 cmd->comment = comment_str;
15785
15786 /* Append it to list of commands */
15787 newcmd = makeNode(AlterTableCmd);
15788 newcmd->subtype = AT_ReAddComment;
15789 newcmd->def = (Node *) cmd;
15790 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15791}
15792
15793/*
15794 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
15795 * for the real analysis, then mutates the IndexStmt based on that verdict.
15796 */
15797static void
15799{
15800 if (CheckIndexCompatible(oldId,
15801 stmt->accessMethod,
15802 stmt->indexParams,
15803 stmt->excludeOpNames,
15804 stmt->iswithoutoverlaps))
15805 {
15806 Relation irel = index_open(oldId, NoLock);
15807
15808 /* If it's a partitioned index, there is no storage to share. */
15809 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
15810 {
15811 stmt->oldNumber = irel->rd_locator.relNumber;
15812 stmt->oldCreateSubid = irel->rd_createSubid;
15813 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
15814 }
15815 index_close(irel, NoLock);
15816 }
15817}
15818
15819/*
15820 * Subroutine for ATPostAlterTypeParse().
15821 *
15822 * Stash the old P-F equality operator into the Constraint node, for possible
15823 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
15824 * this constraint can be skipped.
15825 */
15826static void
15828{
15829 HeapTuple tup;
15830 Datum adatum;
15831 ArrayType *arr;
15832 Oid *rawarr;
15833 int numkeys;
15834 int i;
15835
15836 Assert(con->contype == CONSTR_FOREIGN);
15837 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
15838
15839 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
15840 if (!HeapTupleIsValid(tup)) /* should not happen */
15841 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15842
15843 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
15844 Anum_pg_constraint_conpfeqop);
15845 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
15846 numkeys = ARR_DIMS(arr)[0];
15847 /* test follows the one in ri_FetchConstraintInfo() */
15848 if (ARR_NDIM(arr) != 1 ||
15849 ARR_HASNULL(arr) ||
15850 ARR_ELEMTYPE(arr) != OIDOID)
15851 elog(ERROR, "conpfeqop is not a 1-D Oid array");
15852 rawarr = (Oid *) ARR_DATA_PTR(arr);
15853
15854 /* stash a List of the operator Oids in our Constraint node */
15855 for (i = 0; i < numkeys; i++)
15856 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
15857
15858 ReleaseSysCache(tup);
15859}
15860
15861/*
15862 * ALTER COLUMN .. OPTIONS ( ... )
15863 *
15864 * Returns the address of the modified column
15865 */
15866static ObjectAddress
15868 const char *colName,
15869 List *options,
15870 LOCKMODE lockmode)
15871{
15872 Relation ftrel;
15873 Relation attrel;
15874 ForeignServer *server;
15875 ForeignDataWrapper *fdw;
15876 HeapTuple tuple;
15877 HeapTuple newtuple;
15878 bool isnull;
15879 Datum repl_val[Natts_pg_attribute];
15880 bool repl_null[Natts_pg_attribute];
15881 bool repl_repl[Natts_pg_attribute];
15882 Datum datum;
15883 Form_pg_foreign_table fttableform;
15884 Form_pg_attribute atttableform;
15886 ObjectAddress address;
15887
15888 if (options == NIL)
15889 return InvalidObjectAddress;
15890
15891 /* First, determine FDW validator associated to the foreign table. */
15892 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
15893 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
15894 if (!HeapTupleIsValid(tuple))
15895 ereport(ERROR,
15896 (errcode(ERRCODE_UNDEFINED_OBJECT),
15897 errmsg("foreign table \"%s\" does not exist",
15899 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
15900 server = GetForeignServer(fttableform->ftserver);
15901 fdw = GetForeignDataWrapper(server->fdwid);
15902
15904 ReleaseSysCache(tuple);
15905
15906 attrel = table_open(AttributeRelationId, RowExclusiveLock);
15907 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
15908 if (!HeapTupleIsValid(tuple))
15909 ereport(ERROR,
15910 (errcode(ERRCODE_UNDEFINED_COLUMN),
15911 errmsg("column \"%s\" of relation \"%s\" does not exist",
15912 colName, RelationGetRelationName(rel))));
15913
15914 /* Prevent them from altering a system attribute */
15915 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
15916 attnum = atttableform->attnum;
15917 if (attnum <= 0)
15918 ereport(ERROR,
15919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15920 errmsg("cannot alter system column \"%s\"", colName)));
15921
15922
15923 /* Initialize buffers for new tuple values */
15924 memset(repl_val, 0, sizeof(repl_val));
15925 memset(repl_null, false, sizeof(repl_null));
15926 memset(repl_repl, false, sizeof(repl_repl));
15927
15928 /* Extract the current options */
15929 datum = SysCacheGetAttr(ATTNAME,
15930 tuple,
15931 Anum_pg_attribute_attfdwoptions,
15932 &isnull);
15933 if (isnull)
15934 datum = PointerGetDatum(NULL);
15935
15936 /* Transform the options */
15937 datum = transformGenericOptions(AttributeRelationId,
15938 datum,
15939 options,
15940 fdw->fdwvalidator);
15941
15942 if (PointerIsValid(DatumGetPointer(datum)))
15943 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15944 else
15945 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15946
15947 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15948
15949 /* Everything looks good - update the tuple */
15950
15951 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15952 repl_val, repl_null, repl_repl);
15953
15954 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15955
15956 InvokeObjectPostAlterHook(RelationRelationId,
15957 RelationGetRelid(rel),
15958 atttableform->attnum);
15959 ObjectAddressSubSet(address, RelationRelationId,
15960 RelationGetRelid(rel), attnum);
15961
15962 ReleaseSysCache(tuple);
15963
15965
15966 heap_freetuple(newtuple);
15967
15968 return address;
15969}
15970
15971/*
15972 * ALTER TABLE OWNER
15973 *
15974 * recursing is true if we are recursing from a table to its indexes,
15975 * sequences, or toast table. We don't allow the ownership of those things to
15976 * be changed separately from the parent table. Also, we can skip permission
15977 * checks (this is necessary not just an optimization, else we'd fail to
15978 * handle toast tables properly).
15979 *
15980 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15981 * free-standing composite type.
15982 */
15983void
15984ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15985{
15986 Relation target_rel;
15987 Relation class_rel;
15988 HeapTuple tuple;
15989 Form_pg_class tuple_class;
15990
15991 /*
15992 * Get exclusive lock till end of transaction on the target table. Use
15993 * relation_open so that we can work on indexes and sequences.
15994 */
15995 target_rel = relation_open(relationOid, lockmode);
15996
15997 /* Get its pg_class tuple, too */
15998 class_rel = table_open(RelationRelationId, RowExclusiveLock);
15999
16000 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16001 if (!HeapTupleIsValid(tuple))
16002 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16003 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
16004
16005 /* Can we change the ownership of this tuple? */
16006 switch (tuple_class->relkind)
16007 {
16008 case RELKIND_RELATION:
16009 case RELKIND_VIEW:
16010 case RELKIND_MATVIEW:
16011 case RELKIND_FOREIGN_TABLE:
16012 case RELKIND_PARTITIONED_TABLE:
16013 /* ok to change owner */
16014 break;
16015 case RELKIND_INDEX:
16016 if (!recursing)
16017 {
16018 /*
16019 * Because ALTER INDEX OWNER used to be allowed, and in fact
16020 * is generated by old versions of pg_dump, we give a warning
16021 * and do nothing rather than erroring out. Also, to avoid
16022 * unnecessary chatter while restoring those old dumps, say
16023 * nothing at all if the command would be a no-op anyway.
16024 */
16025 if (tuple_class->relowner != newOwnerId)
16027 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16028 errmsg("cannot change owner of index \"%s\"",
16029 NameStr(tuple_class->relname)),
16030 errhint("Change the ownership of the index's table instead.")));
16031 /* quick hack to exit via the no-op path */
16032 newOwnerId = tuple_class->relowner;
16033 }
16034 break;
16035 case RELKIND_PARTITIONED_INDEX:
16036 if (recursing)
16037 break;
16038 ereport(ERROR,
16039 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16040 errmsg("cannot change owner of index \"%s\"",
16041 NameStr(tuple_class->relname)),
16042 errhint("Change the ownership of the index's table instead.")));
16043 break;
16044 case RELKIND_SEQUENCE:
16045 if (!recursing &&
16046 tuple_class->relowner != newOwnerId)
16047 {
16048 /* if it's an owned sequence, disallow changing it by itself */
16049 Oid tableId;
16050 int32 colId;
16051
16052 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16053 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
16054 ereport(ERROR,
16055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16056 errmsg("cannot change owner of sequence \"%s\"",
16057 NameStr(tuple_class->relname)),
16058 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16059 NameStr(tuple_class->relname),
16060 get_rel_name(tableId))));
16061 }
16062 break;
16063 case RELKIND_COMPOSITE_TYPE:
16064 if (recursing)
16065 break;
16066 ereport(ERROR,
16067 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16068 errmsg("\"%s\" is a composite type",
16069 NameStr(tuple_class->relname)),
16070 /* translator: %s is an SQL ALTER command */
16071 errhint("Use %s instead.",
16072 "ALTER TYPE")));
16073 break;
16074 case RELKIND_TOASTVALUE:
16075 if (recursing)
16076 break;
16077 /* FALL THRU */
16078 default:
16079 ereport(ERROR,
16080 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16081 errmsg("cannot change owner of relation \"%s\"",
16082 NameStr(tuple_class->relname)),
16083 errdetail_relkind_not_supported(tuple_class->relkind)));
16084 }
16085
16086 /*
16087 * If the new owner is the same as the existing owner, consider the
16088 * command to have succeeded. This is for dump restoration purposes.
16089 */
16090 if (tuple_class->relowner != newOwnerId)
16091 {
16092 Datum repl_val[Natts_pg_class];
16093 bool repl_null[Natts_pg_class];
16094 bool repl_repl[Natts_pg_class];
16095 Acl *newAcl;
16096 Datum aclDatum;
16097 bool isNull;
16098 HeapTuple newtuple;
16099
16100 /* skip permission checks when recursing to index or toast table */
16101 if (!recursing)
16102 {
16103 /* Superusers can always do it */
16104 if (!superuser())
16105 {
16106 Oid namespaceOid = tuple_class->relnamespace;
16107 AclResult aclresult;
16108
16109 /* Otherwise, must be owner of the existing object */
16110 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16112 RelationGetRelationName(target_rel));
16113
16114 /* Must be able to become new owner */
16115 check_can_set_role(GetUserId(), newOwnerId);
16116
16117 /* New owner must have CREATE privilege on namespace */
16118 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
16119 ACL_CREATE);
16120 if (aclresult != ACLCHECK_OK)
16121 aclcheck_error(aclresult, OBJECT_SCHEMA,
16122 get_namespace_name(namespaceOid));
16123 }
16124 }
16125
16126 memset(repl_null, false, sizeof(repl_null));
16127 memset(repl_repl, false, sizeof(repl_repl));
16128
16129 repl_repl[Anum_pg_class_relowner - 1] = true;
16130 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
16131
16132 /*
16133 * Determine the modified ACL for the new owner. This is only
16134 * necessary when the ACL is non-null.
16135 */
16136 aclDatum = SysCacheGetAttr(RELOID, tuple,
16137 Anum_pg_class_relacl,
16138 &isNull);
16139 if (!isNull)
16140 {
16141 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16142 tuple_class->relowner, newOwnerId);
16143 repl_repl[Anum_pg_class_relacl - 1] = true;
16144 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
16145 }
16146
16147 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
16148
16149 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16150
16151 heap_freetuple(newtuple);
16152
16153 /*
16154 * We must similarly update any per-column ACLs to reflect the new
16155 * owner; for neatness reasons that's split out as a subroutine.
16156 */
16157 change_owner_fix_column_acls(relationOid,
16158 tuple_class->relowner,
16159 newOwnerId);
16160
16161 /*
16162 * Update owner dependency reference, if any. A composite type has
16163 * none, because it's tracked for the pg_type entry instead of here;
16164 * indexes and TOAST tables don't have their own entries either.
16165 */
16166 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16167 tuple_class->relkind != RELKIND_INDEX &&
16168 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
16169 tuple_class->relkind != RELKIND_TOASTVALUE)
16170 changeDependencyOnOwner(RelationRelationId, relationOid,
16171 newOwnerId);
16172
16173 /*
16174 * Also change the ownership of the table's row type, if it has one
16175 */
16176 if (OidIsValid(tuple_class->reltype))
16177 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
16178
16179 /*
16180 * If we are operating on a table or materialized view, also change
16181 * the ownership of any indexes and sequences that belong to the
16182 * relation, as well as its toast table (if it has one).
16183 */
16184 if (tuple_class->relkind == RELKIND_RELATION ||
16185 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
16186 tuple_class->relkind == RELKIND_MATVIEW ||
16187 tuple_class->relkind == RELKIND_TOASTVALUE)
16188 {
16189 List *index_oid_list;
16190 ListCell *i;
16191
16192 /* Find all the indexes belonging to this relation */
16193 index_oid_list = RelationGetIndexList(target_rel);
16194
16195 /* For each index, recursively change its ownership */
16196 foreach(i, index_oid_list)
16197 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16198
16199 list_free(index_oid_list);
16200 }
16201
16202 /* If it has a toast table, recurse to change its ownership */
16203 if (tuple_class->reltoastrelid != InvalidOid)
16204 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
16205 true, lockmode);
16206
16207 /* If it has dependent sequences, recurse to change them too */
16208 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16209 }
16210
16211 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
16212
16213 ReleaseSysCache(tuple);
16214 table_close(class_rel, RowExclusiveLock);
16215 relation_close(target_rel, NoLock);
16216}
16217
16218/*
16219 * change_owner_fix_column_acls
16220 *
16221 * Helper function for ATExecChangeOwner. Scan the columns of the table
16222 * and fix any non-null column ACLs to reflect the new owner.
16223 */
16224static void
16225change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
16226{
16227 Relation attRelation;
16228 SysScanDesc scan;
16229 ScanKeyData key[1];
16230 HeapTuple attributeTuple;
16231
16232 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
16233 ScanKeyInit(&key[0],
16234 Anum_pg_attribute_attrelid,
16235 BTEqualStrategyNumber, F_OIDEQ,
16236 ObjectIdGetDatum(relationOid));
16237 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
16238 true, NULL, 1, key);
16239 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16240 {
16241 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16242 Datum repl_val[Natts_pg_attribute];
16243 bool repl_null[Natts_pg_attribute];
16244 bool repl_repl[Natts_pg_attribute];
16245 Acl *newAcl;
16246 Datum aclDatum;
16247 bool isNull;
16248 HeapTuple newtuple;
16249
16250 /* Ignore dropped columns */
16251 if (att->attisdropped)
16252 continue;
16253
16254 aclDatum = heap_getattr(attributeTuple,
16255 Anum_pg_attribute_attacl,
16256 RelationGetDescr(attRelation),
16257 &isNull);
16258 /* Null ACLs do not require changes */
16259 if (isNull)
16260 continue;
16261
16262 memset(repl_null, false, sizeof(repl_null));
16263 memset(repl_repl, false, sizeof(repl_repl));
16264
16265 newAcl = aclnewowner(DatumGetAclP(aclDatum),
16266 oldOwnerId, newOwnerId);
16267 repl_repl[Anum_pg_attribute_attacl - 1] = true;
16268 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
16269
16270 newtuple = heap_modify_tuple(attributeTuple,
16271 RelationGetDescr(attRelation),
16272 repl_val, repl_null, repl_repl);
16273
16274 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16275
16276 heap_freetuple(newtuple);
16277 }
16278 systable_endscan(scan);
16279 table_close(attRelation, RowExclusiveLock);
16280}
16281
16282/*
16283 * change_owner_recurse_to_sequences
16284 *
16285 * Helper function for ATExecChangeOwner. Examines pg_depend searching
16286 * for sequences that are dependent on serial columns, and changes their
16287 * ownership.
16288 */
16289static void
16290change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
16291{
16292 Relation depRel;
16293 SysScanDesc scan;
16294 ScanKeyData key[2];
16295 HeapTuple tup;
16296
16297 /*
16298 * SERIAL sequences are those having an auto dependency on one of the
16299 * table's columns (we don't care *which* column, exactly).
16300 */
16301 depRel = table_open(DependRelationId, AccessShareLock);
16302
16303 ScanKeyInit(&key[0],
16304 Anum_pg_depend_refclassid,
16305 BTEqualStrategyNumber, F_OIDEQ,
16306 ObjectIdGetDatum(RelationRelationId));
16307 ScanKeyInit(&key[1],
16308 Anum_pg_depend_refobjid,
16309 BTEqualStrategyNumber, F_OIDEQ,
16310 ObjectIdGetDatum(relationOid));
16311 /* we leave refobjsubid unspecified */
16312
16313 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16314 NULL, 2, key);
16315
16316 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16317 {
16318 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16319 Relation seqRel;
16320
16321 /* skip dependencies other than auto dependencies on columns */
16322 if (depForm->refobjsubid == 0 ||
16323 depForm->classid != RelationRelationId ||
16324 depForm->objsubid != 0 ||
16325 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16326 continue;
16327
16328 /* Use relation_open just in case it's an index */
16329 seqRel = relation_open(depForm->objid, lockmode);
16330
16331 /* skip non-sequence relations */
16332 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16333 {
16334 /* No need to keep the lock */
16335 relation_close(seqRel, lockmode);
16336 continue;
16337 }
16338
16339 /* We don't need to close the sequence while we alter it. */
16340 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16341
16342 /* Now we can close it. Keep the lock till end of transaction. */
16343 relation_close(seqRel, NoLock);
16344 }
16345
16346 systable_endscan(scan);
16347
16349}
16350
16351/*
16352 * ALTER TABLE CLUSTER ON
16353 *
16354 * The only thing we have to do is to change the indisclustered bits.
16355 *
16356 * Return the address of the new clustering index.
16357 */
16358static ObjectAddress
16359ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
16360{
16361 Oid indexOid;
16362 ObjectAddress address;
16363
16364 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16365
16366 if (!OidIsValid(indexOid))
16367 ereport(ERROR,
16368 (errcode(ERRCODE_UNDEFINED_OBJECT),
16369 errmsg("index \"%s\" for table \"%s\" does not exist",
16370 indexName, RelationGetRelationName(rel))));
16371
16372 /* Check index is valid to cluster on */
16373 check_index_is_clusterable(rel, indexOid, lockmode);
16374
16375 /* And do the work */
16376 mark_index_clustered(rel, indexOid, false);
16377
16378 ObjectAddressSet(address,
16379 RelationRelationId, indexOid);
16380
16381 return address;
16382}
16383
16384/*
16385 * ALTER TABLE SET WITHOUT CLUSTER
16386 *
16387 * We have to find any indexes on the table that have indisclustered bit
16388 * set and turn it off.
16389 */
16390static void
16392{
16393 mark_index_clustered(rel, InvalidOid, false);
16394}
16395
16396/*
16397 * Preparation phase for SET ACCESS METHOD
16398 *
16399 * Check that the access method exists and determine whether a change is
16400 * actually needed.
16401 */
16402static void
16404{
16405 Oid amoid;
16406
16407 /*
16408 * Look up the access method name and check that it differs from the
16409 * table's current AM. If DEFAULT was specified for a partitioned table
16410 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16411 */
16412 if (amname != NULL)
16413 amoid = get_table_am_oid(amname, false);
16414 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16415 amoid = InvalidOid;
16416 else
16418
16419 /* if it's a match, phase 3 doesn't need to do anything */
16420 if (rel->rd_rel->relam == amoid)
16421 return;
16422
16423 /* Save info for Phase 3 to do the real work */
16425 tab->newAccessMethod = amoid;
16426 tab->chgAccessMethod = true;
16427}
16428
16429/*
16430 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
16431 * storage that have an interest in preserving AM.
16432 *
16433 * Since these have no storage, setting the access method is a catalog only
16434 * operation.
16435 */
16436static void
16438{
16439 Relation pg_class;
16440 Oid oldAccessMethodId;
16441 HeapTuple tuple;
16442 Form_pg_class rd_rel;
16443 Oid reloid = RelationGetRelid(rel);
16444
16445 /*
16446 * Shouldn't be called on relations having storage; these are processed in
16447 * phase 3.
16448 */
16449 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16450
16451 /* Get a modifiable copy of the relation's pg_class row. */
16452 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16453
16454 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
16455 if (!HeapTupleIsValid(tuple))
16456 elog(ERROR, "cache lookup failed for relation %u", reloid);
16457 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16458
16459 /* Update the pg_class row. */
16460 oldAccessMethodId = rd_rel->relam;
16461 rd_rel->relam = newAccessMethodId;
16462
16463 /* Leave if no update required */
16464 if (rd_rel->relam == oldAccessMethodId)
16465 {
16466 heap_freetuple(tuple);
16467 table_close(pg_class, RowExclusiveLock);
16468 return;
16469 }
16470
16471 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16472
16473 /*
16474 * Update the dependency on the new access method. No dependency is added
16475 * if the new access method is InvalidOid (default case). Be very careful
16476 * that this has to compare the previous value stored in pg_class with the
16477 * new one.
16478 */
16479 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16480 {
16481 ObjectAddress relobj,
16482 referenced;
16483
16484 /*
16485 * New access method is defined and there was no dependency
16486 * previously, so record a new one.
16487 */
16488 ObjectAddressSet(relobj, RelationRelationId, reloid);
16489 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
16490 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
16491 }
16492 else if (OidIsValid(oldAccessMethodId) &&
16493 !OidIsValid(rd_rel->relam))
16494 {
16495 /*
16496 * There was an access method defined, and no new one, so just remove
16497 * the existing dependency.
16498 */
16499 deleteDependencyRecordsForClass(RelationRelationId, reloid,
16500 AccessMethodRelationId,
16502 }
16503 else
16504 {
16505 Assert(OidIsValid(oldAccessMethodId) &&
16506 OidIsValid(rd_rel->relam));
16507
16508 /* Both are valid, so update the dependency */
16509 changeDependencyFor(RelationRelationId, reloid,
16510 AccessMethodRelationId,
16511 oldAccessMethodId, rd_rel->relam);
16512 }
16513
16514 /* make the relam and dependency changes visible */
16516
16517 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16518
16519 heap_freetuple(tuple);
16520 table_close(pg_class, RowExclusiveLock);
16521}
16522
16523/*
16524 * ALTER TABLE SET TABLESPACE
16525 */
16526static void
16527ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
16528{
16529 Oid tablespaceId;
16530
16531 /* Check that the tablespace exists */
16532 tablespaceId = get_tablespace_oid(tablespacename, false);
16533
16534 /* Check permissions except when moving to database's default */
16535 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
16536 {
16537 AclResult aclresult;
16538
16539 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
16540 if (aclresult != ACLCHECK_OK)
16541 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16542 }
16543
16544 /* Save info for Phase 3 to do the real work */
16545 if (OidIsValid(tab->newTableSpace))
16546 ereport(ERROR,
16547 (errcode(ERRCODE_SYNTAX_ERROR),
16548 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16549
16550 tab->newTableSpace = tablespaceId;
16551}
16552
16553/*
16554 * Set, reset, or replace reloptions.
16555 */
16556static void
16558 LOCKMODE lockmode)
16559{
16560 Oid relid;
16561 Relation pgclass;
16562 HeapTuple tuple;
16563 HeapTuple newtuple;
16564 Datum datum;
16565 Datum newOptions;
16566 Datum repl_val[Natts_pg_class];
16567 bool repl_null[Natts_pg_class];
16568 bool repl_repl[Natts_pg_class];
16569 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16570
16571 if (defList == NIL && operation != AT_ReplaceRelOptions)
16572 return; /* nothing to do */
16573
16574 pgclass = table_open(RelationRelationId, RowExclusiveLock);
16575
16576 /* Fetch heap tuple */
16577 relid = RelationGetRelid(rel);
16578 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
16579 if (!HeapTupleIsValid(tuple))
16580 elog(ERROR, "cache lookup failed for relation %u", relid);
16581
16582 if (operation == AT_ReplaceRelOptions)
16583 {
16584 /*
16585 * If we're supposed to replace the reloptions list, we just pretend
16586 * there were none before.
16587 */
16588 datum = (Datum) 0;
16589 }
16590 else
16591 {
16592 bool isnull;
16593
16594 /* Get the old reloptions */
16595 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16596 &isnull);
16597 if (isnull)
16598 datum = (Datum) 0;
16599 }
16600
16601 /* Generate new proposed reloptions (text array) */
16602 newOptions = transformRelOptions(datum, defList, NULL, validnsps, false,
16603 operation == AT_ResetRelOptions);
16604
16605 /* Validate */
16606 switch (rel->rd_rel->relkind)
16607 {
16608 case RELKIND_RELATION:
16609 case RELKIND_MATVIEW:
16610 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16611 break;
16612 case RELKIND_PARTITIONED_TABLE:
16613 (void) partitioned_table_reloptions(newOptions, true);
16614 break;
16615 case RELKIND_VIEW:
16616 (void) view_reloptions(newOptions, true);
16617 break;
16618 case RELKIND_INDEX:
16619 case RELKIND_PARTITIONED_INDEX:
16620 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
16621 break;
16622 case RELKIND_TOASTVALUE:
16623 /* fall through to error -- shouldn't ever get here */
16624 default:
16625 ereport(ERROR,
16626 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16627 errmsg("cannot set options for relation \"%s\"",
16629 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16630 break;
16631 }
16632
16633 /* Special-case validation of view options */
16634 if (rel->rd_rel->relkind == RELKIND_VIEW)
16635 {
16636 Query *view_query = get_view_query(rel);
16637 List *view_options = untransformRelOptions(newOptions);
16638 ListCell *cell;
16639 bool check_option = false;
16640
16641 foreach(cell, view_options)
16642 {
16643 DefElem *defel = (DefElem *) lfirst(cell);
16644
16645 if (strcmp(defel->defname, "check_option") == 0)
16646 check_option = true;
16647 }
16648
16649 /*
16650 * If the check option is specified, look to see if the view is
16651 * actually auto-updatable or not.
16652 */
16653 if (check_option)
16654 {
16655 const char *view_updatable_error =
16656 view_query_is_auto_updatable(view_query, true);
16657
16658 if (view_updatable_error)
16659 ereport(ERROR,
16660 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16661 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16662 errhint("%s", _(view_updatable_error))));
16663 }
16664 }
16665
16666 /*
16667 * All we need do here is update the pg_class row; the new options will be
16668 * propagated into relcaches during post-commit cache inval.
16669 */
16670 memset(repl_val, 0, sizeof(repl_val));
16671 memset(repl_null, false, sizeof(repl_null));
16672 memset(repl_repl, false, sizeof(repl_repl));
16673
16674 if (newOptions != (Datum) 0)
16675 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16676 else
16677 repl_null[Anum_pg_class_reloptions - 1] = true;
16678
16679 repl_repl[Anum_pg_class_reloptions - 1] = true;
16680
16681 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16682 repl_val, repl_null, repl_repl);
16683
16684 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16685 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
16686
16687 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16688
16689 heap_freetuple(newtuple);
16690
16691 ReleaseSysCache(tuple);
16692
16693 /* repeat the whole exercise for the toast table, if there's one */
16694 if (OidIsValid(rel->rd_rel->reltoastrelid))
16695 {
16696 Relation toastrel;
16697 Oid toastid = rel->rd_rel->reltoastrelid;
16698
16699 toastrel = table_open(toastid, lockmode);
16700
16701 /* Fetch heap tuple */
16702 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
16703 if (!HeapTupleIsValid(tuple))
16704 elog(ERROR, "cache lookup failed for relation %u", toastid);
16705
16706 if (operation == AT_ReplaceRelOptions)
16707 {
16708 /*
16709 * If we're supposed to replace the reloptions list, we just
16710 * pretend there were none before.
16711 */
16712 datum = (Datum) 0;
16713 }
16714 else
16715 {
16716 bool isnull;
16717
16718 /* Get the old reloptions */
16719 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
16720 &isnull);
16721 if (isnull)
16722 datum = (Datum) 0;
16723 }
16724
16725 newOptions = transformRelOptions(datum, defList, "toast", validnsps,
16726 false, operation == AT_ResetRelOptions);
16727
16728 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
16729
16730 memset(repl_val, 0, sizeof(repl_val));
16731 memset(repl_null, false, sizeof(repl_null));
16732 memset(repl_repl, false, sizeof(repl_repl));
16733
16734 if (newOptions != (Datum) 0)
16735 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
16736 else
16737 repl_null[Anum_pg_class_reloptions - 1] = true;
16738
16739 repl_repl[Anum_pg_class_reloptions - 1] = true;
16740
16741 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16742 repl_val, repl_null, repl_repl);
16743
16744 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16745
16746 InvokeObjectPostAlterHookArg(RelationRelationId,
16747 RelationGetRelid(toastrel), 0,
16748 InvalidOid, true);
16749
16750 heap_freetuple(newtuple);
16751
16752 ReleaseSysCache(tuple);
16753
16754 table_close(toastrel, NoLock);
16755 }
16756
16757 table_close(pgclass, RowExclusiveLock);
16758}
16759
16760/*
16761 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
16762 * rewriting to be done, so we just want to copy the data as fast as possible.
16763 */
16764static void
16765ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
16766{
16767 Relation rel;
16768 Oid reltoastrelid;
16769 RelFileNumber newrelfilenumber;
16770 RelFileLocator newrlocator;
16771 List *reltoastidxids = NIL;
16772 ListCell *lc;
16773
16774 /*
16775 * Need lock here in case we are recursing to toast table or index
16776 */
16777 rel = relation_open(tableOid, lockmode);
16778
16779 /* Check first if relation can be moved to new tablespace */
16780 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16781 {
16782 InvokeObjectPostAlterHook(RelationRelationId,
16783 RelationGetRelid(rel), 0);
16784 relation_close(rel, NoLock);
16785 return;
16786 }
16787
16788 reltoastrelid = rel->rd_rel->reltoastrelid;
16789 /* Fetch the list of indexes on toast relation if necessary */
16790 if (OidIsValid(reltoastrelid))
16791 {
16792 Relation toastRel = relation_open(reltoastrelid, lockmode);
16793
16794 reltoastidxids = RelationGetIndexList(toastRel);
16795 relation_close(toastRel, lockmode);
16796 }
16797
16798 /*
16799 * Relfilenumbers are not unique in databases across tablespaces, so we
16800 * need to allocate a new one in the new tablespace.
16801 */
16802 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
16803 rel->rd_rel->relpersistence);
16804
16805 /* Open old and new relation */
16806 newrlocator = rel->rd_locator;
16807 newrlocator.relNumber = newrelfilenumber;
16808 newrlocator.spcOid = newTableSpace;
16809
16810 /* hand off to AM to actually create new rel storage and copy the data */
16811 if (rel->rd_rel->relkind == RELKIND_INDEX)
16812 {
16813 index_copy_data(rel, newrlocator);
16814 }
16815 else
16816 {
16817 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
16818 table_relation_copy_data(rel, &newrlocator);
16819 }
16820
16821 /*
16822 * Update the pg_class row.
16823 *
16824 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
16825 * executed on pg_class or its indexes (the above copy wouldn't contain
16826 * the updated pg_class entry), but that's forbidden with
16827 * CheckRelationTableSpaceMove().
16828 */
16829 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
16830
16831 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16832
16834
16835 relation_close(rel, NoLock);
16836
16837 /* Make sure the reltablespace change is visible */
16839
16840 /* Move associated toast relation and/or indexes, too */
16841 if (OidIsValid(reltoastrelid))
16842 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
16843 foreach(lc, reltoastidxids)
16844 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
16845
16846 /* Clean up */
16847 list_free(reltoastidxids);
16848}
16849
16850/*
16851 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
16852 * storage that have an interest in preserving tablespace.
16853 *
16854 * Since these have no storage the tablespace can be updated with a simple
16855 * metadata only operation to update the tablespace.
16856 */
16857static void
16859{
16860 /*
16861 * Shouldn't be called on relations having storage; these are processed in
16862 * phase 3.
16863 */
16864 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16865
16866 /* check if relation can be moved to its new tablespace */
16867 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
16868 {
16869 InvokeObjectPostAlterHook(RelationRelationId,
16870 RelationGetRelid(rel),
16871 0);
16872 return;
16873 }
16874
16875 /* Update can be done, so change reltablespace */
16876 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
16877
16878 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
16879
16880 /* Make sure the reltablespace change is visible */
16882}
16883
16884/*
16885 * Alter Table ALL ... SET TABLESPACE
16886 *
16887 * Allows a user to move all objects of some type in a given tablespace in the
16888 * current database to another tablespace. Objects can be chosen based on the
16889 * owner of the object also, to allow users to move only their objects.
16890 * The user must have CREATE rights on the new tablespace, as usual. The main
16891 * permissions handling is done by the lower-level table move function.
16892 *
16893 * All to-be-moved objects are locked first. If NOWAIT is specified and the
16894 * lock can't be acquired then we ereport(ERROR).
16895 */
16896Oid
16898{
16899 List *relations = NIL;
16900 ListCell *l;
16901 ScanKeyData key[1];
16902 Relation rel;
16903 TableScanDesc scan;
16904 HeapTuple tuple;
16905 Oid orig_tablespaceoid;
16906 Oid new_tablespaceoid;
16907 List *role_oids = roleSpecsToIds(stmt->roles);
16908
16909 /* Ensure we were not asked to move something we can't */
16910 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
16911 stmt->objtype != OBJECT_MATVIEW)
16912 ereport(ERROR,
16913 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16914 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
16915
16916 /* Get the orig and new tablespace OIDs */
16917 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
16918 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
16919
16920 /* Can't move shared relations in to or out of pg_global */
16921 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
16922 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
16923 new_tablespaceoid == GLOBALTABLESPACE_OID)
16924 ereport(ERROR,
16925 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
16926 errmsg("cannot move relations in to or out of pg_global tablespace")));
16927
16928 /*
16929 * Must have CREATE rights on the new tablespace, unless it is the
16930 * database default tablespace (which all users implicitly have CREATE
16931 * rights on).
16932 */
16933 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
16934 {
16935 AclResult aclresult;
16936
16937 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
16938 ACL_CREATE);
16939 if (aclresult != ACLCHECK_OK)
16941 get_tablespace_name(new_tablespaceoid));
16942 }
16943
16944 /*
16945 * Now that the checks are done, check if we should set either to
16946 * InvalidOid because it is our database's default tablespace.
16947 */
16948 if (orig_tablespaceoid == MyDatabaseTableSpace)
16949 orig_tablespaceoid = InvalidOid;
16950
16951 if (new_tablespaceoid == MyDatabaseTableSpace)
16952 new_tablespaceoid = InvalidOid;
16953
16954 /* no-op */
16955 if (orig_tablespaceoid == new_tablespaceoid)
16956 return new_tablespaceoid;
16957
16958 /*
16959 * Walk the list of objects in the tablespace and move them. This will
16960 * only find objects in our database, of course.
16961 */
16962 ScanKeyInit(&key[0],
16963 Anum_pg_class_reltablespace,
16964 BTEqualStrategyNumber, F_OIDEQ,
16965 ObjectIdGetDatum(orig_tablespaceoid));
16966
16967 rel = table_open(RelationRelationId, AccessShareLock);
16968 scan = table_beginscan_catalog(rel, 1, key);
16969 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16970 {
16971 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16972 Oid relOid = relForm->oid;
16973
16974 /*
16975 * Do not move objects in pg_catalog as part of this, if an admin
16976 * really wishes to do so, they can issue the individual ALTER
16977 * commands directly.
16978 *
16979 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16980 * (TOAST will be moved with the main table).
16981 */
16982 if (IsCatalogNamespace(relForm->relnamespace) ||
16983 relForm->relisshared ||
16984 isAnyTempNamespace(relForm->relnamespace) ||
16985 IsToastNamespace(relForm->relnamespace))
16986 continue;
16987
16988 /* Only move the object type requested */
16989 if ((stmt->objtype == OBJECT_TABLE &&
16990 relForm->relkind != RELKIND_RELATION &&
16991 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16992 (stmt->objtype == OBJECT_INDEX &&
16993 relForm->relkind != RELKIND_INDEX &&
16994 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16995 (stmt->objtype == OBJECT_MATVIEW &&
16996 relForm->relkind != RELKIND_MATVIEW))
16997 continue;
16998
16999 /* Check if we are only moving objects owned by certain roles */
17000 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17001 continue;
17002
17003 /*
17004 * Handle permissions-checking here since we are locking the tables
17005 * and also to avoid doing a bunch of work only to fail part-way. Note
17006 * that permissions will also be checked by AlterTableInternal().
17007 *
17008 * Caller must be considered an owner on the table to move it.
17009 */
17010 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
17012 NameStr(relForm->relname));
17013
17014 if (stmt->nowait &&
17016 ereport(ERROR,
17017 (errcode(ERRCODE_OBJECT_IN_USE),
17018 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17019 get_namespace_name(relForm->relnamespace),
17020 NameStr(relForm->relname))));
17021 else
17023
17024 /* Add to our list of objects to move */
17025 relations = lappend_oid(relations, relOid);
17026 }
17027
17028 table_endscan(scan);
17030
17031 if (relations == NIL)
17033 (errcode(ERRCODE_NO_DATA_FOUND),
17034 errmsg("no matching relations in tablespace \"%s\" found",
17035 orig_tablespaceoid == InvalidOid ? "(database default)" :
17036 get_tablespace_name(orig_tablespaceoid))));
17037
17038 /* Everything is locked, loop through and move all of the relations. */
17039 foreach(l, relations)
17040 {
17041 List *cmds = NIL;
17043
17045 cmd->name = stmt->new_tablespacename;
17046
17047 cmds = lappend(cmds, cmd);
17048
17050 /* OID is set by AlterTableInternal */
17051 AlterTableInternal(lfirst_oid(l), cmds, false);
17053 }
17054
17055 return new_tablespaceoid;
17056}
17057
17058static void
17060{
17061 SMgrRelation dstrel;
17062
17063 /*
17064 * Since we copy the file directly without looking at the shared buffers,
17065 * we'd better first flush out any pages of the source relation that are
17066 * in shared buffers. We assume no new changes will be made while we are
17067 * holding exclusive lock on the rel.
17068 */
17070
17071 /*
17072 * Create and copy all forks of the relation, and schedule unlinking of
17073 * old physical files.
17074 *
17075 * NOTE: any conflict in relfilenumber value will be caught in
17076 * RelationCreateStorage().
17077 */
17078 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17079
17080 /* copy main fork */
17082 rel->rd_rel->relpersistence);
17083
17084 /* copy those extra forks that exist */
17085 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17086 forkNum <= MAX_FORKNUM; forkNum++)
17087 {
17088 if (smgrexists(RelationGetSmgr(rel), forkNum))
17089 {
17090 smgrcreate(dstrel, forkNum, false);
17091
17092 /*
17093 * WAL log creation if the relation is persistent, or this is the
17094 * init fork of an unlogged relation.
17095 */
17096 if (RelationIsPermanent(rel) ||
17097 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17098 forkNum == INIT_FORKNUM))
17099 log_smgrcreate(&newrlocator, forkNum);
17100 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
17101 rel->rd_rel->relpersistence);
17102 }
17103 }
17104
17105 /* drop old relation, and close new one */
17107 smgrclose(dstrel);
17108}
17109
17110/*
17111 * ALTER TABLE ENABLE/DISABLE TRIGGER
17112 *
17113 * We just pass this off to trigger.c.
17114 */
17115static void
17116ATExecEnableDisableTrigger(Relation rel, const char *trigname,
17117 char fires_when, bool skip_system, bool recurse,
17118 LOCKMODE lockmode)
17119{
17120 EnableDisableTrigger(rel, trigname, InvalidOid,
17121 fires_when, skip_system, recurse,
17122 lockmode);
17123
17124 InvokeObjectPostAlterHook(RelationRelationId,
17125 RelationGetRelid(rel), 0);
17126}
17127
17128/*
17129 * ALTER TABLE ENABLE/DISABLE RULE
17130 *
17131 * We just pass this off to rewriteDefine.c.
17132 */
17133static void
17134ATExecEnableDisableRule(Relation rel, const char *rulename,
17135 char fires_when, LOCKMODE lockmode)
17136{
17137 EnableDisableRule(rel, rulename, fires_when);
17138
17139 InvokeObjectPostAlterHook(RelationRelationId,
17140 RelationGetRelid(rel), 0);
17141}
17142
17143/*
17144 * ALTER TABLE INHERIT
17145 *
17146 * Add a parent to the child's parents. This verifies that all the columns and
17147 * check constraints of the parent appear in the child and that they have the
17148 * same data types and expressions.
17149 */
17150static void
17152{
17153 if (child_rel->rd_rel->reloftype)
17154 ereport(ERROR,
17155 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17156 errmsg("cannot change inheritance of typed table")));
17157
17158 if (child_rel->rd_rel->relispartition)
17159 ereport(ERROR,
17160 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17161 errmsg("cannot change inheritance of a partition")));
17162
17163 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17164 ereport(ERROR,
17165 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17166 errmsg("cannot change inheritance of partitioned table")));
17167}
17168
17169/*
17170 * Return the address of the new parent relation.
17171 */
17172static ObjectAddress
17173ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
17174{
17175 Relation parent_rel;
17176 List *children;
17177 ObjectAddress address;
17178 const char *trigger_name;
17179
17180 /*
17181 * A self-exclusive lock is needed here. See the similar case in
17182 * MergeAttributes() for a full explanation.
17183 */
17184 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
17185
17186 /*
17187 * Must be owner of both parent and child -- child was checked by
17188 * ATSimplePermissions call in ATPrepCmd
17189 */
17192
17193 /* Permanent rels cannot inherit from temporary ones */
17194 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17195 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17196 ereport(ERROR,
17197 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17198 errmsg("cannot inherit from temporary relation \"%s\"",
17199 RelationGetRelationName(parent_rel))));
17200
17201 /* If parent rel is temp, it must belong to this session */
17202 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17203 !parent_rel->rd_islocaltemp)
17204 ereport(ERROR,
17205 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17206 errmsg("cannot inherit from temporary relation of another session")));
17207
17208 /* Ditto for the child */
17209 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17210 !child_rel->rd_islocaltemp)
17211 ereport(ERROR,
17212 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17213 errmsg("cannot inherit to temporary relation of another session")));
17214
17215 /* Prevent partitioned tables from becoming inheritance parents */
17216 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17217 ereport(ERROR,
17218 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17219 errmsg("cannot inherit from partitioned table \"%s\"",
17220 parent->relname)));
17221
17222 /* Likewise for partitions */
17223 if (parent_rel->rd_rel->relispartition)
17224 ereport(ERROR,
17225 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17226 errmsg("cannot inherit from a partition")));
17227
17228 /*
17229 * Prevent circularity by seeing if proposed parent inherits from child.
17230 * (In particular, this disallows making a rel inherit from itself.)
17231 *
17232 * This is not completely bulletproof because of race conditions: in
17233 * multi-level inheritance trees, someone else could concurrently be
17234 * making another inheritance link that closes the loop but does not join
17235 * either of the rels we have locked. Preventing that seems to require
17236 * exclusive locks on the entire inheritance tree, which is a cure worse
17237 * than the disease. find_all_inheritors() will cope with circularity
17238 * anyway, so don't sweat it too much.
17239 *
17240 * We use weakest lock we can on child's children, namely AccessShareLock.
17241 */
17242 children = find_all_inheritors(RelationGetRelid(child_rel),
17243 AccessShareLock, NULL);
17244
17245 if (list_member_oid(children, RelationGetRelid(parent_rel)))
17246 ereport(ERROR,
17247 (errcode(ERRCODE_DUPLICATE_TABLE),
17248 errmsg("circular inheritance not allowed"),
17249 errdetail("\"%s\" is already a child of \"%s\".",
17250 parent->relname,
17251 RelationGetRelationName(child_rel))));
17252
17253 /*
17254 * If child_rel has row-level triggers with transition tables, we
17255 * currently don't allow it to become an inheritance child. See also
17256 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17257 */
17258 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
17259 if (trigger_name != NULL)
17260 ereport(ERROR,
17261 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17262 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17263 trigger_name, RelationGetRelationName(child_rel)),
17264 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17265
17266 /* OK to create inheritance */
17267 CreateInheritance(child_rel, parent_rel, false);
17268
17269 ObjectAddressSet(address, RelationRelationId,
17270 RelationGetRelid(parent_rel));
17271
17272 /* keep our lock on the parent relation until commit */
17273 table_close(parent_rel, NoLock);
17274
17275 return address;
17276}
17277
17278/*
17279 * CreateInheritance
17280 * Catalog manipulation portion of creating inheritance between a child
17281 * table and a parent table.
17282 *
17283 * Common to ATExecAddInherit() and ATExecAttachPartition().
17284 */
17285static void
17286CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
17287{
17288 Relation catalogRelation;
17289 SysScanDesc scan;
17291 HeapTuple inheritsTuple;
17292 int32 inhseqno;
17293
17294 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17295 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17296
17297 /*
17298 * Check for duplicates in the list of parents, and determine the highest
17299 * inhseqno already present; we'll use the next one for the new parent.
17300 * Also, if proposed child is a partition, it cannot already be
17301 * inheriting.
17302 *
17303 * Note: we do not reject the case where the child already inherits from
17304 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17305 */
17307 Anum_pg_inherits_inhrelid,
17308 BTEqualStrategyNumber, F_OIDEQ,
17310 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
17311 true, NULL, 1, &key);
17312
17313 /* inhseqno sequences start at 1 */
17314 inhseqno = 0;
17315 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17316 {
17317 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17318
17319 if (inh->inhparent == RelationGetRelid(parent_rel))
17320 ereport(ERROR,
17321 (errcode(ERRCODE_DUPLICATE_TABLE),
17322 errmsg("relation \"%s\" would be inherited from more than once",
17323 RelationGetRelationName(parent_rel))));
17324
17325 if (inh->inhseqno > inhseqno)
17326 inhseqno = inh->inhseqno;
17327 }
17328 systable_endscan(scan);
17329
17330 /* Match up the columns and bump attinhcount as needed */
17331 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
17332
17333 /* Match up the constraints and bump coninhcount as needed */
17334 MergeConstraintsIntoExisting(child_rel, parent_rel);
17335
17336 /*
17337 * OK, it looks valid. Make the catalog entries that show inheritance.
17338 */
17340 RelationGetRelid(parent_rel),
17341 inhseqno + 1,
17342 catalogRelation,
17343 parent_rel->rd_rel->relkind ==
17344 RELKIND_PARTITIONED_TABLE);
17345
17346 /* Now we're done with pg_inherits */
17347 table_close(catalogRelation, RowExclusiveLock);
17348}
17349
17350/*
17351 * Obtain the source-text form of the constraint expression for a check
17352 * constraint, given its pg_constraint tuple
17353 */
17354static char *
17356{
17358 bool isnull;
17359 Datum attr;
17360 Datum expr;
17361
17362 con = (Form_pg_constraint) GETSTRUCT(contup);
17363 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17364 if (isnull)
17365 elog(ERROR, "null conbin for constraint %u", con->oid);
17366
17367 expr = DirectFunctionCall2(pg_get_expr, attr,
17368 ObjectIdGetDatum(con->conrelid));
17369 return TextDatumGetCString(expr);
17370}
17371
17372/*
17373 * Determine whether two check constraints are functionally equivalent
17374 *
17375 * The test we apply is to see whether they reverse-compile to the same
17376 * source string. This insulates us from issues like whether attributes
17377 * have the same physical column numbers in parent and child relations.
17378 *
17379 * Note that we ignore enforceability as there are cases where constraints
17380 * with differing enforceability are allowed.
17381 */
17382static bool
17384{
17387
17388 if (acon->condeferrable != bcon->condeferrable ||
17389 acon->condeferred != bcon->condeferred ||
17390 strcmp(decompile_conbin(a, tupleDesc),
17391 decompile_conbin(b, tupleDesc)) != 0)
17392 return false;
17393 else
17394 return true;
17395}
17396
17397/*
17398 * Check columns in child table match up with columns in parent, and increment
17399 * their attinhcount.
17400 *
17401 * Called by CreateInheritance
17402 *
17403 * Currently all parent columns must be found in child. Missing columns are an
17404 * error. One day we might consider creating new columns like CREATE TABLE
17405 * does. However, that is widely unpopular --- in the common use case of
17406 * partitioned tables it's a foot-gun.
17407 *
17408 * The data type must match exactly. If the parent column is NOT NULL then
17409 * the child must be as well. Defaults are not compared, however.
17410 */
17411static void
17412MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
17413{
17414 Relation attrrel;
17415 TupleDesc parent_desc;
17416
17417 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
17418 parent_desc = RelationGetDescr(parent_rel);
17419
17420 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
17421 {
17422 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
17423 char *parent_attname = NameStr(parent_att->attname);
17424 HeapTuple tuple;
17425
17426 /* Ignore dropped columns in the parent. */
17427 if (parent_att->attisdropped)
17428 continue;
17429
17430 /* Find same column in child (matching on column name). */
17431 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
17432 if (HeapTupleIsValid(tuple))
17433 {
17434 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
17435
17436 if (parent_att->atttypid != child_att->atttypid ||
17437 parent_att->atttypmod != child_att->atttypmod)
17438 ereport(ERROR,
17439 (errcode(ERRCODE_DATATYPE_MISMATCH),
17440 errmsg("child table \"%s\" has different type for column \"%s\"",
17441 RelationGetRelationName(child_rel), parent_attname)));
17442
17443 if (parent_att->attcollation != child_att->attcollation)
17444 ereport(ERROR,
17445 (errcode(ERRCODE_COLLATION_MISMATCH),
17446 errmsg("child table \"%s\" has different collation for column \"%s\"",
17447 RelationGetRelationName(child_rel), parent_attname)));
17448
17449 /*
17450 * If the parent has a not-null constraint that's not NO INHERIT,
17451 * make sure the child has one too.
17452 *
17453 * Other constraints are checked elsewhere.
17454 */
17455 if (parent_att->attnotnull && !child_att->attnotnull)
17456 {
17457 HeapTuple contup;
17458
17459 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
17460 parent_att->attnum);
17461 if (HeapTupleIsValid(contup) &&
17462 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
17463 ereport(ERROR,
17464 errcode(ERRCODE_DATATYPE_MISMATCH),
17465 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17466 parent_attname, RelationGetRelationName(child_rel)));
17467 }
17468
17469 /*
17470 * Child column must be generated if and only if parent column is.
17471 */
17472 if (parent_att->attgenerated && !child_att->attgenerated)
17473 ereport(ERROR,
17474 (errcode(ERRCODE_DATATYPE_MISMATCH),
17475 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17476 if (child_att->attgenerated && !parent_att->attgenerated)
17477 ereport(ERROR,
17478 (errcode(ERRCODE_DATATYPE_MISMATCH),
17479 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17480
17481 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17482 ereport(ERROR,
17483 (errcode(ERRCODE_DATATYPE_MISMATCH),
17484 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17485 errdetail("Parent column is %s, child column is %s.",
17486 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17487 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17488
17489 /*
17490 * Regular inheritance children are independent enough not to
17491 * inherit identity columns. But partitions are integral part of
17492 * a partitioned table and inherit identity column.
17493 */
17494 if (ispartition)
17495 child_att->attidentity = parent_att->attidentity;
17496
17497 /*
17498 * OK, bump the child column's inheritance count. (If we fail
17499 * later on, this change will just roll back.)
17500 */
17501 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17502 &child_att->attinhcount))
17503 ereport(ERROR,
17504 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17505 errmsg("too many inheritance parents"));
17506
17507 /*
17508 * In case of partitions, we must enforce that value of attislocal
17509 * is same in all partitions. (Note: there are only inherited
17510 * attributes in partitions)
17511 */
17512 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17513 {
17514 Assert(child_att->attinhcount == 1);
17515 child_att->attislocal = false;
17516 }
17517
17518 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17519 heap_freetuple(tuple);
17520 }
17521 else
17522 {
17523 ereport(ERROR,
17524 (errcode(ERRCODE_DATATYPE_MISMATCH),
17525 errmsg("child table is missing column \"%s\"", parent_attname)));
17526 }
17527 }
17528
17529 table_close(attrrel, RowExclusiveLock);
17530}
17531
17532/*
17533 * Check constraints in child table match up with constraints in parent,
17534 * and increment their coninhcount.
17535 *
17536 * Constraints that are marked ONLY in the parent are ignored.
17537 *
17538 * Called by CreateInheritance
17539 *
17540 * Currently all constraints in parent must be present in the child. One day we
17541 * may consider adding new constraints like CREATE TABLE does.
17542 *
17543 * XXX This is O(N^2) which may be an issue with tables with hundreds of
17544 * constraints. As long as tables have more like 10 constraints it shouldn't be
17545 * a problem though. Even 100 constraints ought not be the end of the world.
17546 *
17547 * XXX See MergeWithExistingConstraint too if you change this code.
17548 */
17549static void
17551{
17552 Relation constraintrel;
17553 SysScanDesc parent_scan;
17554 ScanKeyData parent_key;
17555 HeapTuple parent_tuple;
17556 Oid parent_relid = RelationGetRelid(parent_rel);
17557 AttrMap *attmap;
17558
17559 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
17560
17561 /* Outer loop scans through the parent's constraint definitions */
17562 ScanKeyInit(&parent_key,
17563 Anum_pg_constraint_conrelid,
17564 BTEqualStrategyNumber, F_OIDEQ,
17565 ObjectIdGetDatum(parent_relid));
17566 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17567 true, NULL, 1, &parent_key);
17568
17569 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17570 RelationGetDescr(child_rel),
17571 true);
17572
17573 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
17574 {
17575 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
17576 SysScanDesc child_scan;
17577 ScanKeyData child_key;
17578 HeapTuple child_tuple;
17579 AttrNumber parent_attno;
17580 bool found = false;
17581
17582 if (parent_con->contype != CONSTRAINT_CHECK &&
17583 parent_con->contype != CONSTRAINT_NOTNULL)
17584 continue;
17585
17586 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17587 if (parent_con->connoinherit)
17588 continue;
17589
17590 if (parent_con->contype == CONSTRAINT_NOTNULL)
17591 parent_attno = extractNotNullColumn(parent_tuple);
17592 else
17593 parent_attno = InvalidAttrNumber;
17594
17595 /* Search for a child constraint matching this one */
17596 ScanKeyInit(&child_key,
17597 Anum_pg_constraint_conrelid,
17598 BTEqualStrategyNumber, F_OIDEQ,
17600 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
17601 true, NULL, 1, &child_key);
17602
17603 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
17604 {
17605 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
17606 HeapTuple child_copy;
17607
17608 if (child_con->contype != parent_con->contype)
17609 continue;
17610
17611 /*
17612 * CHECK constraint are matched by constraint name, NOT NULL ones
17613 * by attribute number.
17614 */
17615 if (child_con->contype == CONSTRAINT_CHECK)
17616 {
17617 if (strcmp(NameStr(parent_con->conname),
17618 NameStr(child_con->conname)) != 0)
17619 continue;
17620 }
17621 else if (child_con->contype == CONSTRAINT_NOTNULL)
17622 {
17623 Form_pg_attribute parent_attr;
17624 Form_pg_attribute child_attr;
17625 AttrNumber child_attno;
17626
17627 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
17628 child_attno = extractNotNullColumn(child_tuple);
17629 if (parent_attno != attmap->attnums[child_attno - 1])
17630 continue;
17631
17632 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
17633 /* there shouldn't be constraints on dropped columns */
17634 if (parent_attr->attisdropped || child_attr->attisdropped)
17635 elog(ERROR, "found not-null constraint on dropped columns");
17636 }
17637
17638 if (child_con->contype == CONSTRAINT_CHECK &&
17639 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
17640 ereport(ERROR,
17641 (errcode(ERRCODE_DATATYPE_MISMATCH),
17642 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17643 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
17644
17645 /*
17646 * If the child constraint is "no inherit" then cannot merge
17647 */
17648 if (child_con->connoinherit)
17649 ereport(ERROR,
17650 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17651 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17652 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17653
17654 /*
17655 * If the child constraint is "not valid" then cannot merge with a
17656 * valid parent constraint
17657 */
17658 if (parent_con->convalidated && child_con->conenforced &&
17659 !child_con->convalidated)
17660 ereport(ERROR,
17661 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17662 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17663 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17664
17665 /*
17666 * A NOT ENFORCED child constraint cannot be merged with an
17667 * ENFORCED parent constraint. However, the reverse is allowed,
17668 * where the child constraint is ENFORCED.
17669 */
17670 if (parent_con->conenforced && !child_con->conenforced)
17671 ereport(ERROR,
17672 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17673 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17674 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
17675
17676 /*
17677 * OK, bump the child constraint's inheritance count. (If we fail
17678 * later on, this change will just roll back.)
17679 */
17680 child_copy = heap_copytuple(child_tuple);
17681 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
17682
17683 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17684 &child_con->coninhcount))
17685 ereport(ERROR,
17686 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
17687 errmsg("too many inheritance parents"));
17688
17689 /*
17690 * In case of partitions, an inherited constraint must be
17691 * inherited only once since it cannot have multiple parents and
17692 * it is never considered local.
17693 */
17694 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17695 {
17696 Assert(child_con->coninhcount == 1);
17697 child_con->conislocal = false;
17698 }
17699
17700 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
17701 heap_freetuple(child_copy);
17702
17703 found = true;
17704 break;
17705 }
17706
17707 systable_endscan(child_scan);
17708
17709 if (!found)
17710 {
17711 if (parent_con->contype == CONSTRAINT_NOTNULL)
17712 ereport(ERROR,
17713 errcode(ERRCODE_DATATYPE_MISMATCH),
17714 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17715 get_attname(parent_relid,
17716 extractNotNullColumn(parent_tuple),
17717 false),
17718 RelationGetRelationName(child_rel)));
17719
17720 ereport(ERROR,
17721 (errcode(ERRCODE_DATATYPE_MISMATCH),
17722 errmsg("child table is missing constraint \"%s\"",
17723 NameStr(parent_con->conname))));
17724 }
17725 }
17726
17727 systable_endscan(parent_scan);
17728 table_close(constraintrel, RowExclusiveLock);
17729}
17730
17731/*
17732 * ALTER TABLE NO INHERIT
17733 *
17734 * Return value is the address of the relation that is no longer parent.
17735 */
17736static ObjectAddress
17738{
17739 ObjectAddress address;
17740 Relation parent_rel;
17741
17742 if (rel->rd_rel->relispartition)
17743 ereport(ERROR,
17744 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17745 errmsg("cannot change inheritance of a partition")));
17746
17747 /*
17748 * AccessShareLock on the parent is probably enough, seeing that DROP
17749 * TABLE doesn't lock parent tables at all. We need some lock since we'll
17750 * be inspecting the parent's schema.
17751 */
17752 parent_rel = table_openrv(parent, AccessShareLock);
17753
17754 /*
17755 * We don't bother to check ownership of the parent table --- ownership of
17756 * the child is presumed enough rights.
17757 */
17758
17759 /* Off to RemoveInheritance() where most of the work happens */
17760 RemoveInheritance(rel, parent_rel, false);
17761
17762 ObjectAddressSet(address, RelationRelationId,
17763 RelationGetRelid(parent_rel));
17764
17765 /* keep our lock on the parent relation until commit */
17766 table_close(parent_rel, NoLock);
17767
17768 return address;
17769}
17770
17771/*
17772 * MarkInheritDetached
17773 *
17774 * Set inhdetachpending for a partition, for ATExecDetachPartition
17775 * in concurrent mode. While at it, verify that no other partition is
17776 * already pending detach.
17777 */
17778static void
17780{
17781 Relation catalogRelation;
17782 SysScanDesc scan;
17784 HeapTuple inheritsTuple;
17785 bool found = false;
17786
17787 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17788
17789 /*
17790 * Find pg_inherits entries by inhparent. (We need to scan them all in
17791 * order to verify that no other partition is pending detach.)
17792 */
17793 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
17795 Anum_pg_inherits_inhparent,
17796 BTEqualStrategyNumber, F_OIDEQ,
17797 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17798 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
17799 true, NULL, 1, &key);
17800
17801 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
17802 {
17803 Form_pg_inherits inhForm;
17804
17805 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
17806 if (inhForm->inhdetachpending)
17807 ereport(ERROR,
17808 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17809 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17810 get_rel_name(inhForm->inhrelid),
17811 get_namespace_name(parent_rel->rd_rel->relnamespace),
17812 RelationGetRelationName(parent_rel)),
17813 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17814
17815 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17816 {
17817 HeapTuple newtup;
17818
17819 newtup = heap_copytuple(inheritsTuple);
17820 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
17821
17822 CatalogTupleUpdate(catalogRelation,
17823 &inheritsTuple->t_self,
17824 newtup);
17825 found = true;
17826 heap_freetuple(newtup);
17827 /* keep looking, to ensure we catch others pending detach */
17828 }
17829 }
17830
17831 /* Done */
17832 systable_endscan(scan);
17833 table_close(catalogRelation, RowExclusiveLock);
17834
17835 if (!found)
17836 ereport(ERROR,
17838 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17839 RelationGetRelationName(child_rel),
17840 RelationGetRelationName(parent_rel))));
17841}
17842
17843/*
17844 * RemoveInheritance
17845 *
17846 * Drop a parent from the child's parents. This just adjusts the attinhcount
17847 * and attislocal of the columns and removes the pg_inherit and pg_depend
17848 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
17849 *
17850 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
17851 * up attislocal stays true, which means if a child is ever removed from a
17852 * parent then its columns will never be automatically dropped which may
17853 * surprise. But at least we'll never surprise by dropping columns someone
17854 * isn't expecting to be dropped which would actually mean data loss.
17855 *
17856 * coninhcount and conislocal for inherited constraints are adjusted in
17857 * exactly the same way.
17858 *
17859 * Common to ATExecDropInherit() and ATExecDetachPartition().
17860 */
17861static void
17862RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
17863{
17864 Relation catalogRelation;
17865 SysScanDesc scan;
17866 ScanKeyData key[3];
17867 HeapTuple attributeTuple,
17868 constraintTuple;
17869 AttrMap *attmap;
17870 List *connames;
17871 List *nncolumns;
17872 bool found;
17873 bool is_partitioning;
17874
17875 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17876
17877 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
17878 RelationGetRelid(parent_rel),
17879 expect_detached,
17880 RelationGetRelationName(child_rel));
17881 if (!found)
17882 {
17883 if (is_partitioning)
17884 ereport(ERROR,
17886 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17887 RelationGetRelationName(child_rel),
17888 RelationGetRelationName(parent_rel))));
17889 else
17890 ereport(ERROR,
17892 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
17893 RelationGetRelationName(parent_rel),
17894 RelationGetRelationName(child_rel))));
17895 }
17896
17897 /*
17898 * Search through child columns looking for ones matching parent rel
17899 */
17900 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
17901 ScanKeyInit(&key[0],
17902 Anum_pg_attribute_attrelid,
17903 BTEqualStrategyNumber, F_OIDEQ,
17905 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
17906 true, NULL, 1, key);
17907 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
17908 {
17909 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
17910
17911 /* Ignore if dropped or not inherited */
17912 if (att->attisdropped)
17913 continue;
17914 if (att->attinhcount <= 0)
17915 continue;
17916
17918 NameStr(att->attname)))
17919 {
17920 /* Decrement inhcount and possibly set islocal to true */
17921 HeapTuple copyTuple = heap_copytuple(attributeTuple);
17922 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
17923
17924 copy_att->attinhcount--;
17925 if (copy_att->attinhcount == 0)
17926 copy_att->attislocal = true;
17927
17928 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17929 heap_freetuple(copyTuple);
17930 }
17931 }
17932 systable_endscan(scan);
17933 table_close(catalogRelation, RowExclusiveLock);
17934
17935 /*
17936 * Likewise, find inherited check and not-null constraints and disinherit
17937 * them. To do this, we first need a list of the names of the parent's
17938 * check constraints. (We cheat a bit by only checking for name matches,
17939 * assuming that the expressions will match.)
17940 *
17941 * For NOT NULL columns, we store column numbers to match, mapping them in
17942 * to the child rel's attribute numbers.
17943 */
17944 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
17945 RelationGetDescr(parent_rel),
17946 false);
17947
17948 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17949 ScanKeyInit(&key[0],
17950 Anum_pg_constraint_conrelid,
17951 BTEqualStrategyNumber, F_OIDEQ,
17952 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17953 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17954 true, NULL, 1, key);
17955
17956 connames = NIL;
17957 nncolumns = NIL;
17958
17959 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17960 {
17961 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17962
17963 if (con->connoinherit)
17964 continue;
17965
17966 if (con->contype == CONSTRAINT_CHECK)
17967 connames = lappend(connames, pstrdup(NameStr(con->conname)));
17968 if (con->contype == CONSTRAINT_NOTNULL)
17969 {
17970 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
17971
17972 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
17973 }
17974 }
17975
17976 systable_endscan(scan);
17977
17978 /* Now scan the child's constraints to find matches */
17979 ScanKeyInit(&key[0],
17980 Anum_pg_constraint_conrelid,
17981 BTEqualStrategyNumber, F_OIDEQ,
17983 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17984 true, NULL, 1, key);
17985
17986 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17987 {
17988 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17989 bool match = false;
17990
17991 /*
17992 * Match CHECK constraints by name, not-null constraints by column
17993 * number, and ignore all others.
17994 */
17995 if (con->contype == CONSTRAINT_CHECK)
17996 {
17997 foreach_ptr(char, chkname, connames)
17998 {
17999 if (con->contype == CONSTRAINT_CHECK &&
18000 strcmp(NameStr(con->conname), chkname) == 0)
18001 {
18002 match = true;
18003 connames = foreach_delete_current(connames, chkname);
18004 break;
18005 }
18006 }
18007 }
18008 else if (con->contype == CONSTRAINT_NOTNULL)
18009 {
18010 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
18011
18012 foreach_int(prevattno, nncolumns)
18013 {
18014 if (prevattno == child_attno)
18015 {
18016 match = true;
18017 nncolumns = foreach_delete_current(nncolumns, prevattno);
18018 break;
18019 }
18020 }
18021 }
18022 else
18023 continue;
18024
18025 if (match)
18026 {
18027 /* Decrement inhcount and possibly set islocal to true */
18028 HeapTuple copyTuple = heap_copytuple(constraintTuple);
18029 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
18030
18031 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18032 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18033 RelationGetRelid(child_rel), NameStr(copy_con->conname));
18034
18035 copy_con->coninhcount--;
18036 if (copy_con->coninhcount == 0)
18037 copy_con->conislocal = true;
18038
18039 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
18040 heap_freetuple(copyTuple);
18041 }
18042 }
18043
18044 /* We should have matched all constraints */
18045 if (connames != NIL || nncolumns != NIL)
18046 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18047 list_length(connames) + list_length(nncolumns),
18048 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
18049
18050 systable_endscan(scan);
18051 table_close(catalogRelation, RowExclusiveLock);
18052
18054 RelationRelationId,
18055 RelationGetRelid(parent_rel),
18056 child_dependency_type(is_partitioning));
18057
18058 /*
18059 * Post alter hook of this inherits. Since object_access_hook doesn't take
18060 * multiple object identifiers, we relay oid of parent relation using
18061 * auxiliary_id argument.
18062 */
18063 InvokeObjectPostAlterHookArg(InheritsRelationId,
18064 RelationGetRelid(child_rel), 0,
18065 RelationGetRelid(parent_rel), false);
18066}
18067
18068/*
18069 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
18070 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
18071 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
18072 * be TypeRelationId). There's no convenient way to do this, so go trawling
18073 * through pg_depend.
18074 */
18075static void
18076drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
18077 DependencyType deptype)
18078{
18079 Relation catalogRelation;
18080 SysScanDesc scan;
18081 ScanKeyData key[3];
18082 HeapTuple depTuple;
18083
18084 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
18085
18086 ScanKeyInit(&key[0],
18087 Anum_pg_depend_classid,
18088 BTEqualStrategyNumber, F_OIDEQ,
18089 ObjectIdGetDatum(RelationRelationId));
18090 ScanKeyInit(&key[1],
18091 Anum_pg_depend_objid,
18092 BTEqualStrategyNumber, F_OIDEQ,
18093 ObjectIdGetDatum(relid));
18094 ScanKeyInit(&key[2],
18095 Anum_pg_depend_objsubid,
18096 BTEqualStrategyNumber, F_INT4EQ,
18097 Int32GetDatum(0));
18098
18099 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
18100 NULL, 3, key);
18101
18102 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
18103 {
18104 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
18105
18106 if (dep->refclassid == refclassid &&
18107 dep->refobjid == refobjid &&
18108 dep->refobjsubid == 0 &&
18109 dep->deptype == deptype)
18110 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
18111 }
18112
18113 systable_endscan(scan);
18114 table_close(catalogRelation, RowExclusiveLock);
18115}
18116
18117/*
18118 * ALTER TABLE OF
18119 *
18120 * Attach a table to a composite type, as though it had been created with CREATE
18121 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
18122 * subject table must not have inheritance parents. These restrictions ensure
18123 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
18124 *
18125 * The address of the type is returned.
18126 */
18127static ObjectAddress
18128ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
18129{
18130 Oid relid = RelationGetRelid(rel);
18131 Type typetuple;
18132 Form_pg_type typeform;
18133 Oid typeid;
18134 Relation inheritsRelation,
18135 relationRelation;
18136 SysScanDesc scan;
18138 AttrNumber table_attno,
18139 type_attno;
18140 TupleDesc typeTupleDesc,
18141 tableTupleDesc;
18142 ObjectAddress tableobj,
18143 typeobj;
18144 HeapTuple classtuple;
18145
18146 /* Validate the type. */
18147 typetuple = typenameType(NULL, ofTypename, NULL);
18148 check_of_type(typetuple);
18149 typeform = (Form_pg_type) GETSTRUCT(typetuple);
18150 typeid = typeform->oid;
18151
18152 /* Fail if the table has any inheritance parents. */
18153 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
18155 Anum_pg_inherits_inhrelid,
18156 BTEqualStrategyNumber, F_OIDEQ,
18157 ObjectIdGetDatum(relid));
18158 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
18159 true, NULL, 1, &key);
18161 ereport(ERROR,
18162 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18163 errmsg("typed tables cannot inherit")));
18164 systable_endscan(scan);
18165 table_close(inheritsRelation, AccessShareLock);
18166
18167 /*
18168 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18169 * require that the order also match. However, attnotnull need not match.
18170 */
18171 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
18172 tableTupleDesc = RelationGetDescr(rel);
18173 table_attno = 1;
18174 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18175 {
18176 Form_pg_attribute type_attr,
18177 table_attr;
18178 const char *type_attname,
18179 *table_attname;
18180
18181 /* Get the next non-dropped type attribute. */
18182 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
18183 if (type_attr->attisdropped)
18184 continue;
18185 type_attname = NameStr(type_attr->attname);
18186
18187 /* Get the next non-dropped table attribute. */
18188 do
18189 {
18190 if (table_attno > tableTupleDesc->natts)
18191 ereport(ERROR,
18192 (errcode(ERRCODE_DATATYPE_MISMATCH),
18193 errmsg("table is missing column \"%s\"",
18194 type_attname)));
18195 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
18196 table_attno++;
18197 } while (table_attr->attisdropped);
18198 table_attname = NameStr(table_attr->attname);
18199
18200 /* Compare name. */
18201 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
18202 ereport(ERROR,
18203 (errcode(ERRCODE_DATATYPE_MISMATCH),
18204 errmsg("table has column \"%s\" where type requires \"%s\"",
18205 table_attname, type_attname)));
18206
18207 /* Compare type. */
18208 if (table_attr->atttypid != type_attr->atttypid ||
18209 table_attr->atttypmod != type_attr->atttypmod ||
18210 table_attr->attcollation != type_attr->attcollation)
18211 ereport(ERROR,
18212 (errcode(ERRCODE_DATATYPE_MISMATCH),
18213 errmsg("table \"%s\" has different type for column \"%s\"",
18214 RelationGetRelationName(rel), type_attname)));
18215 }
18216 ReleaseTupleDesc(typeTupleDesc);
18217
18218 /* Any remaining columns at the end of the table had better be dropped. */
18219 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18220 {
18221 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
18222 table_attno - 1);
18223
18224 if (!table_attr->attisdropped)
18225 ereport(ERROR,
18226 (errcode(ERRCODE_DATATYPE_MISMATCH),
18227 errmsg("table has extra column \"%s\"",
18228 NameStr(table_attr->attname))));
18229 }
18230
18231 /* If the table was already typed, drop the existing dependency. */
18232 if (rel->rd_rel->reloftype)
18233 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18235
18236 /* Record a dependency on the new type. */
18237 tableobj.classId = RelationRelationId;
18238 tableobj.objectId = relid;
18239 tableobj.objectSubId = 0;
18240 typeobj.classId = TypeRelationId;
18241 typeobj.objectId = typeid;
18242 typeobj.objectSubId = 0;
18243 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
18244
18245 /* Update pg_class.reloftype */
18246 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18247 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18248 if (!HeapTupleIsValid(classtuple))
18249 elog(ERROR, "cache lookup failed for relation %u", relid);
18250 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18251 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
18252
18253 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18254
18255 heap_freetuple(classtuple);
18256 table_close(relationRelation, RowExclusiveLock);
18257
18258 ReleaseSysCache(typetuple);
18259
18260 return typeobj;
18261}
18262
18263/*
18264 * ALTER TABLE NOT OF
18265 *
18266 * Detach a typed table from its originating type. Just clear reloftype and
18267 * remove the dependency.
18268 */
18269static void
18271{
18272 Oid relid = RelationGetRelid(rel);
18273 Relation relationRelation;
18274 HeapTuple tuple;
18275
18276 if (!OidIsValid(rel->rd_rel->reloftype))
18277 ereport(ERROR,
18278 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18279 errmsg("\"%s\" is not a typed table",
18281
18282 /*
18283 * We don't bother to check ownership of the type --- ownership of the
18284 * table is presumed enough rights. No lock required on the type, either.
18285 */
18286
18287 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18289
18290 /* Clear pg_class.reloftype */
18291 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
18292 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18293 if (!HeapTupleIsValid(tuple))
18294 elog(ERROR, "cache lookup failed for relation %u", relid);
18295 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18296 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
18297
18298 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
18299
18300 heap_freetuple(tuple);
18301 table_close(relationRelation, RowExclusiveLock);
18302}
18303
18304/*
18305 * relation_mark_replica_identity: Update a table's replica identity
18306 *
18307 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
18308 * index. Otherwise, it must be InvalidOid.
18309 *
18310 * Caller had better hold an exclusive lock on the relation, as the results
18311 * of running two of these concurrently wouldn't be pretty.
18312 */
18313static void
18314relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
18315 bool is_internal)
18316{
18317 Relation pg_index;
18318 Relation pg_class;
18319 HeapTuple pg_class_tuple;
18320 HeapTuple pg_index_tuple;
18321 Form_pg_class pg_class_form;
18322 Form_pg_index pg_index_form;
18323 ListCell *index;
18324
18325 /*
18326 * Check whether relreplident has changed, and update it if so.
18327 */
18328 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18329 pg_class_tuple = SearchSysCacheCopy1(RELOID,
18331 if (!HeapTupleIsValid(pg_class_tuple))
18332 elog(ERROR, "cache lookup failed for relation \"%s\"",
18334 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
18335 if (pg_class_form->relreplident != ri_type)
18336 {
18337 pg_class_form->relreplident = ri_type;
18338 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
18339 }
18340 table_close(pg_class, RowExclusiveLock);
18341 heap_freetuple(pg_class_tuple);
18342
18343 /*
18344 * Update the per-index indisreplident flags correctly.
18345 */
18346 pg_index = table_open(IndexRelationId, RowExclusiveLock);
18347 foreach(index, RelationGetIndexList(rel))
18348 {
18349 Oid thisIndexOid = lfirst_oid(index);
18350 bool dirty = false;
18351
18352 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
18353 ObjectIdGetDatum(thisIndexOid));
18354 if (!HeapTupleIsValid(pg_index_tuple))
18355 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18356 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
18357
18358 if (thisIndexOid == indexOid)
18359 {
18360 /* Set the bit if not already set. */
18361 if (!pg_index_form->indisreplident)
18362 {
18363 dirty = true;
18364 pg_index_form->indisreplident = true;
18365 }
18366 }
18367 else
18368 {
18369 /* Unset the bit if set. */
18370 if (pg_index_form->indisreplident)
18371 {
18372 dirty = true;
18373 pg_index_form->indisreplident = false;
18374 }
18375 }
18376
18377 if (dirty)
18378 {
18379 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
18380 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
18381 InvalidOid, is_internal);
18382
18383 /*
18384 * Invalidate the relcache for the table, so that after we commit
18385 * all sessions will refresh the table's replica identity index
18386 * before attempting any UPDATE or DELETE on the table. (If we
18387 * changed the table's pg_class row above, then a relcache inval
18388 * is already queued due to that; but we might not have.)
18389 */
18391 }
18392 heap_freetuple(pg_index_tuple);
18393 }
18394
18395 table_close(pg_index, RowExclusiveLock);
18396}
18397
18398/*
18399 * ALTER TABLE <name> REPLICA IDENTITY ...
18400 */
18401static void
18403{
18404 Oid indexOid;
18405 Relation indexRel;
18406 int key;
18407
18408 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18409 {
18410 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18411 return;
18412 }
18413 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18414 {
18415 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18416 return;
18417 }
18418 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18419 {
18420 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18421 return;
18422 }
18423 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18424 {
18425 /* fallthrough */ ;
18426 }
18427 else
18428 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18429
18430 /* Check that the index exists */
18431 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18432 if (!OidIsValid(indexOid))
18433 ereport(ERROR,
18434 (errcode(ERRCODE_UNDEFINED_OBJECT),
18435 errmsg("index \"%s\" for table \"%s\" does not exist",
18436 stmt->name, RelationGetRelationName(rel))));
18437
18438 indexRel = index_open(indexOid, ShareLock);
18439
18440 /* Check that the index is on the relation we're altering. */
18441 if (indexRel->rd_index == NULL ||
18442 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18443 ereport(ERROR,
18444 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18445 errmsg("\"%s\" is not an index for table \"%s\"",
18446 RelationGetRelationName(indexRel),
18448
18449 /*
18450 * The AM must support uniqueness, and the index must in fact be unique.
18451 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18452 * exclusion), we can use that too.
18453 */
18454 if ((!indexRel->rd_indam->amcanunique ||
18455 !indexRel->rd_index->indisunique) &&
18456 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18457 ereport(ERROR,
18458 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18459 errmsg("cannot use non-unique index \"%s\" as replica identity",
18460 RelationGetRelationName(indexRel))));
18461 /* Deferred indexes are not guaranteed to be always unique. */
18462 if (!indexRel->rd_index->indimmediate)
18463 ereport(ERROR,
18464 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18465 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18466 RelationGetRelationName(indexRel))));
18467 /* Expression indexes aren't supported. */
18468 if (RelationGetIndexExpressions(indexRel) != NIL)
18469 ereport(ERROR,
18470 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18471 errmsg("cannot use expression index \"%s\" as replica identity",
18472 RelationGetRelationName(indexRel))));
18473 /* Predicate indexes aren't supported. */
18474 if (RelationGetIndexPredicate(indexRel) != NIL)
18475 ereport(ERROR,
18476 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18477 errmsg("cannot use partial index \"%s\" as replica identity",
18478 RelationGetRelationName(indexRel))));
18479
18480 /* Check index for nullable columns. */
18481 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18482 {
18483 int16 attno = indexRel->rd_index->indkey.values[key];
18484 Form_pg_attribute attr;
18485
18486 /*
18487 * Reject any other system columns. (Going forward, we'll disallow
18488 * indexes containing such columns in the first place, but they might
18489 * exist in older branches.)
18490 */
18491 if (attno <= 0)
18492 ereport(ERROR,
18493 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
18494 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18495 RelationGetRelationName(indexRel), attno)));
18496
18497 attr = TupleDescAttr(rel->rd_att, attno - 1);
18498 if (!attr->attnotnull)
18499 ereport(ERROR,
18500 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18501 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18502 RelationGetRelationName(indexRel),
18503 NameStr(attr->attname))));
18504 }
18505
18506 /* This index is suitable for use as a replica identity. Mark it. */
18507 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18508
18509 index_close(indexRel, NoLock);
18510}
18511
18512/*
18513 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
18514 */
18515static void
18517{
18518 Relation pg_class;
18519 Oid relid;
18520 HeapTuple tuple;
18521
18522 relid = RelationGetRelid(rel);
18523
18524 /* Pull the record for this relation and update it */
18525 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18526
18527 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18528
18529 if (!HeapTupleIsValid(tuple))
18530 elog(ERROR, "cache lookup failed for relation %u", relid);
18531
18532 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
18533 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18534
18535 InvokeObjectPostAlterHook(RelationRelationId,
18536 RelationGetRelid(rel), 0);
18537
18538 table_close(pg_class, RowExclusiveLock);
18539 heap_freetuple(tuple);
18540}
18541
18542/*
18543 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
18544 */
18545static void
18547{
18548 Relation pg_class;
18549 Oid relid;
18550 HeapTuple tuple;
18551
18552 relid = RelationGetRelid(rel);
18553
18554 pg_class = table_open(RelationRelationId, RowExclusiveLock);
18555
18556 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
18557
18558 if (!HeapTupleIsValid(tuple))
18559 elog(ERROR, "cache lookup failed for relation %u", relid);
18560
18561 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
18562 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18563
18564 InvokeObjectPostAlterHook(RelationRelationId,
18565 RelationGetRelid(rel), 0);
18566
18567 table_close(pg_class, RowExclusiveLock);
18568 heap_freetuple(tuple);
18569}
18570
18571/*
18572 * ALTER FOREIGN TABLE <name> OPTIONS (...)
18573 */
18574static void
18576{
18577 Relation ftrel;
18578 ForeignServer *server;
18579 ForeignDataWrapper *fdw;
18580 HeapTuple tuple;
18581 bool isnull;
18582 Datum repl_val[Natts_pg_foreign_table];
18583 bool repl_null[Natts_pg_foreign_table];
18584 bool repl_repl[Natts_pg_foreign_table];
18585 Datum datum;
18586 Form_pg_foreign_table tableform;
18587
18588 if (options == NIL)
18589 return;
18590
18591 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
18592
18593 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
18594 ObjectIdGetDatum(rel->rd_id));
18595 if (!HeapTupleIsValid(tuple))
18596 ereport(ERROR,
18597 (errcode(ERRCODE_UNDEFINED_OBJECT),
18598 errmsg("foreign table \"%s\" does not exist",
18600 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
18601 server = GetForeignServer(tableform->ftserver);
18602 fdw = GetForeignDataWrapper(server->fdwid);
18603
18604 memset(repl_val, 0, sizeof(repl_val));
18605 memset(repl_null, false, sizeof(repl_null));
18606 memset(repl_repl, false, sizeof(repl_repl));
18607
18608 /* Extract the current options */
18609 datum = SysCacheGetAttr(FOREIGNTABLEREL,
18610 tuple,
18611 Anum_pg_foreign_table_ftoptions,
18612 &isnull);
18613 if (isnull)
18614 datum = PointerGetDatum(NULL);
18615
18616 /* Transform the options */
18617 datum = transformGenericOptions(ForeignTableRelationId,
18618 datum,
18619 options,
18620 fdw->fdwvalidator);
18621
18622 if (PointerIsValid(DatumGetPointer(datum)))
18623 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
18624 else
18625 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
18626
18627 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
18628
18629 /* Everything looks good - update the tuple */
18630
18631 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
18632 repl_val, repl_null, repl_repl);
18633
18634 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18635
18636 /*
18637 * Invalidate relcache so that all sessions will refresh any cached plans
18638 * that might depend on the old options.
18639 */
18641
18642 InvokeObjectPostAlterHook(ForeignTableRelationId,
18643 RelationGetRelid(rel), 0);
18644
18646
18647 heap_freetuple(tuple);
18648}
18649
18650/*
18651 * ALTER TABLE ALTER COLUMN SET COMPRESSION
18652 *
18653 * Return value is the address of the modified column
18654 */
18655static ObjectAddress
18657 const char *column,
18658 Node *newValue,
18659 LOCKMODE lockmode)
18660{
18661 Relation attrel;
18662 HeapTuple tuple;
18663 Form_pg_attribute atttableform;
18665 char *compression;
18666 char cmethod;
18667 ObjectAddress address;
18668
18669 compression = strVal(newValue);
18670
18671 attrel = table_open(AttributeRelationId, RowExclusiveLock);
18672
18673 /* copy the cache entry so we can scribble on it below */
18674 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
18675 if (!HeapTupleIsValid(tuple))
18676 ereport(ERROR,
18677 (errcode(ERRCODE_UNDEFINED_COLUMN),
18678 errmsg("column \"%s\" of relation \"%s\" does not exist",
18679 column, RelationGetRelationName(rel))));
18680
18681 /* prevent them from altering a system attribute */
18682 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
18683 attnum = atttableform->attnum;
18684 if (attnum <= 0)
18685 ereport(ERROR,
18686 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18687 errmsg("cannot alter system column \"%s\"", column)));
18688
18689 /*
18690 * Check that column type is compressible, then get the attribute
18691 * compression method code
18692 */
18693 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
18694
18695 /* update pg_attribute entry */
18696 atttableform->attcompression = cmethod;
18697 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
18698
18699 InvokeObjectPostAlterHook(RelationRelationId,
18700 RelationGetRelid(rel),
18701 attnum);
18702
18703 /*
18704 * Apply the change to indexes as well (only for simple index columns,
18705 * matching behavior of index.c ConstructTupleDescriptor()).
18706 */
18707 SetIndexStorageProperties(rel, attrel, attnum,
18708 false, 0,
18709 true, cmethod,
18710 lockmode);
18711
18712 heap_freetuple(tuple);
18713
18715
18716 /* make changes visible */
18718
18719 ObjectAddressSubSet(address, RelationRelationId,
18720 RelationGetRelid(rel), attnum);
18721 return address;
18722}
18723
18724
18725/*
18726 * Preparation phase for SET LOGGED/UNLOGGED
18727 *
18728 * This verifies that we're not trying to change a temp table. Also,
18729 * existing foreign key constraints are checked to avoid ending up with
18730 * permanent tables referencing unlogged tables.
18731 */
18732static void
18734{
18735 Relation pg_constraint;
18736 HeapTuple tuple;
18737 SysScanDesc scan;
18738 ScanKeyData skey[1];
18739
18740 /*
18741 * Disallow changing status for a temp table. Also verify whether we can
18742 * get away with doing nothing; in such cases we don't need to run the
18743 * checks below, either.
18744 */
18745 switch (rel->rd_rel->relpersistence)
18746 {
18747 case RELPERSISTENCE_TEMP:
18748 ereport(ERROR,
18749 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18750 errmsg("cannot change logged status of table \"%s\" because it is temporary",
18752 errtable(rel)));
18753 break;
18754 case RELPERSISTENCE_PERMANENT:
18755 if (toLogged)
18756 /* nothing to do */
18757 return;
18758 break;
18759 case RELPERSISTENCE_UNLOGGED:
18760 if (!toLogged)
18761 /* nothing to do */
18762 return;
18763 break;
18764 }
18765
18766 /*
18767 * Check that the table is not part of any publication when changing to
18768 * UNLOGGED, as UNLOGGED tables can't be published.
18769 */
18770 if (!toLogged &&
18772 ereport(ERROR,
18773 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18774 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
18776 errdetail("Unlogged relations cannot be replicated.")));
18777
18778 /*
18779 * Check existing foreign key constraints to preserve the invariant that
18780 * permanent tables cannot reference unlogged ones. Self-referencing
18781 * foreign keys can safely be ignored.
18782 */
18783 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
18784
18785 /*
18786 * Scan conrelid if changing to permanent, else confrelid. This also
18787 * determines whether a useful index exists.
18788 */
18789 ScanKeyInit(&skey[0],
18790 toLogged ? Anum_pg_constraint_conrelid :
18791 Anum_pg_constraint_confrelid,
18792 BTEqualStrategyNumber, F_OIDEQ,
18794 scan = systable_beginscan(pg_constraint,
18795 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
18796 true, NULL, 1, skey);
18797
18798 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18799 {
18801
18802 if (con->contype == CONSTRAINT_FOREIGN)
18803 {
18804 Oid foreignrelid;
18805 Relation foreignrel;
18806
18807 /* the opposite end of what we used as scankey */
18808 foreignrelid = toLogged ? con->confrelid : con->conrelid;
18809
18810 /* ignore if self-referencing */
18811 if (RelationGetRelid(rel) == foreignrelid)
18812 continue;
18813
18814 foreignrel = relation_open(foreignrelid, AccessShareLock);
18815
18816 if (toLogged)
18817 {
18818 if (!RelationIsPermanent(foreignrel))
18819 ereport(ERROR,
18820 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18821 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
18823 RelationGetRelationName(foreignrel)),
18824 errtableconstraint(rel, NameStr(con->conname))));
18825 }
18826 else
18827 {
18828 if (RelationIsPermanent(foreignrel))
18829 ereport(ERROR,
18830 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
18831 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
18833 RelationGetRelationName(foreignrel)),
18834 errtableconstraint(rel, NameStr(con->conname))));
18835 }
18836
18837 relation_close(foreignrel, AccessShareLock);
18838 }
18839 }
18840
18841 systable_endscan(scan);
18842
18843 table_close(pg_constraint, AccessShareLock);
18844
18845 /* force rewrite if necessary; see comment in ATRewriteTables */
18847 if (toLogged)
18848 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
18849 else
18850 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
18851 tab->chgPersistence = true;
18852}
18853
18854/*
18855 * Execute ALTER TABLE SET SCHEMA
18856 */
18859{
18860 Relation rel;
18861 Oid relid;
18862 Oid oldNspOid;
18863 Oid nspOid;
18864 RangeVar *newrv;
18865 ObjectAddresses *objsMoved;
18866 ObjectAddress myself;
18867
18869 stmt->missing_ok ? RVR_MISSING_OK : 0,
18871 stmt);
18872
18873 if (!OidIsValid(relid))
18874 {
18876 (errmsg("relation \"%s\" does not exist, skipping",
18877 stmt->relation->relname)));
18878 return InvalidObjectAddress;
18879 }
18880
18881 rel = relation_open(relid, NoLock);
18882
18883 oldNspOid = RelationGetNamespace(rel);
18884
18885 /* If it's an owned sequence, disallow moving it by itself. */
18886 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
18887 {
18888 Oid tableId;
18889 int32 colId;
18890
18891 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
18892 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
18893 ereport(ERROR,
18894 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18895 errmsg("cannot move an owned sequence into another schema"),
18896 errdetail("Sequence \"%s\" is linked to table \"%s\".",
18898 get_rel_name(tableId))));
18899 }
18900
18901 /* Get and lock schema OID and check its permissions. */
18902 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
18903 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
18904
18905 /* common checks on switching namespaces */
18906 CheckSetNamespace(oldNspOid, nspOid);
18907
18908 objsMoved = new_object_addresses();
18909 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
18910 free_object_addresses(objsMoved);
18911
18912 ObjectAddressSet(myself, RelationRelationId, relid);
18913
18914 if (oldschema)
18915 *oldschema = oldNspOid;
18916
18917 /* close rel, but keep lock until commit */
18918 relation_close(rel, NoLock);
18919
18920 return myself;
18921}
18922
18923/*
18924 * The guts of relocating a table or materialized view to another namespace:
18925 * besides moving the relation itself, its dependent objects are relocated to
18926 * the new schema.
18927 */
18928void
18930 ObjectAddresses *objsMoved)
18931{
18932 Relation classRel;
18933
18934 Assert(objsMoved != NULL);
18935
18936 /* OK, modify the pg_class row and pg_depend entry */
18937 classRel = table_open(RelationRelationId, RowExclusiveLock);
18938
18939 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18940 nspOid, true, objsMoved);
18941
18942 /* Fix the table's row type too, if it has one */
18943 if (OidIsValid(rel->rd_rel->reltype))
18944 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18945 false, /* isImplicitArray */
18946 false, /* ignoreDependent */
18947 false, /* errorOnTableType */
18948 objsMoved);
18949
18950 /* Fix other dependent stuff */
18951 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18952 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18953 objsMoved, AccessExclusiveLock);
18954 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18955 false, objsMoved);
18956
18957 table_close(classRel, RowExclusiveLock);
18958}
18959
18960/*
18961 * The guts of relocating a relation to another namespace: fix the pg_class
18962 * entry, and the pg_depend entry if any. Caller must already have
18963 * opened and write-locked pg_class.
18964 */
18965void
18967 Oid oldNspOid, Oid newNspOid,
18968 bool hasDependEntry,
18969 ObjectAddresses *objsMoved)
18970{
18971 HeapTuple classTup;
18972 Form_pg_class classForm;
18973 ObjectAddress thisobj;
18974 bool already_done = false;
18975
18976 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
18977 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
18978 if (!HeapTupleIsValid(classTup))
18979 elog(ERROR, "cache lookup failed for relation %u", relOid);
18980 classForm = (Form_pg_class) GETSTRUCT(classTup);
18981
18982 Assert(classForm->relnamespace == oldNspOid);
18983
18984 thisobj.classId = RelationRelationId;
18985 thisobj.objectId = relOid;
18986 thisobj.objectSubId = 0;
18987
18988 /*
18989 * If the object has already been moved, don't move it again. If it's
18990 * already in the right place, don't move it, but still fire the object
18991 * access hook.
18992 */
18993 already_done = object_address_present(&thisobj, objsMoved);
18994 if (!already_done && oldNspOid != newNspOid)
18995 {
18996 ItemPointerData otid = classTup->t_self;
18997
18998 /* check for duplicate name (more friendly than unique-index failure) */
18999 if (get_relname_relid(NameStr(classForm->relname),
19000 newNspOid) != InvalidOid)
19001 ereport(ERROR,
19002 (errcode(ERRCODE_DUPLICATE_TABLE),
19003 errmsg("relation \"%s\" already exists in schema \"%s\"",
19004 NameStr(classForm->relname),
19005 get_namespace_name(newNspOid))));
19006
19007 /* classTup is a copy, so OK to scribble on */
19008 classForm->relnamespace = newNspOid;
19009
19010 CatalogTupleUpdate(classRel, &otid, classTup);
19011 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
19012
19013
19014 /* Update dependency on schema if caller said so */
19015 if (hasDependEntry &&
19016 changeDependencyFor(RelationRelationId,
19017 relOid,
19018 NamespaceRelationId,
19019 oldNspOid,
19020 newNspOid) != 1)
19021 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19022 NameStr(classForm->relname));
19023 }
19024 else
19025 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
19026 if (!already_done)
19027 {
19028 add_exact_object_address(&thisobj, objsMoved);
19029
19030 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
19031 }
19032
19033 heap_freetuple(classTup);
19034}
19035
19036/*
19037 * Move all indexes for the specified relation to another namespace.
19038 *
19039 * Note: we assume adequate permission checking was done by the caller,
19040 * and that the caller has a suitable lock on the owning relation.
19041 */
19042static void
19044 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
19045{
19046 List *indexList;
19047 ListCell *l;
19048
19049 indexList = RelationGetIndexList(rel);
19050
19051 foreach(l, indexList)
19052 {
19053 Oid indexOid = lfirst_oid(l);
19054 ObjectAddress thisobj;
19055
19056 thisobj.classId = RelationRelationId;
19057 thisobj.objectId = indexOid;
19058 thisobj.objectSubId = 0;
19059
19060 /*
19061 * Note: currently, the index will not have its own dependency on the
19062 * namespace, so we don't need to do changeDependencyFor(). There's no
19063 * row type in pg_type, either.
19064 *
19065 * XXX this objsMoved test may be pointless -- surely we have a single
19066 * dependency link from a relation to each index?
19067 */
19068 if (!object_address_present(&thisobj, objsMoved))
19069 {
19070 AlterRelationNamespaceInternal(classRel, indexOid,
19071 oldNspOid, newNspOid,
19072 false, objsMoved);
19073 add_exact_object_address(&thisobj, objsMoved);
19074 }
19075 }
19076
19077 list_free(indexList);
19078}
19079
19080/*
19081 * Move all identity and SERIAL-column sequences of the specified relation to another
19082 * namespace.
19083 *
19084 * Note: we assume adequate permission checking was done by the caller,
19085 * and that the caller has a suitable lock on the owning relation.
19086 */
19087static void
19089 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
19090 LOCKMODE lockmode)
19091{
19092 Relation depRel;
19093 SysScanDesc scan;
19094 ScanKeyData key[2];
19095 HeapTuple tup;
19096
19097 /*
19098 * SERIAL sequences are those having an auto dependency on one of the
19099 * table's columns (we don't care *which* column, exactly).
19100 */
19101 depRel = table_open(DependRelationId, AccessShareLock);
19102
19103 ScanKeyInit(&key[0],
19104 Anum_pg_depend_refclassid,
19105 BTEqualStrategyNumber, F_OIDEQ,
19106 ObjectIdGetDatum(RelationRelationId));
19107 ScanKeyInit(&key[1],
19108 Anum_pg_depend_refobjid,
19109 BTEqualStrategyNumber, F_OIDEQ,
19111 /* we leave refobjsubid unspecified */
19112
19113 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
19114 NULL, 2, key);
19115
19116 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19117 {
19118 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
19119 Relation seqRel;
19120
19121 /* skip dependencies other than auto dependencies on columns */
19122 if (depForm->refobjsubid == 0 ||
19123 depForm->classid != RelationRelationId ||
19124 depForm->objsubid != 0 ||
19125 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19126 continue;
19127
19128 /* Use relation_open just in case it's an index */
19129 seqRel = relation_open(depForm->objid, lockmode);
19130
19131 /* skip non-sequence relations */
19132 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19133 {
19134 /* No need to keep the lock */
19135 relation_close(seqRel, lockmode);
19136 continue;
19137 }
19138
19139 /* Fix the pg_class and pg_depend entries */
19140 AlterRelationNamespaceInternal(classRel, depForm->objid,
19141 oldNspOid, newNspOid,
19142 true, objsMoved);
19143
19144 /*
19145 * Sequences used to have entries in pg_type, but no longer do. If we
19146 * ever re-instate that, we'll need to move the pg_type entry to the
19147 * new namespace, too (using AlterTypeNamespaceInternal).
19148 */
19149 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
19150
19151 /* Now we can close it. Keep the lock till end of transaction. */
19152 relation_close(seqRel, NoLock);
19153 }
19154
19155 systable_endscan(scan);
19156
19158}
19159
19160
19161/*
19162 * This code supports
19163 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
19164 *
19165 * Because we only support this for TEMP tables, it's sufficient to remember
19166 * the state in a backend-local data structure.
19167 */
19168
19169/*
19170 * Register a newly-created relation's ON COMMIT action.
19171 */
19172void
19174{
19175 OnCommitItem *oc;
19176 MemoryContext oldcxt;
19177
19178 /*
19179 * We needn't bother registering the relation unless there is an ON COMMIT
19180 * action we need to take.
19181 */
19183 return;
19184
19186
19187 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
19188 oc->relid = relid;
19189 oc->oncommit = action;
19192
19193 /*
19194 * We use lcons() here so that ON COMMIT actions are processed in reverse
19195 * order of registration. That might not be essential but it seems
19196 * reasonable.
19197 */
19199
19200 MemoryContextSwitchTo(oldcxt);
19201}
19202
19203/*
19204 * Unregister any ON COMMIT action when a relation is deleted.
19205 *
19206 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
19207 */
19208void
19210{
19211 ListCell *l;
19212
19213 foreach(l, on_commits)
19214 {
19215 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19216
19217 if (oc->relid == relid)
19218 {
19220 break;
19221 }
19222 }
19223}
19224
19225/*
19226 * Perform ON COMMIT actions.
19227 *
19228 * This is invoked just before actually committing, since it's possible
19229 * to encounter errors.
19230 */
19231void
19233{
19234 ListCell *l;
19235 List *oids_to_truncate = NIL;
19236 List *oids_to_drop = NIL;
19237
19238 foreach(l, on_commits)
19239 {
19240 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19241
19242 /* Ignore entry if already dropped in this xact */
19244 continue;
19245
19246 switch (oc->oncommit)
19247 {
19248 case ONCOMMIT_NOOP:
19250 /* Do nothing (there shouldn't be such entries, actually) */
19251 break;
19253
19254 /*
19255 * If this transaction hasn't accessed any temporary
19256 * relations, we can skip truncating ON COMMIT DELETE ROWS
19257 * tables, as they must still be empty.
19258 */
19260 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
19261 break;
19262 case ONCOMMIT_DROP:
19263 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
19264 break;
19265 }
19266 }
19267
19268 /*
19269 * Truncate relations before dropping so that all dependencies between
19270 * relations are removed after they are worked on. Doing it like this
19271 * might be a waste as it is possible that a relation being truncated will
19272 * be dropped anyway due to its parent being dropped, but this makes the
19273 * code more robust because of not having to re-check that the relation
19274 * exists at truncation time.
19275 */
19276 if (oids_to_truncate != NIL)
19277 heap_truncate(oids_to_truncate);
19278
19279 if (oids_to_drop != NIL)
19280 {
19281 ObjectAddresses *targetObjects = new_object_addresses();
19282
19283 foreach(l, oids_to_drop)
19284 {
19285 ObjectAddress object;
19286
19287 object.classId = RelationRelationId;
19288 object.objectId = lfirst_oid(l);
19289 object.objectSubId = 0;
19290
19291 Assert(!object_address_present(&object, targetObjects));
19292
19293 add_exact_object_address(&object, targetObjects);
19294 }
19295
19296 /*
19297 * Object deletion might involve toast table access (to clean up
19298 * toasted catalog entries), so ensure we have a valid snapshot.
19299 */
19301
19302 /*
19303 * Since this is an automatic drop, rather than one directly initiated
19304 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19305 */
19308
19310
19311#ifdef USE_ASSERT_CHECKING
19312
19313 /*
19314 * Note that table deletion will call remove_on_commit_action, so the
19315 * entry should get marked as deleted.
19316 */
19317 foreach(l, on_commits)
19318 {
19319 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
19320
19321 if (oc->oncommit != ONCOMMIT_DROP)
19322 continue;
19323
19325 }
19326#endif
19327 }
19328}
19329
19330/*
19331 * Post-commit or post-abort cleanup for ON COMMIT management.
19332 *
19333 * All we do here is remove no-longer-needed OnCommitItem entries.
19334 *
19335 * During commit, remove entries that were deleted during this transaction;
19336 * during abort, remove those created during this transaction.
19337 */
19338void
19340{
19341 ListCell *cur_item;
19342
19343 foreach(cur_item, on_commits)
19344 {
19345 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19346
19347 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19349 {
19350 /* cur_item must be removed */
19352 pfree(oc);
19353 }
19354 else
19355 {
19356 /* cur_item must be preserved */
19359 }
19360 }
19361}
19362
19363/*
19364 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
19365 *
19366 * During subabort, we can immediately remove entries created during this
19367 * subtransaction. During subcommit, just relabel entries marked during
19368 * this subtransaction as being the parent's responsibility.
19369 */
19370void
19372 SubTransactionId parentSubid)
19373{
19374 ListCell *cur_item;
19375
19376 foreach(cur_item, on_commits)
19377 {
19378 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
19379
19380 if (!isCommit && oc->creating_subid == mySubid)
19381 {
19382 /* cur_item must be removed */
19384 pfree(oc);
19385 }
19386 else
19387 {
19388 /* cur_item must be preserved */
19389 if (oc->creating_subid == mySubid)
19390 oc->creating_subid = parentSubid;
19391 if (oc->deleting_subid == mySubid)
19392 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19393 }
19394 }
19395}
19396
19397/*
19398 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
19399 * the relation to be locked only if (1) it's a plain or partitioned table,
19400 * materialized view, or TOAST table and (2) the current user is the owner (or
19401 * the superuser) or has been granted MAINTAIN. This meets the
19402 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
19403 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
19404 */
19405void
19407 Oid relId, Oid oldRelId, void *arg)
19408{
19409 char relkind;
19410 AclResult aclresult;
19411
19412 /* Nothing to do if the relation was not found. */
19413 if (!OidIsValid(relId))
19414 return;
19415
19416 /*
19417 * If the relation does exist, check whether it's an index. But note that
19418 * the relation might have been dropped between the time we did the name
19419 * lookup and now. In that case, there's nothing to do.
19420 */
19421 relkind = get_rel_relkind(relId);
19422 if (!relkind)
19423 return;
19424 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19425 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19426 ereport(ERROR,
19427 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19428 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19429
19430 /* Check permissions */
19431 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
19432 if (aclresult != ACLCHECK_OK)
19433 aclcheck_error(aclresult,
19435 relation->relname);
19436}
19437
19438/*
19439 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
19440 */
19441static void
19443 Oid relId, Oid oldRelId, void *arg)
19444{
19445 HeapTuple tuple;
19446
19447 /* Nothing to do if the relation was not found. */
19448 if (!OidIsValid(relId))
19449 return;
19450
19451 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19452 if (!HeapTupleIsValid(tuple)) /* should not happen */
19453 elog(ERROR, "cache lookup failed for relation %u", relId);
19454
19457
19458 ReleaseSysCache(tuple);
19459}
19460
19461/*
19462 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
19463 * the owner of the relation, or superuser.
19464 */
19465void
19467 Oid relId, Oid oldRelId, void *arg)
19468{
19469 HeapTuple tuple;
19470
19471 /* Nothing to do if the relation was not found. */
19472 if (!OidIsValid(relId))
19473 return;
19474
19475 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19476 if (!HeapTupleIsValid(tuple)) /* should not happen */
19477 elog(ERROR, "cache lookup failed for relation %u", relId);
19478
19479 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
19481 relation->relname);
19482
19483 if (!allowSystemTableMods &&
19484 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19485 ereport(ERROR,
19486 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19487 errmsg("permission denied: \"%s\" is a system catalog",
19488 relation->relname)));
19489
19490 ReleaseSysCache(tuple);
19491}
19492
19493/*
19494 * Common RangeVarGetRelid callback for rename, set schema, and alter table
19495 * processing.
19496 */
19497static void
19499 void *arg)
19500{
19501 Node *stmt = (Node *) arg;
19502 ObjectType reltype;
19503 HeapTuple tuple;
19504 Form_pg_class classform;
19505 AclResult aclresult;
19506 char relkind;
19507
19508 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19509 if (!HeapTupleIsValid(tuple))
19510 return; /* concurrently dropped */
19511 classform = (Form_pg_class) GETSTRUCT(tuple);
19512 relkind = classform->relkind;
19513
19514 /* Must own relation. */
19515 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
19517
19518 /* No system table modifications unless explicitly allowed. */
19519 if (!allowSystemTableMods && IsSystemClass(relid, classform))
19520 ereport(ERROR,
19521 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
19522 errmsg("permission denied: \"%s\" is a system catalog",
19523 rv->relname)));
19524
19525 /*
19526 * Extract the specified relation type from the statement parse tree.
19527 *
19528 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19529 * have CREATE rights on the containing namespace.
19530 */
19531 if (IsA(stmt, RenameStmt))
19532 {
19533 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
19535 if (aclresult != ACLCHECK_OK)
19536 aclcheck_error(aclresult, OBJECT_SCHEMA,
19537 get_namespace_name(classform->relnamespace));
19538 reltype = ((RenameStmt *) stmt)->renameType;
19539 }
19540 else if (IsA(stmt, AlterObjectSchemaStmt))
19541 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19542
19543 else if (IsA(stmt, AlterTableStmt))
19544 reltype = ((AlterTableStmt *) stmt)->objtype;
19545 else
19546 {
19547 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19548 reltype = OBJECT_TABLE; /* placate compiler */
19549 }
19550
19551 /*
19552 * For compatibility with prior releases, we allow ALTER TABLE to be used
19553 * with most other types of relations (but not composite types). We allow
19554 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19555 * otherwise. Otherwise, the user must select the correct form of the
19556 * command for the relation at issue.
19557 */
19558 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19559 ereport(ERROR,
19560 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19561 errmsg("\"%s\" is not a sequence", rv->relname)));
19562
19563 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19564 ereport(ERROR,
19565 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19566 errmsg("\"%s\" is not a view", rv->relname)));
19567
19568 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19569 ereport(ERROR,
19570 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19571 errmsg("\"%s\" is not a materialized view", rv->relname)));
19572
19573 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19574 ereport(ERROR,
19575 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19576 errmsg("\"%s\" is not a foreign table", rv->relname)));
19577
19578 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19579 ereport(ERROR,
19580 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19581 errmsg("\"%s\" is not a composite type", rv->relname)));
19582
19583 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19584 relkind != RELKIND_PARTITIONED_INDEX
19585 && !IsA(stmt, RenameStmt))
19586 ereport(ERROR,
19587 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19588 errmsg("\"%s\" is not an index", rv->relname)));
19589
19590 /*
19591 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19592 * TYPE for that.
19593 */
19594 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19595 ereport(ERROR,
19596 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19597 errmsg("\"%s\" is a composite type", rv->relname),
19598 /* translator: %s is an SQL ALTER command */
19599 errhint("Use %s instead.",
19600 "ALTER TYPE")));
19601
19602 /*
19603 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19604 * to a different schema, such as indexes and TOAST tables.
19605 */
19607 {
19608 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19609 ereport(ERROR,
19610 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19611 errmsg("cannot change schema of index \"%s\"",
19612 rv->relname),
19613 errhint("Change the schema of the table instead.")));
19614 else if (relkind == RELKIND_COMPOSITE_TYPE)
19615 ereport(ERROR,
19616 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19617 errmsg("cannot change schema of composite type \"%s\"",
19618 rv->relname),
19619 /* translator: %s is an SQL ALTER command */
19620 errhint("Use %s instead.",
19621 "ALTER TYPE")));
19622 else if (relkind == RELKIND_TOASTVALUE)
19623 ereport(ERROR,
19624 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19625 errmsg("cannot change schema of TOAST table \"%s\"",
19626 rv->relname),
19627 errhint("Change the schema of the table instead.")));
19628 }
19629
19630 ReleaseSysCache(tuple);
19631}
19632
19633/*
19634 * Transform any expressions present in the partition key
19635 *
19636 * Returns a transformed PartitionSpec.
19637 */
19638static PartitionSpec *
19640{
19641 PartitionSpec *newspec;
19642 ParseState *pstate;
19643 ParseNamespaceItem *nsitem;
19644 ListCell *l;
19645
19646 newspec = makeNode(PartitionSpec);
19647
19648 newspec->strategy = partspec->strategy;
19649 newspec->partParams = NIL;
19650 newspec->location = partspec->location;
19651
19652 /* Check valid number of columns for strategy */
19653 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19654 list_length(partspec->partParams) != 1)
19655 ereport(ERROR,
19656 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19657 errmsg("cannot use \"list\" partition strategy with more than one column")));
19658
19659 /*
19660 * Create a dummy ParseState and insert the target relation as its sole
19661 * rangetable entry. We need a ParseState for transformExpr.
19662 */
19663 pstate = make_parsestate(NULL);
19664 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
19665 NULL, false, true);
19666 addNSItemToQuery(pstate, nsitem, true, true, true);
19667
19668 /* take care of any partition expressions */
19669 foreach(l, partspec->partParams)
19670 {
19672
19673 if (pelem->expr)
19674 {
19675 /* Copy, to avoid scribbling on the input */
19676 pelem = copyObject(pelem);
19677
19678 /* Now do parse transformation of the expression */
19679 pelem->expr = transformExpr(pstate, pelem->expr,
19681
19682 /* we have to fix its collations too */
19683 assign_expr_collations(pstate, pelem->expr);
19684 }
19685
19686 newspec->partParams = lappend(newspec->partParams, pelem);
19687 }
19688
19689 return newspec;
19690}
19691
19692/*
19693 * Compute per-partition-column information from a list of PartitionElems.
19694 * Expressions in the PartitionElems must be parse-analyzed already.
19695 */
19696static void
19697ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
19698 List **partexprs, Oid *partopclass, Oid *partcollation,
19699 PartitionStrategy strategy)
19700{
19701 int attn;
19702 ListCell *lc;
19703 Oid am_oid;
19704
19705 attn = 0;
19706 foreach(lc, partParams)
19707 {
19709 Oid atttype;
19710 Oid attcollation;
19711
19712 if (pelem->name != NULL)
19713 {
19714 /* Simple attribute reference */
19715 HeapTuple atttuple;
19716 Form_pg_attribute attform;
19717
19719 pelem->name);
19720 if (!HeapTupleIsValid(atttuple))
19721 ereport(ERROR,
19722 (errcode(ERRCODE_UNDEFINED_COLUMN),
19723 errmsg("column \"%s\" named in partition key does not exist",
19724 pelem->name),
19725 parser_errposition(pstate, pelem->location)));
19726 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
19727
19728 if (attform->attnum <= 0)
19729 ereport(ERROR,
19730 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19731 errmsg("cannot use system column \"%s\" in partition key",
19732 pelem->name),
19733 parser_errposition(pstate, pelem->location)));
19734
19735 /*
19736 * Stored generated columns cannot work: They are computed after
19737 * BEFORE triggers, but partition routing is done before all
19738 * triggers. Maybe virtual generated columns could be made to
19739 * work, but then they would need to be handled as an expression
19740 * below.
19741 */
19742 if (attform->attgenerated)
19743 ereport(ERROR,
19744 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19745 errmsg("cannot use generated column in partition key"),
19746 errdetail("Column \"%s\" is a generated column.",
19747 pelem->name),
19748 parser_errposition(pstate, pelem->location)));
19749
19750 partattrs[attn] = attform->attnum;
19751 atttype = attform->atttypid;
19752 attcollation = attform->attcollation;
19753 ReleaseSysCache(atttuple);
19754 }
19755 else
19756 {
19757 /* Expression */
19758 Node *expr = pelem->expr;
19759 char partattname[16];
19760
19761 Assert(expr != NULL);
19762 atttype = exprType(expr);
19763 attcollation = exprCollation(expr);
19764
19765 /*
19766 * The expression must be of a storable type (e.g., not RECORD).
19767 * The test is the same as for whether a table column is of a safe
19768 * type (which is why we needn't check for the non-expression
19769 * case).
19770 */
19771 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
19772 CheckAttributeType(partattname,
19773 atttype, attcollation,
19775
19776 /*
19777 * Strip any top-level COLLATE clause. This ensures that we treat
19778 * "x COLLATE y" and "(x COLLATE y)" alike.
19779 */
19780 while (IsA(expr, CollateExpr))
19781 expr = (Node *) ((CollateExpr *) expr)->arg;
19782
19783 if (IsA(expr, Var) &&
19784 ((Var *) expr)->varattno > 0)
19785 {
19786 /*
19787 * User wrote "(column)" or "(column COLLATE something)".
19788 * Treat it like simple attribute anyway.
19789 */
19790 partattrs[attn] = ((Var *) expr)->varattno;
19791 }
19792 else
19793 {
19794 Bitmapset *expr_attrs = NULL;
19795 int i;
19796
19797 partattrs[attn] = 0; /* marks the column as expression */
19798 *partexprs = lappend(*partexprs, expr);
19799
19800 /*
19801 * transformPartitionSpec() should have already rejected
19802 * subqueries, aggregates, window functions, and SRFs, based
19803 * on the EXPR_KIND_ for partition expressions.
19804 */
19805
19806 /*
19807 * Cannot allow system column references, since that would
19808 * make partition routing impossible: their values won't be
19809 * known yet when we need to do that.
19810 */
19811 pull_varattnos(expr, 1, &expr_attrs);
19812 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
19813 {
19815 expr_attrs))
19816 ereport(ERROR,
19817 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19818 errmsg("partition key expressions cannot contain system column references")));
19819 }
19820
19821 /*
19822 * Stored generated columns cannot work: They are computed
19823 * after BEFORE triggers, but partition routing is done before
19824 * all triggers. Virtual generated columns could probably
19825 * work, but it would require more work elsewhere (for example
19826 * SET EXPRESSION would need to check whether the column is
19827 * used in partition keys). Seems safer to prohibit for now.
19828 */
19829 i = -1;
19830 while ((i = bms_next_member(expr_attrs, i)) >= 0)
19831 {
19833
19834 if (attno > 0 &&
19835 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
19836 ereport(ERROR,
19837 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19838 errmsg("cannot use generated column in partition key"),
19839 errdetail("Column \"%s\" is a generated column.",
19840 get_attname(RelationGetRelid(rel), attno, false)),
19841 parser_errposition(pstate, pelem->location)));
19842 }
19843
19844 /*
19845 * Preprocess the expression before checking for mutability.
19846 * This is essential for the reasons described in
19847 * contain_mutable_functions_after_planning. However, we call
19848 * expression_planner for ourselves rather than using that
19849 * function, because if constant-folding reduces the
19850 * expression to a constant, we'd like to know that so we can
19851 * complain below.
19852 *
19853 * Like contain_mutable_functions_after_planning, assume that
19854 * expression_planner won't scribble on its input, so this
19855 * won't affect the partexprs entry we saved above.
19856 */
19857 expr = (Node *) expression_planner((Expr *) expr);
19858
19859 /*
19860 * Partition expressions cannot contain mutable functions,
19861 * because a given row must always map to the same partition
19862 * as long as there is no change in the partition boundary
19863 * structure.
19864 */
19865 if (contain_mutable_functions(expr))
19866 ereport(ERROR,
19867 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19868 errmsg("functions in partition key expression must be marked IMMUTABLE")));
19869
19870 /*
19871 * While it is not exactly *wrong* for a partition expression
19872 * to be a constant, it seems better to reject such keys.
19873 */
19874 if (IsA(expr, Const))
19875 ereport(ERROR,
19876 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19877 errmsg("cannot use constant expression as partition key")));
19878 }
19879 }
19880
19881 /*
19882 * Apply collation override if any
19883 */
19884 if (pelem->collation)
19885 attcollation = get_collation_oid(pelem->collation, false);
19886
19887 /*
19888 * Check we have a collation iff it's a collatable type. The only
19889 * expected failures here are (1) COLLATE applied to a noncollatable
19890 * type, or (2) partition expression had an unresolved collation. But
19891 * we might as well code this to be a complete consistency check.
19892 */
19893 if (type_is_collatable(atttype))
19894 {
19895 if (!OidIsValid(attcollation))
19896 ereport(ERROR,
19897 (errcode(ERRCODE_INDETERMINATE_COLLATION),
19898 errmsg("could not determine which collation to use for partition expression"),
19899 errhint("Use the COLLATE clause to set the collation explicitly.")));
19900 }
19901 else
19902 {
19903 if (OidIsValid(attcollation))
19904 ereport(ERROR,
19905 (errcode(ERRCODE_DATATYPE_MISMATCH),
19906 errmsg("collations are not supported by type %s",
19907 format_type_be(atttype))));
19908 }
19909
19910 partcollation[attn] = attcollation;
19911
19912 /*
19913 * Identify the appropriate operator class. For list and range
19914 * partitioning, we use a btree operator class; hash partitioning uses
19915 * a hash operator class.
19916 */
19917 if (strategy == PARTITION_STRATEGY_HASH)
19918 am_oid = HASH_AM_OID;
19919 else
19920 am_oid = BTREE_AM_OID;
19921
19922 if (!pelem->opclass)
19923 {
19924 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
19925
19926 if (!OidIsValid(partopclass[attn]))
19927 {
19928 if (strategy == PARTITION_STRATEGY_HASH)
19929 ereport(ERROR,
19930 (errcode(ERRCODE_UNDEFINED_OBJECT),
19931 errmsg("data type %s has no default operator class for access method \"%s\"",
19932 format_type_be(atttype), "hash"),
19933 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
19934 else
19935 ereport(ERROR,
19936 (errcode(ERRCODE_UNDEFINED_OBJECT),
19937 errmsg("data type %s has no default operator class for access method \"%s\"",
19938 format_type_be(atttype), "btree"),
19939 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19940 }
19941 }
19942 else
19943 partopclass[attn] = ResolveOpClass(pelem->opclass,
19944 atttype,
19945 am_oid == HASH_AM_OID ? "hash" : "btree",
19946 am_oid);
19947
19948 attn++;
19949 }
19950}
19951
19952/*
19953 * PartConstraintImpliedByRelConstraint
19954 * Do scanrel's existing constraints imply the partition constraint?
19955 *
19956 * "Existing constraints" include its check constraints and column-level
19957 * not-null constraints. partConstraint describes the partition constraint,
19958 * in implicit-AND form.
19959 */
19960bool
19962 List *partConstraint)
19963{
19964 List *existConstraint = NIL;
19965 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19966 int i;
19967
19968 if (constr && constr->has_not_null)
19969 {
19970 int natts = scanrel->rd_att->natts;
19971
19972 for (i = 1; i <= natts; i++)
19973 {
19974 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
19975
19976 /* invalid not-null constraint must be ignored here */
19977 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
19978 {
19979 Form_pg_attribute wholeatt = TupleDescAttr(scanrel->rd_att, i - 1);
19980 NullTest *ntest = makeNode(NullTest);
19981
19982 ntest->arg = (Expr *) makeVar(1,
19983 i,
19984 wholeatt->atttypid,
19985 wholeatt->atttypmod,
19986 wholeatt->attcollation,
19987 0);
19988 ntest->nulltesttype = IS_NOT_NULL;
19989
19990 /*
19991 * argisrow=false is correct even for a composite column,
19992 * because attnotnull does not represent a SQL-spec IS NOT
19993 * NULL test in such a case, just IS DISTINCT FROM NULL.
19994 */
19995 ntest->argisrow = false;
19996 ntest->location = -1;
19997 existConstraint = lappend(existConstraint, ntest);
19998 }
19999 }
20000 }
20001
20002 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
20003}
20004
20005/*
20006 * ConstraintImpliedByRelConstraint
20007 * Do scanrel's existing constraints imply the given constraint?
20008 *
20009 * testConstraint is the constraint to validate. provenConstraint is a
20010 * caller-provided list of conditions which this function may assume
20011 * to be true. Both provenConstraint and testConstraint must be in
20012 * implicit-AND form, must only contain immutable clauses, and must
20013 * contain only Vars with varno = 1.
20014 */
20015bool
20016ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
20017{
20018 List *existConstraint = list_copy(provenConstraint);
20019 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20020 int num_check,
20021 i;
20022
20023 num_check = (constr != NULL) ? constr->num_check : 0;
20024 for (i = 0; i < num_check; i++)
20025 {
20026 Node *cexpr;
20027
20028 /*
20029 * If this constraint hasn't been fully validated yet, we must ignore
20030 * it here.
20031 */
20032 if (!constr->check[i].ccvalid)
20033 continue;
20034
20035 /*
20036 * NOT ENFORCED constraints are always marked as invalid, which should
20037 * have been ignored.
20038 */
20039 Assert(constr->check[i].ccenforced);
20040
20041 cexpr = stringToNode(constr->check[i].ccbin);
20042
20043 /*
20044 * Run each expression through const-simplification and
20045 * canonicalization. It is necessary, because we will be comparing it
20046 * to similarly-processed partition constraint expressions, and may
20047 * fail to detect valid matches without this.
20048 */
20049 cexpr = eval_const_expressions(NULL, cexpr);
20050 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20051
20052 existConstraint = list_concat(existConstraint,
20053 make_ands_implicit((Expr *) cexpr));
20054 }
20055
20056 /*
20057 * Try to make the proof. Since we are comparing CHECK constraints, we
20058 * need to use weak implication, i.e., we assume existConstraint is
20059 * not-false and try to prove the same for testConstraint.
20060 *
20061 * Note that predicate_implied_by assumes its first argument is known
20062 * immutable. That should always be true for both NOT NULL and partition
20063 * constraints, so we don't test it here.
20064 */
20065 return predicate_implied_by(testConstraint, existConstraint, true);
20066}
20067
20068/*
20069 * QueuePartitionConstraintValidation
20070 *
20071 * Add an entry to wqueue to have the given partition constraint validated by
20072 * Phase 3, for the given relation, and all its children.
20073 *
20074 * We first verify whether the given constraint is implied by pre-existing
20075 * relation constraints; if it is, there's no need to scan the table to
20076 * validate, so don't queue in that case.
20077 */
20078static void
20080 List *partConstraint,
20081 bool validate_default)
20082{
20083 /*
20084 * Based on the table's existing constraints, determine whether or not we
20085 * may skip scanning the table.
20086 */
20087 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
20088 {
20089 if (!validate_default)
20091 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20092 RelationGetRelationName(scanrel))));
20093 else
20095 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20096 RelationGetRelationName(scanrel))));
20097 return;
20098 }
20099
20100 /*
20101 * Constraints proved insufficient. For plain relations, queue a
20102 * validation item now; for partitioned tables, recurse to process each
20103 * partition.
20104 */
20105 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20106 {
20107 AlteredTableInfo *tab;
20108
20109 /* Grab a work queue entry. */
20110 tab = ATGetQueueEntry(wqueue, scanrel);
20111 Assert(tab->partition_constraint == NULL);
20112 tab->partition_constraint = (Expr *) linitial(partConstraint);
20113 tab->validate_default = validate_default;
20114 }
20115 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20116 {
20117 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20118 int i;
20119
20120 for (i = 0; i < partdesc->nparts; i++)
20121 {
20122 Relation part_rel;
20123 List *thisPartConstraint;
20124
20125 /*
20126 * This is the minimum lock we need to prevent deadlocks.
20127 */
20128 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
20129
20130 /*
20131 * Adjust the constraint for scanrel so that it matches this
20132 * partition's attribute numbers.
20133 */
20134 thisPartConstraint =
20135 map_partition_varattnos(partConstraint, 1,
20136 part_rel, scanrel);
20137
20138 QueuePartitionConstraintValidation(wqueue, part_rel,
20139 thisPartConstraint,
20140 validate_default);
20141 table_close(part_rel, NoLock); /* keep lock till commit */
20142 }
20143 }
20144}
20145
20146/*
20147 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
20148 *
20149 * Return the address of the newly attached partition.
20150 */
20151static ObjectAddress
20153 AlterTableUtilityContext *context)
20154{
20155 Relation attachrel,
20156 catalog;
20157 List *attachrel_children;
20158 List *partConstraint;
20159 SysScanDesc scan;
20160 ScanKeyData skey;
20161 AttrNumber attno;
20162 int natts;
20163 TupleDesc tupleDesc;
20164 ObjectAddress address;
20165 const char *trigger_name;
20166 Oid defaultPartOid;
20167 List *partBoundConstraint;
20168 ParseState *pstate = make_parsestate(NULL);
20169
20170 pstate->p_sourcetext = context->queryString;
20171
20172 /*
20173 * We must lock the default partition if one exists, because attaching a
20174 * new partition will change its partition constraint.
20175 */
20176 defaultPartOid =
20178 if (OidIsValid(defaultPartOid))
20179 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20180
20181 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
20182
20183 /*
20184 * XXX I think it'd be a good idea to grab locks on all tables referenced
20185 * by FKs at this point also.
20186 */
20187
20188 /*
20189 * Must be owner of both parent and source table -- parent was checked by
20190 * ATSimplePermissions call in ATPrepCmd
20191 */
20194
20195 /* A partition can only have one parent */
20196 if (attachrel->rd_rel->relispartition)
20197 ereport(ERROR,
20198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20199 errmsg("\"%s\" is already a partition",
20200 RelationGetRelationName(attachrel))));
20201
20202 if (OidIsValid(attachrel->rd_rel->reloftype))
20203 ereport(ERROR,
20204 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20205 errmsg("cannot attach a typed table as partition")));
20206
20207 /*
20208 * Table being attached should not already be part of inheritance; either
20209 * as a child table...
20210 */
20211 catalog = table_open(InheritsRelationId, AccessShareLock);
20212 ScanKeyInit(&skey,
20213 Anum_pg_inherits_inhrelid,
20214 BTEqualStrategyNumber, F_OIDEQ,
20216 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
20217 NULL, 1, &skey);
20219 ereport(ERROR,
20220 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20221 errmsg("cannot attach inheritance child as partition")));
20222 systable_endscan(scan);
20223
20224 /* ...or as a parent table (except the case when it is partitioned) */
20225 ScanKeyInit(&skey,
20226 Anum_pg_inherits_inhparent,
20227 BTEqualStrategyNumber, F_OIDEQ,
20229 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
20230 1, &skey);
20232 attachrel->rd_rel->relkind == RELKIND_RELATION)
20233 ereport(ERROR,
20234 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20235 errmsg("cannot attach inheritance parent as partition")));
20236 systable_endscan(scan);
20237 table_close(catalog, AccessShareLock);
20238
20239 /*
20240 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20241 * particular, this disallows making a rel a partition of itself.)
20242 *
20243 * We do that by checking if rel is a member of the list of attachrel's
20244 * partitions provided the latter is partitioned at all. We want to avoid
20245 * having to construct this list again, so we request the strongest lock
20246 * on all partitions. We need the strongest lock, because we may decide
20247 * to scan them if we find out that the table being attached (or its leaf
20248 * partitions) may contain rows that violate the partition constraint. If
20249 * the table has a constraint that would prevent such rows, which by
20250 * definition is present in all the partitions, we need not scan the
20251 * table, nor its partitions. But we cannot risk a deadlock by taking a
20252 * weaker lock now and the stronger one only when needed.
20253 */
20254 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
20255 AccessExclusiveLock, NULL);
20256 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
20257 ereport(ERROR,
20258 (errcode(ERRCODE_DUPLICATE_TABLE),
20259 errmsg("circular inheritance not allowed"),
20260 errdetail("\"%s\" is already a child of \"%s\".",
20262 RelationGetRelationName(attachrel))));
20263
20264 /* If the parent is permanent, so must be all of its partitions. */
20265 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20266 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20267 ereport(ERROR,
20268 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20269 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20271
20272 /* Temp parent cannot have a partition that is itself not a temp */
20273 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20274 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20275 ereport(ERROR,
20276 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20277 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20279
20280 /* If the parent is temp, it must belong to this session */
20281 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20282 !rel->rd_islocaltemp)
20283 ereport(ERROR,
20284 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20285 errmsg("cannot attach as partition of temporary relation of another session")));
20286
20287 /* Ditto for the partition */
20288 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20289 !attachrel->rd_islocaltemp)
20290 ereport(ERROR,
20291 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20292 errmsg("cannot attach temporary relation of another session as partition")));
20293
20294 /*
20295 * Check if attachrel has any identity columns or any columns that aren't
20296 * in the parent.
20297 */
20298 tupleDesc = RelationGetDescr(attachrel);
20299 natts = tupleDesc->natts;
20300 for (attno = 1; attno <= natts; attno++)
20301 {
20302 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20303 char *attributeName = NameStr(attribute->attname);
20304
20305 /* Ignore dropped */
20306 if (attribute->attisdropped)
20307 continue;
20308
20309 if (attribute->attidentity)
20310 ereport(ERROR,
20311 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20312 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20313 RelationGetRelationName(attachrel), attributeName),
20314 errdetail("The new partition may not contain an identity column."));
20315
20316 /* Try to find the column in parent (matching on column name) */
20317 if (!SearchSysCacheExists2(ATTNAME,
20319 CStringGetDatum(attributeName)))
20320 ereport(ERROR,
20321 (errcode(ERRCODE_DATATYPE_MISMATCH),
20322 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20323 RelationGetRelationName(attachrel), attributeName,
20325 errdetail("The new partition may contain only the columns present in parent.")));
20326 }
20327
20328 /*
20329 * If child_rel has row-level triggers with transition tables, we
20330 * currently don't allow it to become a partition. See also prohibitions
20331 * in ATExecAddInherit() and CreateTrigger().
20332 */
20333 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
20334 if (trigger_name != NULL)
20335 ereport(ERROR,
20336 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20337 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20338 trigger_name, RelationGetRelationName(attachrel)),
20339 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20340
20341 /*
20342 * Check that the new partition's bound is valid and does not overlap any
20343 * of existing partitions of the parent - note that it does not return on
20344 * error.
20345 */
20347 cmd->bound, pstate);
20348
20349 /* OK to create inheritance. Rest of the checks performed there */
20350 CreateInheritance(attachrel, rel, true);
20351
20352 /* Update the pg_class entry. */
20353 StorePartitionBound(attachrel, rel, cmd->bound);
20354
20355 /* Ensure there exists a correct set of indexes in the partition. */
20356 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
20357
20358 /* and triggers */
20359 CloneRowTriggersToPartition(rel, attachrel);
20360
20361 /*
20362 * Clone foreign key constraints. Callee is responsible for setting up
20363 * for phase 3 constraint verification.
20364 */
20365 CloneForeignKeyConstraints(wqueue, rel, attachrel);
20366
20367 /*
20368 * Generate partition constraint from the partition bound specification.
20369 * If the parent itself is a partition, make sure to include its
20370 * constraint as well.
20371 */
20372 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
20373
20374 /*
20375 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20376 * since it's needed later to construct the constraint expression for
20377 * validating against the default partition, if any.
20378 */
20379 partConstraint = list_concat_copy(partBoundConstraint,
20381
20382 /* Skip validation if there are no constraints to validate. */
20383 if (partConstraint)
20384 {
20385 /*
20386 * Run the partition quals through const-simplification similar to
20387 * check constraints. We skip canonicalize_qual, though, because
20388 * partition quals should be in canonical form already.
20389 */
20390 partConstraint =
20392 (Node *) partConstraint);
20393
20394 /* XXX this sure looks wrong */
20395 partConstraint = list_make1(make_ands_explicit(partConstraint));
20396
20397 /*
20398 * Adjust the generated constraint to match this partition's attribute
20399 * numbers.
20400 */
20401 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
20402 rel);
20403
20404 /* Validate partition constraints against the table being attached. */
20405 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
20406 false);
20407 }
20408
20409 /*
20410 * If we're attaching a partition other than the default partition and a
20411 * default one exists, then that partition's partition constraint changes,
20412 * so add an entry to the work queue to validate it, too. (We must not do
20413 * this when the partition being attached is the default one; we already
20414 * did it above!)
20415 */
20416 if (OidIsValid(defaultPartOid))
20417 {
20418 Relation defaultrel;
20419 List *defPartConstraint;
20420
20421 Assert(!cmd->bound->is_default);
20422
20423 /* we already hold a lock on the default partition */
20424 defaultrel = table_open(defaultPartOid, NoLock);
20425 defPartConstraint =
20426 get_proposed_default_constraint(partBoundConstraint);
20427
20428 /*
20429 * Map the Vars in the constraint expression from rel's attnos to
20430 * defaultrel's.
20431 */
20432 defPartConstraint =
20433 map_partition_varattnos(defPartConstraint,
20434 1, defaultrel, rel);
20435 QueuePartitionConstraintValidation(wqueue, defaultrel,
20436 defPartConstraint, true);
20437
20438 /* keep our lock until commit. */
20439 table_close(defaultrel, NoLock);
20440 }
20441
20442 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
20443
20444 /*
20445 * If the partition we just attached is partitioned itself, invalidate
20446 * relcache for all descendent partitions too to ensure that their
20447 * rd_partcheck expression trees are rebuilt; partitions already locked at
20448 * the beginning of this function.
20449 */
20450 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20451 {
20452 ListCell *l;
20453
20454 foreach(l, attachrel_children)
20455 {
20457 }
20458 }
20459
20460 /* keep our lock until commit */
20461 table_close(attachrel, NoLock);
20462
20463 return address;
20464}
20465
20466/*
20467 * AttachPartitionEnsureIndexes
20468 * subroutine for ATExecAttachPartition to create/match indexes
20469 *
20470 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
20471 * PARTITION: every partition must have an index attached to each index on the
20472 * partitioned table.
20473 */
20474static void
20476{
20477 List *idxes;
20478 List *attachRelIdxs;
20479 Relation *attachrelIdxRels;
20480 IndexInfo **attachInfos;
20481 ListCell *cell;
20482 MemoryContext cxt;
20483 MemoryContext oldcxt;
20484
20486 "AttachPartitionEnsureIndexes",
20488 oldcxt = MemoryContextSwitchTo(cxt);
20489
20490 idxes = RelationGetIndexList(rel);
20491 attachRelIdxs = RelationGetIndexList(attachrel);
20492 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
20493 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
20494
20495 /* Build arrays of all existing indexes and their IndexInfos */
20496 foreach_oid(cldIdxId, attachRelIdxs)
20497 {
20498 int i = foreach_current_index(cldIdxId);
20499
20500 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
20501 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
20502 }
20503
20504 /*
20505 * If we're attaching a foreign table, we must fail if any of the indexes
20506 * is a constraint index; otherwise, there's nothing to do here. Do this
20507 * before starting work, to avoid wasting the effort of building a few
20508 * non-unique indexes before coming across a unique one.
20509 */
20510 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20511 {
20512 foreach(cell, idxes)
20513 {
20514 Oid idx = lfirst_oid(cell);
20516
20517 if (idxRel->rd_index->indisunique ||
20518 idxRel->rd_index->indisprimary)
20519 ereport(ERROR,
20520 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20521 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20522 RelationGetRelationName(attachrel),
20524 errdetail("Partitioned table \"%s\" contains unique indexes.",
20527 }
20528
20529 goto out;
20530 }
20531
20532 /*
20533 * For each index on the partitioned table, find a matching one in the
20534 * partition-to-be; if one is not found, create one.
20535 */
20536 foreach(cell, idxes)
20537 {
20538 Oid idx = lfirst_oid(cell);
20540 IndexInfo *info;
20541 AttrMap *attmap;
20542 bool found = false;
20543 Oid constraintOid;
20544
20545 /*
20546 * Ignore indexes in the partitioned table other than partitioned
20547 * indexes.
20548 */
20549 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20550 {
20552 continue;
20553 }
20554
20555 /* construct an indexinfo to compare existing indexes against */
20556 info = BuildIndexInfo(idxRel);
20557 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
20558 RelationGetDescr(rel),
20559 false);
20561
20562 /*
20563 * Scan the list of existing indexes in the partition-to-be, and mark
20564 * the first matching, valid, unattached one we find, if any, as
20565 * partition of the parent index. If we find one, we're done.
20566 */
20567 for (int i = 0; i < list_length(attachRelIdxs); i++)
20568 {
20569 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
20570 Oid cldConstrOid = InvalidOid;
20571
20572 /* does this index have a parent? if so, can't use it */
20573 if (attachrelIdxRels[i]->rd_rel->relispartition)
20574 continue;
20575
20576 /* If this index is invalid, can't use it */
20577 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20578 continue;
20579
20580 if (CompareIndexInfo(attachInfos[i], info,
20581 attachrelIdxRels[i]->rd_indcollation,
20582 idxRel->rd_indcollation,
20583 attachrelIdxRels[i]->rd_opfamily,
20584 idxRel->rd_opfamily,
20585 attmap))
20586 {
20587 /*
20588 * If this index is being created in the parent because of a
20589 * constraint, then the child needs to have a constraint also,
20590 * so look for one. If there is no such constraint, this
20591 * index is no good, so keep looking.
20592 */
20593 if (OidIsValid(constraintOid))
20594 {
20595 cldConstrOid =
20597 cldIdxId);
20598 /* no dice */
20599 if (!OidIsValid(cldConstrOid))
20600 continue;
20601
20602 /* Ensure they're both the same type of constraint */
20603 if (get_constraint_type(constraintOid) !=
20604 get_constraint_type(cldConstrOid))
20605 continue;
20606 }
20607
20608 /* bingo. */
20609 IndexSetParentIndex(attachrelIdxRels[i], idx);
20610 if (OidIsValid(constraintOid))
20611 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
20612 RelationGetRelid(attachrel));
20613 found = true;
20614
20616 break;
20617 }
20618 }
20619
20620 /*
20621 * If no suitable index was found in the partition-to-be, create one
20622 * now. Note that if this is a PK, not-null constraints must already
20623 * exist.
20624 */
20625 if (!found)
20626 {
20627 IndexStmt *stmt;
20628 Oid conOid;
20629
20631 idxRel, attmap,
20632 &conOid);
20634 RelationGetRelid(idxRel),
20635 conOid,
20636 -1,
20637 true, false, false, false, false);
20638 }
20639
20641 }
20642
20643out:
20644 /* Clean up. */
20645 for (int i = 0; i < list_length(attachRelIdxs); i++)
20646 index_close(attachrelIdxRels[i], AccessShareLock);
20647 MemoryContextSwitchTo(oldcxt);
20649}
20650
20651/*
20652 * CloneRowTriggersToPartition
20653 * subroutine for ATExecAttachPartition/DefineRelation to create row
20654 * triggers on partitions
20655 */
20656static void
20658{
20659 Relation pg_trigger;
20661 SysScanDesc scan;
20662 HeapTuple tuple;
20663 MemoryContext perTupCxt;
20664
20665 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20666 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
20667 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
20668 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
20669 true, NULL, 1, &key);
20670
20672 "clone trig", ALLOCSET_SMALL_SIZES);
20673
20674 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20675 {
20676 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
20677 CreateTrigStmt *trigStmt;
20678 Node *qual = NULL;
20679 Datum value;
20680 bool isnull;
20681 List *cols = NIL;
20682 List *trigargs = NIL;
20683 MemoryContext oldcxt;
20684
20685 /*
20686 * Ignore statement-level triggers; those are not cloned.
20687 */
20688 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20689 continue;
20690
20691 /*
20692 * Don't clone internal triggers, because the constraint cloning code
20693 * will.
20694 */
20695 if (trigForm->tgisinternal)
20696 continue;
20697
20698 /*
20699 * Complain if we find an unexpected trigger type.
20700 */
20701 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20702 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20703 elog(ERROR, "unexpected trigger \"%s\" found",
20704 NameStr(trigForm->tgname));
20705
20706 /* Use short-lived context for CREATE TRIGGER */
20707 oldcxt = MemoryContextSwitchTo(perTupCxt);
20708
20709 /*
20710 * If there is a WHEN clause, generate a 'cooked' version of it that's
20711 * appropriate for the partition.
20712 */
20713 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
20714 RelationGetDescr(pg_trigger), &isnull);
20715 if (!isnull)
20716 {
20718 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20719 partition, parent);
20720 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20721 partition, parent);
20722 }
20723
20724 /*
20725 * If there is a column list, transform it to a list of column names.
20726 * Note we don't need to map this list in any way ...
20727 */
20728 if (trigForm->tgattr.dim1 > 0)
20729 {
20730 int i;
20731
20732 for (i = 0; i < trigForm->tgattr.dim1; i++)
20733 {
20735
20736 col = TupleDescAttr(parent->rd_att,
20737 trigForm->tgattr.values[i] - 1);
20738 cols = lappend(cols,
20739 makeString(pstrdup(NameStr(col->attname))));
20740 }
20741 }
20742
20743 /* Reconstruct trigger arguments list. */
20744 if (trigForm->tgnargs > 0)
20745 {
20746 char *p;
20747
20748 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
20749 RelationGetDescr(pg_trigger), &isnull);
20750 if (isnull)
20751 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20752 NameStr(trigForm->tgname), RelationGetRelationName(partition));
20753
20754 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20755
20756 for (int i = 0; i < trigForm->tgnargs; i++)
20757 {
20758 trigargs = lappend(trigargs, makeString(pstrdup(p)));
20759 p += strlen(p) + 1;
20760 }
20761 }
20762
20763 trigStmt = makeNode(CreateTrigStmt);
20764 trigStmt->replace = false;
20765 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20766 trigStmt->trigname = NameStr(trigForm->tgname);
20767 trigStmt->relation = NULL;
20768 trigStmt->funcname = NULL; /* passed separately */
20769 trigStmt->args = trigargs;
20770 trigStmt->row = true;
20771 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20772 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20773 trigStmt->columns = cols;
20774 trigStmt->whenClause = NULL; /* passed separately */
20775 trigStmt->transitionRels = NIL; /* not supported at present */
20776 trigStmt->deferrable = trigForm->tgdeferrable;
20777 trigStmt->initdeferred = trigForm->tginitdeferred;
20778 trigStmt->constrrel = NULL; /* passed separately */
20779
20780 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
20781 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20782 trigForm->tgfoid, trigForm->oid, qual,
20783 false, true, trigForm->tgenabled);
20784
20785 MemoryContextSwitchTo(oldcxt);
20786 MemoryContextReset(perTupCxt);
20787 }
20788
20789 MemoryContextDelete(perTupCxt);
20790
20791 systable_endscan(scan);
20792 table_close(pg_trigger, RowExclusiveLock);
20793}
20794
20795/*
20796 * ALTER TABLE DETACH PARTITION
20797 *
20798 * Return the address of the relation that is no longer a partition of rel.
20799 *
20800 * If concurrent mode is requested, we run in two transactions. A side-
20801 * effect is that this command cannot run in a multi-part ALTER TABLE.
20802 * Currently, that's enforced by the grammar.
20803 *
20804 * The strategy for concurrency is to first modify the partition's
20805 * pg_inherit catalog row to make it visible to everyone that the
20806 * partition is detached, lock the partition against writes, and commit
20807 * the transaction; anyone who requests the partition descriptor from
20808 * that point onwards has to ignore such a partition. In a second
20809 * transaction, we wait until all transactions that could have seen the
20810 * partition as attached are gone, then we remove the rest of partition
20811 * metadata (pg_inherits and pg_class.relpartbounds).
20812 */
20813static ObjectAddress
20815 RangeVar *name, bool concurrent)
20816{
20817 Relation partRel;
20818 ObjectAddress address;
20819 Oid defaultPartOid;
20820
20821 /*
20822 * We must lock the default partition, because detaching this partition
20823 * will change its partition constraint.
20824 */
20825 defaultPartOid =
20827 if (OidIsValid(defaultPartOid))
20828 {
20829 /*
20830 * Concurrent detaching when a default partition exists is not
20831 * supported. The main problem is that the default partition
20832 * constraint would change. And there's a definitional problem: what
20833 * should happen to the tuples that are being inserted that belong to
20834 * the partition being detached? Putting them on the partition being
20835 * detached would be wrong, since they'd become "lost" after the
20836 * detaching completes but we cannot put them in the default partition
20837 * either until we alter its partition constraint.
20838 *
20839 * I think we could solve this problem if we effected the constraint
20840 * change before committing the first transaction. But the lock would
20841 * have to remain AEL and it would cause concurrent query planning to
20842 * be blocked, so changing it that way would be even worse.
20843 */
20844 if (concurrent)
20845 ereport(ERROR,
20846 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20847 errmsg("cannot detach partitions concurrently when a default partition exists")));
20848 LockRelationOid(defaultPartOid, AccessExclusiveLock);
20849 }
20850
20851 /*
20852 * In concurrent mode, the partition is locked with share-update-exclusive
20853 * in the first transaction. This allows concurrent transactions to be
20854 * doing DML to the partition.
20855 */
20856 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
20858
20859 /*
20860 * Check inheritance conditions and either delete the pg_inherits row (in
20861 * non-concurrent mode) or just set the inhdetachpending flag.
20862 */
20863 if (!concurrent)
20864 RemoveInheritance(partRel, rel, false);
20865 else
20866 MarkInheritDetached(partRel, rel);
20867
20868 /*
20869 * Ensure that foreign keys still hold after this detach. This keeps
20870 * locks on the referencing tables, which prevents concurrent transactions
20871 * from adding rows that we wouldn't see. For this to work in concurrent
20872 * mode, it is critical that the partition appears as no longer attached
20873 * for the RI queries as soon as the first transaction commits.
20874 */
20876
20877 /*
20878 * Concurrent mode has to work harder; first we add a new constraint to
20879 * the partition that matches the partition constraint. Then we close our
20880 * existing transaction, and in a new one wait for all processes to catch
20881 * up on the catalog updates we've done so far; at that point we can
20882 * complete the operation.
20883 */
20884 if (concurrent)
20885 {
20886 Oid partrelid,
20887 parentrelid;
20888 LOCKTAG tag;
20889 char *parentrelname;
20890 char *partrelname;
20891
20892 /*
20893 * Add a new constraint to the partition being detached, which
20894 * supplants the partition constraint (unless there is one already).
20895 */
20896 DetachAddConstraintIfNeeded(wqueue, partRel);
20897
20898 /*
20899 * We're almost done now; the only traces that remain are the
20900 * pg_inherits tuple and the partition's relpartbounds. Before we can
20901 * remove those, we need to wait until all transactions that know that
20902 * this is a partition are gone.
20903 */
20904
20905 /*
20906 * Remember relation OIDs to re-acquire them later; and relation names
20907 * too, for error messages if something is dropped in between.
20908 */
20909 partrelid = RelationGetRelid(partRel);
20910 parentrelid = RelationGetRelid(rel);
20911 parentrelname = MemoryContextStrdup(PortalContext,
20913 partrelname = MemoryContextStrdup(PortalContext,
20914 RelationGetRelationName(partRel));
20915
20916 /* Invalidate relcache entries for the parent -- must be before close */
20918
20919 table_close(partRel, NoLock);
20920 table_close(rel, NoLock);
20921 tab->rel = NULL;
20922
20923 /* Make updated catalog entry visible */
20926
20928
20929 /*
20930 * Now wait. This ensures that all queries that were planned
20931 * including the partition are finished before we remove the rest of
20932 * catalog entries. We don't need or indeed want to acquire this
20933 * lock, though -- that would block later queries.
20934 *
20935 * We don't need to concern ourselves with waiting for a lock on the
20936 * partition itself, since we will acquire AccessExclusiveLock below.
20937 */
20938 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20940
20941 /*
20942 * Now acquire locks in both relations again. Note they may have been
20943 * removed in the meantime, so care is required.
20944 */
20945 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20946 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20947
20948 /* If the relations aren't there, something bad happened; bail out */
20949 if (rel == NULL)
20950 {
20951 if (partRel != NULL) /* shouldn't happen */
20952 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20953 partrelname);
20954 ereport(ERROR,
20955 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20956 errmsg("partitioned table \"%s\" was removed concurrently",
20957 parentrelname)));
20958 }
20959 if (partRel == NULL)
20960 ereport(ERROR,
20961 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20962 errmsg("partition \"%s\" was removed concurrently", partrelname)));
20963
20964 tab->rel = rel;
20965 }
20966
20967 /* Do the final part of detaching */
20968 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20969
20970 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20971
20972 /* keep our lock until commit */
20973 table_close(partRel, NoLock);
20974
20975 return address;
20976}
20977
20978/*
20979 * Second part of ALTER TABLE .. DETACH.
20980 *
20981 * This is separate so that it can be run independently when the second
20982 * transaction of the concurrent algorithm fails (crash or abort).
20983 */
20984static void
20985DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20986 Oid defaultPartOid)
20987{
20988 Relation classRel;
20989 List *fks;
20990 ListCell *cell;
20991 List *indexes;
20992 Datum new_val[Natts_pg_class];
20993 bool new_null[Natts_pg_class],
20994 new_repl[Natts_pg_class];
20995 HeapTuple tuple,
20996 newtuple;
20997 Relation trigrel = NULL;
20998 List *fkoids = NIL;
20999
21000 if (concurrent)
21001 {
21002 /*
21003 * We can remove the pg_inherits row now. (In the non-concurrent case,
21004 * this was already done).
21005 */
21006 RemoveInheritance(partRel, rel, true);
21007 }
21008
21009 /* Drop any triggers that were cloned on creation/attach. */
21011
21012 /*
21013 * Detach any foreign keys that are inherited. This includes creating
21014 * additional action triggers.
21015 */
21016 fks = copyObject(RelationGetFKeyList(partRel));
21017 if (fks != NIL)
21018 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
21019
21020 /*
21021 * It's possible that the partition being detached has a foreign key that
21022 * references a partitioned table. When that happens, there are multiple
21023 * pg_constraint rows for the partition: one points to the partitioned
21024 * table itself, while the others point to each of its partitions. Only
21025 * the topmost one is to be considered here; the child constraints must be
21026 * left alone, because conceptually those aren't coming from our parent
21027 * partitioned table, but from this partition itself.
21028 *
21029 * We implement this by collecting all the constraint OIDs in a first scan
21030 * of the FK array, and skipping in the loop below those constraints whose
21031 * parents are listed here.
21032 */
21034 fkoids = lappend_oid(fkoids, fk->conoid);
21035
21036 foreach(cell, fks)
21037 {
21038 ForeignKeyCacheInfo *fk = lfirst(cell);
21039 HeapTuple contup;
21040 Form_pg_constraint conform;
21041
21042 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
21043 if (!HeapTupleIsValid(contup))
21044 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21045 conform = (Form_pg_constraint) GETSTRUCT(contup);
21046
21047 /*
21048 * Consider only inherited foreign keys, and only if their parents
21049 * aren't in the list.
21050 */
21051 if (conform->contype != CONSTRAINT_FOREIGN ||
21052 !OidIsValid(conform->conparentid) ||
21053 list_member_oid(fkoids, conform->conparentid))
21054 {
21055 ReleaseSysCache(contup);
21056 continue;
21057 }
21058
21059 /*
21060 * The constraint on this table must be marked no longer a child of
21061 * the parent's constraint, as do its check triggers.
21062 */
21064
21065 /*
21066 * Also, look up the partition's "check" triggers corresponding to the
21067 * ENFORCED constraint being detached and detach them from the parent
21068 * triggers. NOT ENFORCED constraints do not have these triggers;
21069 * therefore, this step is not needed.
21070 */
21071 if (fk->conenforced)
21072 {
21073 Oid insertTriggerOid,
21074 updateTriggerOid;
21075
21077 fk->conoid, fk->confrelid, fk->conrelid,
21078 &insertTriggerOid, &updateTriggerOid);
21079 Assert(OidIsValid(insertTriggerOid));
21080 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
21081 RelationGetRelid(partRel));
21082 Assert(OidIsValid(updateTriggerOid));
21083 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
21084 RelationGetRelid(partRel));
21085 }
21086
21087 /*
21088 * Lastly, create the action triggers on the referenced table, using
21089 * addFkRecurseReferenced, which requires some elaborate setup (so put
21090 * it in a separate block). While at it, if the table is partitioned,
21091 * that function will recurse to create the pg_constraint rows and
21092 * action triggers for each partition.
21093 *
21094 * Note there's no need to do addFkConstraint() here, because the
21095 * pg_constraint row already exists.
21096 */
21097 {
21098 Constraint *fkconstraint;
21099 int numfks;
21100 AttrNumber conkey[INDEX_MAX_KEYS];
21101 AttrNumber confkey[INDEX_MAX_KEYS];
21102 Oid conpfeqop[INDEX_MAX_KEYS];
21103 Oid conppeqop[INDEX_MAX_KEYS];
21104 Oid conffeqop[INDEX_MAX_KEYS];
21105 int numfkdelsetcols;
21106 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21107 Relation refdRel;
21108
21110 &numfks,
21111 conkey,
21112 confkey,
21113 conpfeqop,
21114 conppeqop,
21115 conffeqop,
21116 &numfkdelsetcols,
21117 confdelsetcols);
21118
21119 /* Create a synthetic node we'll use throughout */
21120 fkconstraint = makeNode(Constraint);
21121 fkconstraint->contype = CONSTRAINT_FOREIGN;
21122 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21123 fkconstraint->deferrable = conform->condeferrable;
21124 fkconstraint->initdeferred = conform->condeferred;
21125 fkconstraint->is_enforced = conform->conenforced;
21126 fkconstraint->skip_validation = true;
21127 fkconstraint->initially_valid = conform->convalidated;
21128 /* a few irrelevant fields omitted here */
21129 fkconstraint->pktable = NULL;
21130 fkconstraint->fk_attrs = NIL;
21131 fkconstraint->pk_attrs = NIL;
21132 fkconstraint->fk_matchtype = conform->confmatchtype;
21133 fkconstraint->fk_upd_action = conform->confupdtype;
21134 fkconstraint->fk_del_action = conform->confdeltype;
21135 fkconstraint->fk_del_set_cols = NIL;
21136 fkconstraint->old_conpfeqop = NIL;
21137 fkconstraint->old_pktable_oid = InvalidOid;
21138 fkconstraint->location = -1;
21139
21140 /* set up colnames, used to generate the constraint name */
21141 for (int i = 0; i < numfks; i++)
21142 {
21144
21145 att = TupleDescAttr(RelationGetDescr(partRel),
21146 conkey[i] - 1);
21147
21148 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21149 makeString(NameStr(att->attname)));
21150 }
21151
21153
21154 addFkRecurseReferenced(fkconstraint, partRel,
21155 refdRel,
21156 conform->conindid,
21157 fk->conoid,
21158 numfks,
21159 confkey,
21160 conkey,
21161 conpfeqop,
21162 conppeqop,
21163 conffeqop,
21164 numfkdelsetcols,
21165 confdelsetcols,
21166 true,
21168 conform->conperiod);
21169 table_close(refdRel, NoLock); /* keep lock till end of xact */
21170 }
21171
21172 ReleaseSysCache(contup);
21173 }
21174 list_free_deep(fks);
21175 if (trigrel)
21176 table_close(trigrel, RowExclusiveLock);
21177
21178 /*
21179 * Any sub-constraints that are in the referenced-side of a larger
21180 * constraint have to be removed. This partition is no longer part of the
21181 * key space of the constraint.
21182 */
21183 foreach(cell, GetParentedForeignKeyRefs(partRel))
21184 {
21185 Oid constrOid = lfirst_oid(cell);
21186 ObjectAddress constraint;
21187
21189 deleteDependencyRecordsForClass(ConstraintRelationId,
21190 constrOid,
21191 ConstraintRelationId,
21194
21195 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
21196 performDeletion(&constraint, DROP_RESTRICT, 0);
21197 }
21198
21199 /* Now we can detach indexes */
21200 indexes = RelationGetIndexList(partRel);
21201 foreach(cell, indexes)
21202 {
21203 Oid idxid = lfirst_oid(cell);
21204 Oid parentidx;
21205 Relation idx;
21206 Oid constrOid;
21207 Oid parentConstrOid;
21208
21209 if (!has_superclass(idxid))
21210 continue;
21211
21212 parentidx = get_partition_parent(idxid, false);
21213 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21214
21217
21218 /*
21219 * If there's a constraint associated with the index, detach it too.
21220 * Careful: it is possible for a constraint index in a partition to be
21221 * the child of a non-constraint index, so verify whether the parent
21222 * index does actually have a constraint.
21223 */
21225 idxid);
21227 parentidx);
21228 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
21230
21232 }
21233
21234 /* Update pg_class tuple */
21235 classRel = table_open(RelationRelationId, RowExclusiveLock);
21236 tuple = SearchSysCacheCopy1(RELOID,
21238 if (!HeapTupleIsValid(tuple))
21239 elog(ERROR, "cache lookup failed for relation %u",
21240 RelationGetRelid(partRel));
21241 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
21242
21243 /* Clear relpartbound and reset relispartition */
21244 memset(new_val, 0, sizeof(new_val));
21245 memset(new_null, false, sizeof(new_null));
21246 memset(new_repl, false, sizeof(new_repl));
21247 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
21248 new_null[Anum_pg_class_relpartbound - 1] = true;
21249 new_repl[Anum_pg_class_relpartbound - 1] = true;
21250 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21251 new_val, new_null, new_repl);
21252
21253 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21254 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21255 heap_freetuple(newtuple);
21256 table_close(classRel, RowExclusiveLock);
21257
21258 /*
21259 * Drop identity property from all identity columns of partition.
21260 */
21261 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21262 {
21263 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21264
21265 if (!attr->attisdropped && attr->attidentity)
21266 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21267 AccessExclusiveLock, true, true);
21268 }
21269
21270 if (OidIsValid(defaultPartOid))
21271 {
21272 /*
21273 * If the relation being detached is the default partition itself,
21274 * remove it from the parent's pg_partitioned_table entry.
21275 *
21276 * If not, we must invalidate default partition's relcache entry, as
21277 * in StorePartitionBound: its partition constraint depends on every
21278 * other partition's partition constraint.
21279 */
21280 if (RelationGetRelid(partRel) == defaultPartOid)
21282 else
21283 CacheInvalidateRelcacheByRelid(defaultPartOid);
21284 }
21285
21286 /*
21287 * Invalidate the parent's relcache so that the partition is no longer
21288 * included in its partition descriptor.
21289 */
21291
21292 /*
21293 * If the partition we just detached is partitioned itself, invalidate
21294 * relcache for all descendent partitions too to ensure that their
21295 * rd_partcheck expression trees are rebuilt; must lock partitions before
21296 * doing so, using the same lockmode as what partRel has been locked with
21297 * by the caller.
21298 */
21299 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21300 {
21301 List *children;
21302
21303 children = find_all_inheritors(RelationGetRelid(partRel),
21304 AccessExclusiveLock, NULL);
21305 foreach(cell, children)
21306 {
21308 }
21309 }
21310}
21311
21312/*
21313 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
21314 *
21315 * To use when a DETACH PARTITION command previously did not run to
21316 * completion; this completes the detaching process.
21317 */
21318static ObjectAddress
21320{
21321 Relation partRel;
21322 ObjectAddress address;
21323 Snapshot snap = GetActiveSnapshot();
21324
21326
21327 /*
21328 * Wait until existing snapshots are gone. This is important if the
21329 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21330 * user could immediately run DETACH FINALIZE without actually waiting for
21331 * existing transactions. We must not complete the detach action until
21332 * all such queries are complete (otherwise we would present them with an
21333 * inconsistent view of catalogs).
21334 */
21335 WaitForOlderSnapshots(snap->xmin, false);
21336
21337 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21338
21339 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
21340
21341 table_close(partRel, NoLock);
21342
21343 return address;
21344}
21345
21346/*
21347 * DetachAddConstraintIfNeeded
21348 * Subroutine for ATExecDetachPartition. Create a constraint that
21349 * takes the place of the partition constraint, but avoid creating
21350 * a dupe if a constraint already exists which implies the needed
21351 * constraint.
21352 */
21353static void
21355{
21356 List *constraintExpr;
21357
21358 constraintExpr = RelationGetPartitionQual(partRel);
21359 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
21360
21361 /*
21362 * Avoid adding a new constraint if the needed constraint is implied by an
21363 * existing constraint
21364 */
21365 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
21366 {
21367 AlteredTableInfo *tab;
21368 Constraint *n;
21369
21370 tab = ATGetQueueEntry(wqueue, partRel);
21371
21372 /* Add constraint on partition, equivalent to the partition constraint */
21373 n = makeNode(Constraint);
21374 n->contype = CONSTR_CHECK;
21375 n->conname = NULL;
21376 n->location = -1;
21377 n->is_no_inherit = false;
21378 n->raw_expr = NULL;
21379 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
21380 n->is_enforced = true;
21381 n->initially_valid = true;
21382 n->skip_validation = true;
21383 /* It's a re-add, since it nominally already exists */
21384 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
21385 true, false, true, ShareUpdateExclusiveLock);
21386 }
21387}
21388
21389/*
21390 * DropClonedTriggersFromPartition
21391 * subroutine for ATExecDetachPartition to remove any triggers that were
21392 * cloned to the partition when it was created-as-partition or attached.
21393 * This undoes what CloneRowTriggersToPartition did.
21394 */
21395static void
21397{
21398 ScanKeyData skey;
21399 SysScanDesc scan;
21400 HeapTuple trigtup;
21401 Relation tgrel;
21402 ObjectAddresses *objects;
21403
21404 objects = new_object_addresses();
21405
21406 /*
21407 * Scan pg_trigger to search for all triggers on this rel.
21408 */
21409 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
21410 F_OIDEQ, ObjectIdGetDatum(partitionId));
21411 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
21412 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
21413 true, NULL, 1, &skey);
21414 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
21415 {
21416 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
21417 ObjectAddress trig;
21418
21419 /* Ignore triggers that weren't cloned */
21420 if (!OidIsValid(pg_trigger->tgparentid))
21421 continue;
21422
21423 /*
21424 * Ignore internal triggers that are implementation objects of foreign
21425 * keys, because these will be detached when the foreign keys
21426 * themselves are.
21427 */
21428 if (OidIsValid(pg_trigger->tgconstrrelid))
21429 continue;
21430
21431 /*
21432 * This is ugly, but necessary: remove the dependency markings on the
21433 * trigger so that it can be removed.
21434 */
21435 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21436 TriggerRelationId,
21438 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
21439 RelationRelationId,
21441
21442 /* remember this trigger to remove it below */
21443 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
21444 add_exact_object_address(&trig, objects);
21445 }
21446
21447 /* make the dependency removal visible to the deletion below */
21450
21451 /* done */
21452 free_object_addresses(objects);
21453 systable_endscan(scan);
21455}
21456
21457/*
21458 * Before acquiring lock on an index, acquire the same lock on the owning
21459 * table.
21460 */
21462{
21466};
21467
21468static void
21469RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
21470 void *arg)
21471{
21473 Form_pg_class classform;
21474 HeapTuple tuple;
21475
21476 state = (struct AttachIndexCallbackState *) arg;
21477
21478 if (!state->lockedParentTbl)
21479 {
21480 LockRelationOid(state->parentTblOid, AccessShareLock);
21481 state->lockedParentTbl = true;
21482 }
21483
21484 /*
21485 * If we previously locked some other heap, and the name we're looking up
21486 * no longer refers to an index on that relation, release the now-useless
21487 * lock. XXX maybe we should do *after* we verify whether the index does
21488 * not actually belong to the same relation ...
21489 */
21490 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21491 {
21492 UnlockRelationOid(state->partitionOid, AccessShareLock);
21493 state->partitionOid = InvalidOid;
21494 }
21495
21496 /* Didn't find a relation, so no need for locking or permission checks. */
21497 if (!OidIsValid(relOid))
21498 return;
21499
21500 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21501 if (!HeapTupleIsValid(tuple))
21502 return; /* concurrently dropped, so nothing to do */
21503 classform = (Form_pg_class) GETSTRUCT(tuple);
21504 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21505 classform->relkind != RELKIND_INDEX)
21506 ereport(ERROR,
21507 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21508 errmsg("\"%s\" is not an index", rv->relname)));
21509 ReleaseSysCache(tuple);
21510
21511 /*
21512 * Since we need only examine the heap's tupledesc, an access share lock
21513 * on it (preventing any DDL) is sufficient.
21514 */
21515 state->partitionOid = IndexGetRelation(relOid, false);
21516 LockRelationOid(state->partitionOid, AccessShareLock);
21517}
21518
21519/*
21520 * ALTER INDEX i1 ATTACH PARTITION i2
21521 */
21522static ObjectAddress
21524{
21525 Relation partIdx;
21526 Relation partTbl;
21527 Relation parentTbl;
21528 ObjectAddress address;
21529 Oid partIdxId;
21530 Oid currParent;
21532
21533 /*
21534 * We need to obtain lock on the index 'name' to modify it, but we also
21535 * need to read its owning table's tuple descriptor -- so we need to lock
21536 * both. To avoid deadlocks, obtain lock on the table before doing so on
21537 * the index. Furthermore, we need to examine the parent table of the
21538 * partition, so lock that one too.
21539 */
21540 state.partitionOid = InvalidOid;
21541 state.parentTblOid = parentIdx->rd_index->indrelid;
21542 state.lockedParentTbl = false;
21543 partIdxId =
21546 &state);
21547 /* Not there? */
21548 if (!OidIsValid(partIdxId))
21549 ereport(ERROR,
21550 (errcode(ERRCODE_UNDEFINED_OBJECT),
21551 errmsg("index \"%s\" does not exist", name->relname)));
21552
21553 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21554 partIdx = relation_open(partIdxId, AccessExclusiveLock);
21555
21556 /* we already hold locks on both tables, so this is safe: */
21557 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21558 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21559
21560 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
21561
21562 /* Silently do nothing if already in the right state */
21563 currParent = partIdx->rd_rel->relispartition ?
21564 get_partition_parent(partIdxId, false) : InvalidOid;
21565 if (currParent != RelationGetRelid(parentIdx))
21566 {
21567 IndexInfo *childInfo;
21568 IndexInfo *parentInfo;
21569 AttrMap *attmap;
21570 bool found;
21571 int i;
21572 PartitionDesc partDesc;
21573 Oid constraintOid,
21574 cldConstrId = InvalidOid;
21575
21576 /*
21577 * If this partition already has an index attached, refuse the
21578 * operation.
21579 */
21580 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
21581
21582 if (OidIsValid(currParent))
21583 ereport(ERROR,
21584 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21585 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21586 RelationGetRelationName(partIdx),
21587 RelationGetRelationName(parentIdx)),
21588 errdetail("Index \"%s\" is already attached to another index.",
21589 RelationGetRelationName(partIdx))));
21590
21591 /* Make sure it indexes a partition of the other index's table */
21592 partDesc = RelationGetPartitionDesc(parentTbl, true);
21593 found = false;
21594 for (i = 0; i < partDesc->nparts; i++)
21595 {
21596 if (partDesc->oids[i] == state.partitionOid)
21597 {
21598 found = true;
21599 break;
21600 }
21601 }
21602 if (!found)
21603 ereport(ERROR,
21604 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21605 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21606 RelationGetRelationName(partIdx),
21607 RelationGetRelationName(parentIdx)),
21608 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21609 RelationGetRelationName(partIdx),
21610 RelationGetRelationName(parentTbl))));
21611
21612 /* Ensure the indexes are compatible */
21613 childInfo = BuildIndexInfo(partIdx);
21614 parentInfo = BuildIndexInfo(parentIdx);
21615 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
21616 RelationGetDescr(parentTbl),
21617 false);
21618 if (!CompareIndexInfo(childInfo, parentInfo,
21619 partIdx->rd_indcollation,
21620 parentIdx->rd_indcollation,
21621 partIdx->rd_opfamily,
21622 parentIdx->rd_opfamily,
21623 attmap))
21624 ereport(ERROR,
21625 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21626 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21627 RelationGetRelationName(partIdx),
21628 RelationGetRelationName(parentIdx)),
21629 errdetail("The index definitions do not match.")));
21630
21631 /*
21632 * If there is a constraint in the parent, make sure there is one in
21633 * the child too.
21634 */
21635 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
21636 RelationGetRelid(parentIdx));
21637
21638 if (OidIsValid(constraintOid))
21639 {
21641 partIdxId);
21642 if (!OidIsValid(cldConstrId))
21643 ereport(ERROR,
21644 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
21645 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21646 RelationGetRelationName(partIdx),
21647 RelationGetRelationName(parentIdx)),
21648 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
21649 RelationGetRelationName(parentIdx),
21650 RelationGetRelationName(parentTbl),
21651 RelationGetRelationName(partIdx))));
21652 }
21653
21654 /*
21655 * If it's a primary key, make sure the columns in the partition are
21656 * NOT NULL.
21657 */
21658 if (parentIdx->rd_index->indisprimary)
21659 verifyPartitionIndexNotNull(childInfo, partTbl);
21660
21661 /* All good -- do it */
21662 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
21663 if (OidIsValid(constraintOid))
21664 ConstraintSetParentConstraint(cldConstrId, constraintOid,
21665 RelationGetRelid(partTbl));
21666
21667 free_attrmap(attmap);
21668
21669 validatePartitionedIndex(parentIdx, parentTbl);
21670 }
21671
21672 relation_close(parentTbl, AccessShareLock);
21673 /* keep these locks till commit */
21674 relation_close(partTbl, NoLock);
21675 relation_close(partIdx, NoLock);
21676
21677 return address;
21678}
21679
21680/*
21681 * Verify whether the given partition already contains an index attached
21682 * to the given partitioned index. If so, raise an error.
21683 */
21684static void
21685refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
21686{
21687 Oid existingIdx;
21688
21689 existingIdx = index_get_partition(partitionTbl,
21690 RelationGetRelid(parentIdx));
21691 if (OidIsValid(existingIdx))
21692 ereport(ERROR,
21693 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
21694 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21695 RelationGetRelationName(partIdx),
21696 RelationGetRelationName(parentIdx)),
21697 errdetail("Another index is already attached for partition \"%s\".",
21698 RelationGetRelationName(partitionTbl))));
21699}
21700
21701/*
21702 * Verify whether the set of attached partition indexes to a parent index on
21703 * a partitioned table is complete. If it is, mark the parent index valid.
21704 *
21705 * This should be called each time a partition index is attached.
21706 */
21707static void
21709{
21710 Relation inheritsRel;
21711 SysScanDesc scan;
21713 int tuples = 0;
21714 HeapTuple inhTup;
21715 bool updated = false;
21716
21717 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21718
21719 /*
21720 * Scan pg_inherits for this parent index. Count each valid index we find
21721 * (verifying the pg_index entry for each), and if we reach the total
21722 * amount we expect, we can mark this parent index as valid.
21723 */
21724 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
21725 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
21726 BTEqualStrategyNumber, F_OIDEQ,
21728 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
21729 NULL, 1, &key);
21730 while ((inhTup = systable_getnext(scan)) != NULL)
21731 {
21732 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
21733 HeapTuple indTup;
21734 Form_pg_index indexForm;
21735
21736 indTup = SearchSysCache1(INDEXRELID,
21737 ObjectIdGetDatum(inhForm->inhrelid));
21738 if (!HeapTupleIsValid(indTup))
21739 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21740 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21741 if (indexForm->indisvalid)
21742 tuples += 1;
21743 ReleaseSysCache(indTup);
21744 }
21745
21746 /* Done with pg_inherits */
21747 systable_endscan(scan);
21748 table_close(inheritsRel, AccessShareLock);
21749
21750 /*
21751 * If we found as many inherited indexes as the partitioned table has
21752 * partitions, we're good; update pg_index to set indisvalid.
21753 */
21754 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21755 {
21756 Relation idxRel;
21757 HeapTuple indTup;
21758 Form_pg_index indexForm;
21759
21760 idxRel = table_open(IndexRelationId, RowExclusiveLock);
21761 indTup = SearchSysCacheCopy1(INDEXRELID,
21763 if (!HeapTupleIsValid(indTup))
21764 elog(ERROR, "cache lookup failed for index %u",
21765 RelationGetRelid(partedIdx));
21766 indexForm = (Form_pg_index) GETSTRUCT(indTup);
21767
21768 indexForm->indisvalid = true;
21769 updated = true;
21770
21771 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
21772
21774 heap_freetuple(indTup);
21775 }
21776
21777 /*
21778 * If this index is in turn a partition of a larger index, validating it
21779 * might cause the parent to become valid also. Try that.
21780 */
21781 if (updated && partedIdx->rd_rel->relispartition)
21782 {
21783 Oid parentIdxId,
21784 parentTblId;
21785 Relation parentIdx,
21786 parentTbl;
21787
21788 /* make sure we see the validation we just did */
21790
21791 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
21792 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
21793 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
21794 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
21795 Assert(!parentIdx->rd_index->indisvalid);
21796
21797 validatePartitionedIndex(parentIdx, parentTbl);
21798
21801 }
21802}
21803
21804/*
21805 * When attaching an index as a partition of a partitioned index which is a
21806 * primary key, verify that all the columns in the partition are marked NOT
21807 * NULL.
21808 */
21809static void
21811{
21812 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
21813 {
21815 iinfo->ii_IndexAttrNumbers[i] - 1);
21816
21817 if (!att->attnotnull)
21818 ereport(ERROR,
21819 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
21820 errmsg("invalid primary key definition"),
21821 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
21822 NameStr(att->attname),
21823 RelationGetRelationName(partition)));
21824 }
21825}
21826
21827/*
21828 * Return an OID list of constraints that reference the given relation
21829 * that are marked as having a parent constraints.
21830 */
21831static List *
21833{
21834 Relation pg_constraint;
21835 HeapTuple tuple;
21836 SysScanDesc scan;
21837 ScanKeyData key[2];
21838 List *constraints = NIL;
21839
21840 /*
21841 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21842 * scan.
21843 */
21844 if (RelationGetIndexList(partition) == NIL ||
21847 return NIL;
21848
21849 /* Search for constraints referencing this table */
21850 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
21851 ScanKeyInit(&key[0],
21852 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
21853 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
21854 ScanKeyInit(&key[1],
21855 Anum_pg_constraint_contype, BTEqualStrategyNumber,
21856 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
21857
21858 /* XXX This is a seqscan, as we don't have a usable index */
21859 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21860 while ((tuple = systable_getnext(scan)) != NULL)
21861 {
21862 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21863
21864 /*
21865 * We only need to process constraints that are part of larger ones.
21866 */
21867 if (!OidIsValid(constrForm->conparentid))
21868 continue;
21869
21870 constraints = lappend_oid(constraints, constrForm->oid);
21871 }
21872
21873 systable_endscan(scan);
21874 table_close(pg_constraint, AccessShareLock);
21875
21876 return constraints;
21877}
21878
21879/*
21880 * During DETACH PARTITION, verify that any foreign keys pointing to the
21881 * partitioned table would not become invalid. An error is raised if any
21882 * referenced values exist.
21883 */
21884static void
21886{
21887 List *constraints;
21888 ListCell *cell;
21889
21890 constraints = GetParentedForeignKeyRefs(partition);
21891
21892 foreach(cell, constraints)
21893 {
21894 Oid constrOid = lfirst_oid(cell);
21895 HeapTuple tuple;
21896 Form_pg_constraint constrForm;
21897 Relation rel;
21898 Trigger trig = {0};
21899
21900 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
21901 if (!HeapTupleIsValid(tuple))
21902 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
21903 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
21904
21905 Assert(OidIsValid(constrForm->conparentid));
21906 Assert(constrForm->confrelid == RelationGetRelid(partition));
21907
21908 /* prevent data changes into the referencing table until commit */
21909 rel = table_open(constrForm->conrelid, ShareLock);
21910
21911 trig.tgoid = InvalidOid;
21912 trig.tgname = NameStr(constrForm->conname);
21914 trig.tgisinternal = true;
21915 trig.tgconstrrelid = RelationGetRelid(partition);
21916 trig.tgconstrindid = constrForm->conindid;
21917 trig.tgconstraint = constrForm->oid;
21918 trig.tgdeferrable = false;
21919 trig.tginitdeferred = false;
21920 /* we needn't fill in remaining fields */
21921
21922 RI_PartitionRemove_Check(&trig, rel, partition);
21923
21924 ReleaseSysCache(tuple);
21925
21926 table_close(rel, NoLock);
21927 }
21928}
21929
21930/*
21931 * resolve column compression specification to compression method.
21932 */
21933static char
21934GetAttributeCompression(Oid atttypid, const char *compression)
21935{
21936 char cmethod;
21937
21938 if (compression == NULL || strcmp(compression, "default") == 0)
21940
21941 /*
21942 * To specify a nondefault method, the column data type must be toastable.
21943 * Note this says nothing about whether the column's attstorage setting
21944 * permits compression; we intentionally allow attstorage and
21945 * attcompression to be independent. But with a non-toastable type,
21946 * attstorage could not be set to a value that would permit compression.
21947 *
21948 * We don't actually need to enforce this, since nothing bad would happen
21949 * if attcompression were non-default; it would never be consulted. But
21950 * it seems more user-friendly to complain about a certainly-useless
21951 * attempt to set the property.
21952 */
21953 if (!TypeIsToastable(atttypid))
21954 ereport(ERROR,
21955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21956 errmsg("column data type %s does not support compression",
21957 format_type_be(atttypid))));
21958
21959 cmethod = CompressionNameToMethod(compression);
21960 if (!CompressionMethodIsValid(cmethod))
21961 ereport(ERROR,
21962 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21963 errmsg("invalid compression method \"%s\"", compression)));
21964
21965 return cmethod;
21966}
21967
21968/*
21969 * resolve column storage specification
21970 */
21971static char
21972GetAttributeStorage(Oid atttypid, const char *storagemode)
21973{
21974 char cstorage = 0;
21975
21976 if (pg_strcasecmp(storagemode, "plain") == 0)
21977 cstorage = TYPSTORAGE_PLAIN;
21978 else if (pg_strcasecmp(storagemode, "external") == 0)
21979 cstorage = TYPSTORAGE_EXTERNAL;
21980 else if (pg_strcasecmp(storagemode, "extended") == 0)
21981 cstorage = TYPSTORAGE_EXTENDED;
21982 else if (pg_strcasecmp(storagemode, "main") == 0)
21983 cstorage = TYPSTORAGE_MAIN;
21984 else if (pg_strcasecmp(storagemode, "default") == 0)
21985 cstorage = get_typstorage(atttypid);
21986 else
21987 ereport(ERROR,
21988 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
21989 errmsg("invalid storage type \"%s\"",
21990 storagemode)));
21991
21992 /*
21993 * safety check: do not allow toasted storage modes unless column datatype
21994 * is TOAST-aware.
21995 */
21996 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
21997 ereport(ERROR,
21998 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
21999 errmsg("column data type %s can only have storage PLAIN",
22000 format_type_be(atttypid))));
22001
22002 return cstorage;
22003}
Datum idx(PG_FUNCTION_ARGS)
Definition: _int_op.c:262
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1103
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5325
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5570
AclResult
Definition: acl.h:182
@ ACLCHECK_OK
Definition: acl.h:183
@ ACLCHECK_NOT_OWNER
Definition: acl.h:185
#define DatumGetAclP(X)
Definition: acl.h:120
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition: aclchk.c:2639
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3853
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3821
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4075
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:2958
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4024
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition: amapi.c:148
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition: amcmds.c:173
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3361
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
void free_attrmap(AttrMap *map)
Definition: attmap.c:56
AttrMap * make_attrmap(int maplen)
Definition: attmap.c:40
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:175
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition: attmap.c:261
int16 AttrNumber
Definition: attnum.h:21
#define InvalidAttrNumber
Definition: attnum.h:23
char * get_tablespace_name(Oid spc_oid)
Definition: tablespace.c:1472
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
Definition: tablespace.c:1426
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Definition: tablespace.c:1143
List * raw_parser(const char *str, RawParseMode mode)
Definition: parser.c:42
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6435
Bitmapset * bms_make_singleton(int x)
Definition: bitmapset.c:216
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
#define bms_is_empty(a)
Definition: bitmapset.h:118
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4943
#define TextDatumGetCString(d)
Definition: builtins.h:98
#define NameStr(name)
Definition: c.h:717
uint16 bits16
Definition: c.h:510
uint32 SubTransactionId
Definition: c.h:627
#define gettext_noop(x)
Definition: c.h:1167
#define PG_USED_FOR_ASSERTS_ONLY
Definition: c.h:224
#define InvalidSubTransactionId
Definition: c.h:629
#define PointerIsValid(pointer)
Definition: c.h:734
int16_t int16
Definition: c.h:497
int32_t int32
Definition: c.h:498
#define MemSet(start, val, len)
Definition: c.h:991
uint32 CommandId
Definition: c.h:637
#define PG_INT16_MAX
Definition: c.h:557
#define OidIsValid(objectId)
Definition: c.h:746
bool IsToastNamespace(Oid namespaceId)
Definition: catalog.c:261
bool IsSystemRelation(Relation relation)
Definition: catalog.c:74
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:559
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:243
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:86
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:371
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition: clauses.c:2256
bool contain_volatile_functions(Node *clause)
Definition: clauses.c:539
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition: cluster.c:494
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition: cluster.c:1445
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition: cluster.c:705
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition: cluster.c:554
CompareType
Definition: cmptype.h:32
@ COMPARE_OVERLAP
Definition: cmptype.h:40
@ COMPARE_EQ
Definition: cmptype.h:36
Oid collid
void ResetSequence(Oid seq_relid)
Definition: sequence.c:262
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition: sequence.c:541
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition: comment.c:410
ObjectAddress CommentObject(CommentStmt *stmt)
Definition: comment.c:40
int32 defGetInt32(DefElem *def)
Definition: define.c:149
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition: dependency.c:332
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2608
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2548
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2502
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2788
#define PERFORM_DELETION_CONCURRENTLY
Definition: dependency.h:93
DependencyType
Definition: dependency.h:32
@ DEPENDENCY_AUTO
Definition: dependency.h:34
@ DEPENDENCY_INTERNAL
Definition: dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition: dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition: dependency.h:37
@ DEPENDENCY_NORMAL
Definition: dependency.h:33
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:956
void hash_destroy(HTAB *hashp)
Definition: dynahash.c:866
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1421
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1386
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1158
int errdetail(const char *fmt,...)
Definition: elog.c:1204
int errhint(const char *fmt,...)
Definition: elog.c:1318
int errcode(int sqlerrcode)
Definition: elog.c:854
int errmsg(const char *fmt,...)
Definition: elog.c:1071
#define _(x)
Definition: elog.c:91
#define PG_TRY(...)
Definition: elog.h:371
#define WARNING
Definition: elog.h:36
#define PG_END_TRY(...)
Definition: elog.h:396
#define DEBUG1
Definition: elog.h:30
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define NOTICE
Definition: elog.h:35
#define PG_FINALLY(...)
Definition: elog.h:388
#define ereport(elevel,...)
Definition: elog.h:149
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:223
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
void EventTriggerAlterTableRelid(Oid objectId)
void EventTriggerAlterTableEnd(void)
void EventTriggerCollectAlterTableSubcmd(Node *subcmd, ObjectAddress address)
#define AT_REWRITE_ALTER_PERSISTENCE
Definition: event_trigger.h:40
#define AT_REWRITE_DEFAULT_VAL
Definition: event_trigger.h:41
#define AT_REWRITE_ACCESS_METHOD
Definition: event_trigger.h:43
#define AT_REWRITE_COLUMN_REWRITE
Definition: event_trigger.h:42
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition: execExpr.c:143
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition: execExpr.c:765
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition: execExpr.c:872
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition: execMain.c:1329
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition: execMain.c:2170
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1427
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1443
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1741
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
Definition: execTuples.c:1833
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
Definition: execTuples.c:1765
void FreeExecutorState(EState *estate)
Definition: execUtils.c:193
EState * CreateExecutorState(void)
Definition: execUtils.c:88
struct ResultRelInfo ResultRelInfo
#define GetPerTupleExprContext(estate)
Definition: executor.h:678
#define ResetExprContext(econtext)
Definition: executor.h:672
#define GetPerTupleMemoryContext(estate)
Definition: executor.h:683
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition: executor.h:415
#define palloc0_object(type)
Definition: fe_memutils.h:75
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:684
#define DatumGetByteaPP(X)
Definition: fmgr.h:291
#define SizeForFunctionCallInfo(nargs)
Definition: fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition: fmgr.h:110
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition: foreign.c:37
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition: foreign.c:377
ForeignServer * GetForeignServer(Oid serverid)
Definition: foreign.c:111
Oid GetForeignServerIdByRelId(Oid relid)
Definition: foreign.c:355
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
Definition: foreigncmds.c:110
char * format_type_with_typemod(Oid type_oid, int32 typemod)
Definition: format_type.c:362
char * format_type_be(Oid type_oid)
Definition: format_type.c:343
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:388
bool IsBinaryUpgrade
Definition: globals.c:122
bool allowSystemTableMods
Definition: globals.c:131
Oid MyDatabaseTableSpace
Definition: globals.c:97
Oid MyDatabaseId
Definition: globals.c:95
#define newval
Assert(PointerIsAligned(start, uint64))
for(;;)
void RelationClearMissing(Relation rel)
Definition: heap.c:1954
List * heap_truncate_find_FKs(List *relationIds)
Definition: heap.c:3673
void StorePartitionKey(Relation rel, char strategy, int16 partnatts, AttrNumber *partattrs, List *partexprs, Oid *partopclass, Oid *partcollation)
Definition: heap.c:3800
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:3398
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition: heap.c:1112
void heap_truncate(List *relids)
Definition: heap.c:3493
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition: heap.c:544
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition: heap.c:3578
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3956
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls)
Definition: heap.c:2884
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2375
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition: heap.c:708
void heap_truncate_one_rel(Relation rel)
Definition: heap.c:3534
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition: heap.c:2020
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1314
BulkInsertState GetBulkInsertState(void)
Definition: heapam.c:1989
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:2006
#define XLOG_HEAP_TRUNCATE
Definition: heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
Definition: heapam_xlog.h:127
#define SizeOfHeapTruncate
Definition: heapam_xlog.h:142
#define XLH_TRUNCATE_CASCADE
Definition: heapam_xlog.h:126
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition: heaptuple.c:1210
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:778
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition: heaptuple.c:456
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1435
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
Definition: htup_details.h:904
static void * GETSTRUCT(const HeapTupleData *tuple)
Definition: htup_details.h:728
#define MaxHeapAttributeNumber
Definition: htup_details.h:48
#define stmt
Definition: indent_codes.h:59
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3583
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition: index.c:2537
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition: index.c:3948
IndexInfo * BuildIndexInfo(Relation index)
Definition: index.c:2428
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition: index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition: index.c:1885
#define REINDEX_REL_PROCESS_TOAST
Definition: index.h:159
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition: index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition: index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition: index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition: index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition: index.h:93
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:177
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:133
ObjectAddress DefineIndex(Oid tableId, IndexStmt *stmt, Oid indexRelationId, Oid parentIndexId, Oid parentConstraintId, int total_parts, bool is_alter_table, bool check_rights, bool check_not_in_use, bool skip_build, bool quiet)
Definition: indexcmds.c:542
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4409
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2345
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition: indexcmds.c:178
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:435
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2260
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
long val
Definition: informix.c:689
static struct @165 value
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition: int.h:67
void AcceptInvalidationMessages(void)
Definition: inval.c:930
void CacheInvalidateRelcache(Relation relation)
Definition: inval.c:1631
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition: inval.c:1687
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1665
int b
Definition: isn.c:74
int a
Definition: isn.c:73
int j
Definition: isn.c:78
int i
Definition: isn.c:77
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:81
Datum is_valid(PG_FUNCTION_ARGS)
Definition: isn.c:1110
List * lcons_oid(Oid datum, List *list)
Definition: list.c:531
List * lappend(List *list, void *datum)
Definition: list.c:339
List * list_difference_ptr(const List *list1, const List *list2)
Definition: list.c:1263
List * list_delete_nth_cell(List *list, int n)
Definition: list.c:767
List * list_concat(List *list1, const List *list2)
Definition: list.c:561
List * list_concat_copy(const List *list1, const List *list2)
Definition: list.c:598
List * list_copy(const List *oldlist)
Definition: list.c:1573
List * lappend_int(List *list, int datum)
Definition: list.c:357
List * lappend_oid(List *list, Oid datum)
Definition: list.c:375
List * lcons(void *datum, List *list)
Definition: list.c:495
List * list_append_unique_oid(List *list, Oid datum)
Definition: list.c:1380
void list_free(List *list)
Definition: list.c:1546
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:722
void list_free_deep(List *list)
Definition: list.c:1560
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:151
void UnlockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:229
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:905
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition: lmgr.c:107
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:334
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:351
void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
Definition: lmgr.c:601
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:182
int LOCKMODE
Definition: lockdefs.h:26
#define NoLock
Definition: lockdefs.h:34
#define AccessExclusiveLock
Definition: lockdefs.h:43
#define ShareRowExclusiveLock
Definition: lockdefs.h:41
#define AccessShareLock
Definition: lockdefs.h:36
#define InplaceUpdateTupleLock
Definition: lockdefs.h:48
#define ShareUpdateExclusiveLock
Definition: lockdefs.h:39
#define RowShareLock
Definition: lockdefs.h:37
#define ShareLock
Definition: lockdefs.h:40
#define RowExclusiveLock
Definition: lockdefs.h:38
char * get_rel_name(Oid relid)
Definition: lsyscache.c:2068
AttrNumber get_attnum(Oid relid, const char *attname)
Definition: lsyscache.c:950
Oid get_constraint_index(Oid conoid)
Definition: lsyscache.c:1205
char get_typstorage(Oid typid)
Definition: lsyscache.c:2559
bool get_index_isreplident(Oid index_oid)
Definition: lsyscache.c:3695
char get_rel_relkind(Oid relid)
Definition: lsyscache.c:2143
Oid get_typcollation(Oid typid)
Definition: lsyscache.c:3196
char * get_collation_name(Oid colloid)
Definition: lsyscache.c:1127
bool get_index_isclustered(Oid index_oid)
Definition: lsyscache.c:3741
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition: lsyscache.c:167
char * get_constraint_name(Oid conoid)
Definition: lsyscache.c:1173
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:919
bool get_collation_isdeterministic(Oid colloid)
Definition: lsyscache.c:1146
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition: lsyscache.c:1393
Oid get_rel_relam(Oid relid)
Definition: lsyscache.c:2240
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3221
Oid get_rel_tablespace(Oid relid)
Definition: lsyscache.c:2194
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2871
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition: lsyscache.c:2678
Oid getBaseType(Oid typid)
Definition: lsyscache.c:2661
char * get_namespace_name(Oid nspid)
Definition: lsyscache.c:3506
char get_constraint_type(Oid conoid)
Definition: lsyscache.c:1235
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition: lsyscache.c:2025
#define TypeIsToastable(typid)
Definition: lsyscache.h:218
Expr * make_ands_explicit(List *andclauses)
Definition: makefuncs.c:799
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:531
Var * makeVar(int varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:66
ColumnDef * makeColumnDef(const char *colname, Oid typeOid, int32 typmod, Oid collOid)
Definition: makefuncs.c:565
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition: makefuncs.c:388
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition: makefuncs.c:473
Constraint * makeNotNullConstraint(String *colname)
Definition: makefuncs.c:493
List * make_ands_implicit(Expr *clause)
Definition: makefuncs.c:810
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:2314
void MemoryContextReset(MemoryContext context)
Definition: mcxt.c:414
char * pstrdup(const char *in)
Definition: mcxt.c:2327
void pfree(void *pointer)
Definition: mcxt.c:2152
void * palloc0(Size size)
Definition: mcxt.c:1975
void * palloc(Size size)
Definition: mcxt.c:1945
MemoryContext CurrentMemoryContext
Definition: mcxt.c:159
MemoryContext CacheMemoryContext
Definition: mcxt.c:168
void MemoryContextDelete(MemoryContext context)
Definition: mcxt.c:485
MemoryContext PortalContext
Definition: mcxt.c:174
#define AllocSetContextCreate
Definition: memutils.h:149
#define ALLOCSET_DEFAULT_SIZES
Definition: memutils.h:180
#define ALLOCSET_SMALL_SIZES
Definition: memutils.h:190
#define CHECK_FOR_INTERRUPTS()
Definition: miscadmin.h:123
bool InSecurityRestrictedOperation(void)
Definition: miscinit.c:690
Oid GetUserId(void)
Definition: miscinit.c:520
MultiXactId ReadNextMultiXactId(void)
Definition: multixact.c:771
void namestrcpy(Name name, const char *str)
Definition: name.c:233
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition: namespace.c:739
bool isAnyTempNamespace(Oid namespaceId)
Definition: namespace.c:3687
Oid get_collation_oid(List *collname, bool missing_ok)
Definition: namespace.c:3971
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition: namespace.c:3459
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3554
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3355
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition: namespace.c:441
@ RVR_MISSING_OK
Definition: namespace.h:72
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition: namespace.h:80
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
Oid exprCollation(const Node *expr)
Definition: nodeFuncs.c:821
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:705
#define IsA(nodeptr, _type_)
Definition: nodes.h:164
#define copyObject(obj)
Definition: nodes.h:230
#define nodeTag(nodeptr)
Definition: nodes.h:139
#define makeNode(_type_)
Definition: nodes.h:161
#define castNode(_type_, nodeptr)
Definition: nodes.h:182
#define InvokeObjectPostCreateHook(classId, objectId, subId)
Definition: objectaccess.h:173
#define InvokeObjectTruncateHook(objectId)
Definition: objectaccess.h:191
#define InvokeObjectPostAlterHook(classId, objectId, subId)
Definition: objectaccess.h:197
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
Definition: objectaccess.h:200
ObjectType get_relkind_objtype(char relkind)
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
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:797
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:124
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
Definition: parse_coerce.c:557
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
CoercionPathType
Definition: parse_coerce.h:25
@ COERCION_PATH_NONE
Definition: parse_coerce.h:26
@ COERCION_PATH_RELABELTYPE
Definition: parse_coerce.h:28
void assign_expr_collations(ParseState *pstate, Node *expr)
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
int parser_errposition(ParseState *pstate, int location)
Definition: parse_node.c:106
ParseState * make_parsestate(ParseState *parentParseState)
Definition: parse_node.c:39
@ EXPR_KIND_PARTITION_EXPRESSION
Definition: parse_node.h:80
ParseNamespaceItem * addRangeTableEntryForRelation(ParseState *pstate, Relation rel, int lockmode, Alias *alias, bool inh, bool inFromCl)
Oid attnumCollationId(Relation rd, int attid)
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
Oid attnumTypeId(Relation rd, int attid)
const NameData * attnumAttName(Relation rd, int attid)
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition: parse_type.c:310
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition: parse_type.c:264
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition: parse_type.c:540
Oid typenameTypeId(ParseState *pstate, const TypeName *typeName)
Definition: parse_type.c:291
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
PartitionBoundSpec * transformPartitionBound(ParseState *pstate, Relation parent, PartitionBoundSpec *spec)
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2809
#define ACL_MAINTAIN
Definition: parsenodes.h:90
#define ACL_USAGE
Definition: parsenodes.h:84
#define FKCONSTR_ACTION_SETDEFAULT
Definition: parsenodes.h:2812
PartitionStrategy
Definition: parsenodes.h:882
@ PARTITION_STRATEGY_HASH
Definition: parsenodes.h:885
@ PARTITION_STRATEGY_LIST
Definition: parsenodes.h:883
ConstrType
Definition: parsenodes.h:2787
@ CONSTR_FOREIGN
Definition: parsenodes.h:2798
@ CONSTR_UNIQUE
Definition: parsenodes.h:2796
@ CONSTR_DEFAULT
Definition: parsenodes.h:2791
@ CONSTR_NOTNULL
Definition: parsenodes.h:2790
@ CONSTR_CHECK
Definition: parsenodes.h:2794
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2797
@ CONSTR_PRIMARY
Definition: parsenodes.h:2795
DropBehavior
Definition: parsenodes.h:2389
@ DROP_CASCADE
Definition: parsenodes.h:2391
@ DROP_RESTRICT
Definition: parsenodes.h:2390
ObjectType
Definition: parsenodes.h:2316
@ OBJECT_MATVIEW
Definition: parsenodes.h:2340
@ OBJECT_SCHEMA
Definition: parsenodes.h:2353
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2335
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2359
@ OBJECT_INDEX
Definition: parsenodes.h:2337
@ OBJECT_SEQUENCE
Definition: parsenodes.h:2354
@ OBJECT_TABLE
Definition: parsenodes.h:2358
@ OBJECT_VIEW
Definition: parsenodes.h:2368
@ OBJECT_TYPE
Definition: parsenodes.h:2366
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2357
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2330
AlterTableType
Definition: parsenodes.h:2408
@ AT_AddIndexConstraint
Definition: parsenodes.h:2430
@ AT_DropOf
Definition: parsenodes.h:2461
@ AT_SetOptions
Definition: parsenodes.h:2418
@ AT_DropIdentity
Definition: parsenodes.h:2473
@ AT_DisableTrigUser
Definition: parsenodes.h:2453
@ AT_DropNotNull
Definition: parsenodes.h:2413
@ AT_AddOf
Definition: parsenodes.h:2460
@ AT_ResetOptions
Definition: parsenodes.h:2419
@ AT_ReplicaIdentity
Definition: parsenodes.h:2462
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2445
@ AT_EnableRowSecurity
Definition: parsenodes.h:2463
@ AT_AddColumnToView
Definition: parsenodes.h:2410
@ AT_ResetRelOptions
Definition: parsenodes.h:2444
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2448
@ AT_DropOids
Definition: parsenodes.h:2440
@ AT_SetIdentity
Definition: parsenodes.h:2472
@ AT_ReAddStatistics
Definition: parsenodes.h:2474
@ AT_SetUnLogged
Definition: parsenodes.h:2439
@ AT_DisableTrig
Definition: parsenodes.h:2449
@ AT_SetCompression
Definition: parsenodes.h:2421
@ AT_DropExpression
Definition: parsenodes.h:2416
@ AT_AddIndex
Definition: parsenodes.h:2423
@ AT_EnableReplicaRule
Definition: parsenodes.h:2456
@ AT_ReAddIndex
Definition: parsenodes.h:2424
@ AT_DropConstraint
Definition: parsenodes.h:2431
@ AT_SetNotNull
Definition: parsenodes.h:2414
@ AT_ClusterOn
Definition: parsenodes.h:2436
@ AT_AddIdentity
Definition: parsenodes.h:2471
@ AT_ForceRowSecurity
Definition: parsenodes.h:2465
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2455
@ AT_SetAccessMethod
Definition: parsenodes.h:2441
@ AT_AlterColumnType
Definition: parsenodes.h:2433
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2470
@ AT_AddInherit
Definition: parsenodes.h:2458
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2427
@ AT_EnableTrig
Definition: parsenodes.h:2446
@ AT_DropColumn
Definition: parsenodes.h:2422
@ AT_ReAddComment
Definition: parsenodes.h:2432
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2434
@ AT_DisableTrigAll
Definition: parsenodes.h:2451
@ AT_EnableRule
Definition: parsenodes.h:2454
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2466
@ AT_DetachPartition
Definition: parsenodes.h:2469
@ AT_SetStatistics
Definition: parsenodes.h:2417
@ AT_AttachPartition
Definition: parsenodes.h:2468
@ AT_AddConstraint
Definition: parsenodes.h:2425
@ AT_DropInherit
Definition: parsenodes.h:2459
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2447
@ AT_SetLogged
Definition: parsenodes.h:2438
@ AT_SetStorage
Definition: parsenodes.h:2420
@ AT_DisableRule
Definition: parsenodes.h:2457
@ AT_DisableRowSecurity
Definition: parsenodes.h:2464
@ AT_SetRelOptions
Definition: parsenodes.h:2443
@ AT_ChangeOwner
Definition: parsenodes.h:2435
@ AT_EnableTrigUser
Definition: parsenodes.h:2452
@ AT_SetExpression
Definition: parsenodes.h:2415
@ AT_ReAddConstraint
Definition: parsenodes.h:2426
@ AT_SetTableSpace
Definition: parsenodes.h:2442
@ AT_GenericOptions
Definition: parsenodes.h:2467
@ AT_ColumnDefault
Definition: parsenodes.h:2411
@ AT_CookedColumnDefault
Definition: parsenodes.h:2412
@ AT_AlterConstraint
Definition: parsenodes.h:2428
@ AT_EnableTrigAll
Definition: parsenodes.h:2450
@ AT_DropCluster
Definition: parsenodes.h:2437
@ AT_ValidateConstraint
Definition: parsenodes.h:2429
@ AT_AddColumn
Definition: parsenodes.h:2409
#define ACL_REFERENCES
Definition: parsenodes.h:81
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2810
#define ACL_TRUNCATE
Definition: parsenodes.h:80
#define ACL_CREATE
Definition: parsenodes.h:85
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2811
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2808
@ RAW_PARSE_DEFAULT
Definition: parser.h:39
List * SystemFuncName(char *name)
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
Definition: partbounds.c:2896
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition: partbounds.c:249
void check_default_partition_contents(Relation parent, Relation default_rel, PartitionBoundSpec *new_spec)
Definition: partbounds.c:3251
List * RelationGetPartitionQual(Relation rel)
Definition: partcache.c:277
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition: partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition: partition.c:222
bool has_partition_attrs(Relation rel, Bitmapset *attnums, bool *used_in_expr)
Definition: partition.c:255
List * get_proposed_default_constraint(List *new_part_constraints)
Definition: partition.c:370
void update_default_partition_oid(Oid parentId, Oid defaultPartId)
Definition: partition.c:340
Oid index_get_partition(Relation partition, Oid indexId)
Definition: partition.c:176
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal)
Definition: pg_attrdef.c:35
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition: pg_attrdef.c:278
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:320
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition: pg_attrdef.c:152
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
bool attnotnull
Definition: pg_attribute.h:123
void * arg
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
NameData relname
Definition: pg_class.h:38
FormData_pg_class * Form_pg_class
Definition: pg_class.h:156
#define PARTITION_MAX_KEYS
#define INDEX_MAX_KEYS
#define NAMEDATALEN
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
Definition: pg_constraint.c:51
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
void RenameConstraintById(Oid conId, const char *newname)
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok)
FormData_pg_constraint * Form_pg_constraint
@ CONSTRAINT_RELATION
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition: pg_depend.c:45
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition: pg_depend.c:457
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:351
List * getOwnedSequences(Oid relid)
Definition: pg_depend.c:936
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition: pg_depend.c:301
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition: pg_depend.c:398
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:945
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:988
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition: pg_depend.c:828
FormData_pg_depend * Form_pg_depend
Definition: pg_depend.h:72
FormData_pg_foreign_table * Form_pg_foreign_table
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
Definition: pg_inherits.c:552
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition: pg_inherits.c:58
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
bool has_superclass(Oid relationId)
Definition: pg_inherits.c:377
bool PartitionHasPendingDetach(Oid partoid)
Definition: pg_inherits.c:620
FormData_pg_inherits * Form_pg_inherits
Definition: pg_inherits.h: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
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:518
#define foreach_current_index(var_or_cell)
Definition: pg_list.h:403
#define lfirst_int(lc)
Definition: pg_list.h:173
#define foreach_delete_current(lst, var_or_cell)
Definition: pg_list.h:391
#define list_make1_oid(x1)
Definition: pg_list.h:242
#define list_make1(x1)
Definition: pg_list.h:212
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414
static void * list_nth(const List *list, int n)
Definition: pg_list.h:299
#define linitial(l)
Definition: pg_list.h:178
#define list_make3(x1, x2, x3)
Definition: pg_list.h:216
#define foreach_node(type, var, lst)
Definition: pg_list.h:496
static ListCell * list_head(const List *l)
Definition: pg_list.h:128
#define foreach_oid(var, lst)
Definition: pg_list.h:471
#define list_nth_node(type, list, n)
Definition: pg_list.h:327
static ListCell * lnext(const List *l, const ListCell *c)
Definition: pg_list.h:343
#define linitial_oid(l)
Definition: pg_list.h:180
#define lfirst_oid(lc)
Definition: pg_list.h:174
#define list_make2(x1, x2)
Definition: pg_list.h:214
#define foreach_int(var, lst)
Definition: pg_list.h:470
FormData_pg_opclass * Form_pg_opclass
Definition: pg_opclass.h:83
List * GetRelationPublications(Oid relid)
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391
static char * buf
Definition: pg_test_fsync.c:72
FormData_pg_trigger * Form_pg_trigger
Definition: pg_trigger.h:80
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition: pg_type.c:765
FormData_pg_type * Form_pg_type
Definition: pg_type.h:261
#define ERRCODE_UNDEFINED_TABLE
Definition: pgbench.c:79
void pgstat_count_truncate(Relation rel)
Expr * expression_planner(Expr *expr)
Definition: planner.c:6688
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
#define snprintf
Definition: port.h:239
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: strlcpy.c:45
static Datum PointerGetDatum(const void *X)
Definition: postgres.h:327
uintptr_t Datum
Definition: postgres.h:69
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:257
static Pointer DatumGetPointer(Datum X)
Definition: postgres.h:317
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:355
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:217
static Datum CharGetDatum(char X)
Definition: postgres.h:127
#define InvalidOid
Definition: postgres_ext.h:35
unsigned int Oid
Definition: postgres_ext.h:30
void CheckTableForSerializableConflictIn(Relation relation)
Definition: predicate.c:4419
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition: predicate.c:3123
bool predicate_implied_by(List *predicate_list, List *clause_list, bool weak)
Definition: predtest.c:152
Expr * canonicalize_qual(Expr *qual, bool is_check)
Definition: prepqual.c:293
char * c
#define PRS2_OLD_VARNO
Definition: primnodes.h:250
#define PRS2_NEW_VARNO
Definition: primnodes.h:251
OnCommitAction
Definition: primnodes.h:57
@ ONCOMMIT_DELETE_ROWS
Definition: primnodes.h:60
@ ONCOMMIT_NOOP
Definition: primnodes.h:58
@ ONCOMMIT_PRESERVE_ROWS
Definition: primnodes.h:59
@ ONCOMMIT_DROP
Definition: primnodes.h:61
@ COERCE_IMPLICIT_CAST
Definition: primnodes.h:753
@ IS_NOT_NULL
Definition: primnodes.h:1957
@ COERCION_ASSIGNMENT
Definition: primnodes.h:732
@ COERCION_IMPLICIT
Definition: primnodes.h:731
void * stringToNode(const char *str)
Definition: read.c:90
#define RelationGetForm(relation)
Definition: rel.h:510
#define RelationGetRelid(relation)
Definition: rel.h:516
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:712
#define RelationIsUsedAsCatalogTable(relation)
Definition: rel.h:397
static SMgrRelation RelationGetSmgr(Relation rel)
Definition: rel.h:578
#define RelationGetDescr(relation)
Definition: rel.h:542
#define RelationIsMapped(relation)
Definition: rel.h:565
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:522
#define RelationGetRelationName(relation)
Definition: rel.h:550
#define RELATION_IS_OTHER_TEMP(relation)
Definition: rel.h:669
#define RelationGetNamespace(relation)
Definition: rel.h:557
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:535
#define RelationIsPermanent(relation)
Definition: rel.h:628
List * RelationGetIndexList(Relation relation)
Definition: relcache.c:4833
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition: relcache.c:5044
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6100
int errtablecol(Relation rel, int attnum)
Definition: relcache.c:6063
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5207
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5300
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition: relcache.c:3773
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4728
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5094
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3976
int errtable(Relation rel)
Definition: relcache.c:6046
struct RelationData * Relation
Definition: relcache.h:27
@ INDEX_ATTR_BITMAP_KEY
Definition: relcache.h:69
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition: relcache.h:70
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition: relcache.h:71
Datum transformRelOptions(Datum oldOptions, List *defList, const char *namspace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
Definition: reloptions.c:1167
List * untransformRelOptions(Datum options)
Definition: reloptions.c:1342
bytea * view_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2025
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
Definition: reloptions.c:2081
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2011
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2135
bytea * attribute_reloptions(Datum reloptions, bool validate)
Definition: reloptions.c:2096
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
Definition: reloptions.c:2046
#define HEAP_RELOPT_NAMESPACES
Definition: reloptions.h:61
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:56
@ MAIN_FORKNUM
Definition: relpath.h:58
@ INIT_FORKNUM
Definition: relpath.h:61
#define MAX_FORKNUM
Definition: relpath.h:70
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
#define RULE_FIRES_ALWAYS
Definition: rewriteDefine.h:22
#define RULE_DISABLED
Definition: rewriteDefine.h:24
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Node * build_column_default(Relation rel, int attrno)
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
Node * map_variable_attnos(Node *node, int target_varno, int sublevels_up, const AttrMap *attno_map, Oid to_rowtype, bool *found_whole_row)
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
Definition: ri_triggers.c:474
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1519
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1813
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3210
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition: ruleutils.c:1627
char * pg_get_indexdef_string(Oid indexrelid)
Definition: ruleutils.c:1225
const char * quote_identifier(const char *ident)
Definition: ruleutils.c:13019
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2675
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2184
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
@ ForwardScanDirection
Definition: sdir.h:28
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition: smgr.c:481
void smgrclose(SMgrRelation reln)
Definition: smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition: smgr.c:462
TransactionId RecentXmin
Definition: snapmgr.c:159
Snapshot GetTransactionSnapshot(void)
Definition: snapmgr.c:271
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:342
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:853
void PushActiveSnapshot(Snapshot snapshot)
Definition: snapmgr.c:669
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:811
void PopActiveSnapshot(void)
Definition: snapmgr.c:762
Snapshot GetActiveSnapshot(void)
Definition: snapmgr.c:787
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: relation.c:205
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:88
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition: relation.c:47
void check_stack_depth(void)
Definition: stack_depth.c:95
ObjectAddress CreateStatistics(CreateStatsStmt *stmt)
Definition: statscmds.c:62
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:916
void RelationPreserveStorage(RelFileLocator rlocator, bool atCommit)
Definition: storage.c:252
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition: storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition: storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition: storage.c:187
void RelationDropStorage(Relation rel)
Definition: storage.c:207
#define InvalidStrategy
Definition: stratnum.h:24
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
RoleSpec * newowner
Definition: parsenodes.h:2485
DropBehavior behavior
Definition: parsenodes.h:2488
AlterTableType subtype
Definition: parsenodes.h:2480
RangeVar * relation
Definition: parsenodes.h:2401
ObjectType objtype
Definition: parsenodes.h:2403
const char * queryString
Definition: utility.h:33
List * constraints
Definition: tablecmds.c:187
bool verify_new_notnull
Definition: tablecmds.c:190
List * changedConstraintDefs
Definition: tablecmds.c:203
Expr * partition_constraint
Definition: tablecmds.c:198
char newrelpersistence
Definition: tablecmds.c:197
List * changedStatisticsDefs
Definition: tablecmds.c:209
bool chgAccessMethod
Definition: tablecmds.c:192
List * afterStmts
Definition: tablecmds.c:189
char * clusterOnIndex
Definition: tablecmds.c:207
char * replicaIdentityIndex
Definition: tablecmds.c:206
List * changedStatisticsOids
Definition: tablecmds.c:208
List * changedIndexDefs
Definition: tablecmds.c:205
List * changedIndexOids
Definition: tablecmds.c:204
List * changedConstraintOids
Definition: tablecmds.c:202
bool validate_default
Definition: tablecmds.c:200
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:185
TupleDesc oldDesc
Definition: tablecmds.c:173
Relation rel
Definition: tablecmds.c:182
Definition: attmap.h:35
AttrNumber * attnums
Definition: attmap.h:36
bool is_not_null
Definition: parsenodes.h:742
char identity
Definition: parsenodes.h:748
RangeVar * identitySequence
Definition: parsenodes.h:749
List * constraints
Definition: parsenodes.h:754
Node * cooked_default
Definition: parsenodes.h:747
char * storage_name
Definition: parsenodes.h:745
char * colname
Definition: parsenodes.h:737
TypeName * typeName
Definition: parsenodes.h:738
char generated
Definition: parsenodes.h:751
bool is_from_type
Definition: parsenodes.h:743
Node * raw_default
Definition: parsenodes.h:746
char storage
Definition: parsenodes.h:744
bool is_local
Definition: parsenodes.h:741
int16 inhcount
Definition: parsenodes.h:740
char * compression
Definition: parsenodes.h:739
ParseLoc location
Definition: parsenodes.h:756
char * comment
Definition: parsenodes.h:3351
ObjectType objtype
Definition: parsenodes.h:3349
Node * object
Definition: parsenodes.h:3350
bool attisdropped
Definition: tupdesc.h:77
char attnullability
Definition: tupdesc.h:79
char * ccname
Definition: tupdesc.h:30
bool ccenforced
Definition: tupdesc.h:32
bool ccvalid
Definition: tupdesc.h:33
char * ccbin
Definition: tupdesc.h:31
bool initdeferred
Definition: parsenodes.h:2825
ParseLoc location
Definition: parsenodes.h:2866
bool reset_default_tblspc
Definition: parsenodes.h:2847
List * keys
Definition: parsenodes.h:2837
List * pk_attrs
Definition: parsenodes.h:2855
List * fk_del_set_cols
Definition: parsenodes.h:2861
bool fk_with_period
Definition: parsenodes.h:2856
ConstrType contype
Definition: parsenodes.h:2822
Oid old_pktable_oid
Definition: parsenodes.h:2863
bool is_no_inherit
Definition: parsenodes.h:2829
char fk_upd_action
Definition: parsenodes.h:2859
List * old_conpfeqop
Definition: parsenodes.h:2862
bool is_enforced
Definition: parsenodes.h:2826
char fk_matchtype
Definition: parsenodes.h:2858
bool pk_with_period
Definition: parsenodes.h:2857
char * cooked_expr
Definition: parsenodes.h:2832
bool initially_valid
Definition: parsenodes.h:2828
bool skip_validation
Definition: parsenodes.h:2827
bool deferrable
Definition: parsenodes.h:2824
Node * raw_expr
Definition: parsenodes.h:2830
char * conname
Definition: parsenodes.h:2823
RangeVar * pktable
Definition: parsenodes.h:2853
char fk_del_action
Definition: parsenodes.h:2860
List * fk_attrs
Definition: parsenodes.h:2854
Oid conoid
Definition: heap.h:39
char * name
Definition: heap.h:40
AttrNumber attnum
Definition: heap.h:41
bool skip_validation
Definition: heap.h:44
bool is_enforced
Definition: heap.h:43
bool is_no_inherit
Definition: heap.h:47
int16 inhcount
Definition: heap.h:46
bool is_local
Definition: heap.h:45
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:42
Node * whenClause
Definition: parsenodes.h:3110
List * transitionRels
Definition: parsenodes.h:3112
RangeVar * constrrel
Definition: parsenodes.h:3116
RangeVar * relation
Definition: parsenodes.h:3101
char * defname
Definition: parsenodes.h:826
bool missing_ok
Definition: parsenodes.h:3326
List * objects
Definition: parsenodes.h:3323
ObjectType removeType
Definition: parsenodes.h:3324
bool concurrent
Definition: parsenodes.h:3327
DropBehavior behavior
Definition: parsenodes.h:3325
int es_instrument
Definition: execnodes.h:718
MemoryContext es_query_cxt
Definition: execnodes.h:708
List * es_opened_result_relations
Definition: execnodes.h:686
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:268
ExecForeignTruncate_function ExecForeignTruncate
Definition: fdwapi.h:263
bool conenforced
Definition: rel.h:288
Oid funcid
Definition: primnodes.h:767
List * args
Definition: primnodes.h:785
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:220
ItemPointerData t_self
Definition: htup.h:65
amoptions_function amoptions
Definition: amapi.h:302
bool amcanunique
Definition: amapi.h:256
bool ii_Unique
Definition: execnodes.h:209
int ii_NumIndexKeyAttrs
Definition: execnodes.h:197
AttrNumber ii_IndexAttrNumbers[INDEX_MAX_KEYS]
Definition: execnodes.h:198
bool reset_default_tblspc
Definition: parsenodes.h:3471
char * idxname
Definition: parsenodes.h:3445
char * idxcomment
Definition: parsenodes.h:3455
Definition: lock.h:166
Definition: pg_list.h:54
AttrNumber attnum
Definition: tablecmds.c:236
bool is_generated
Definition: tablecmds.c:239
ExprState * exprstate
Definition: tablecmds.c:238
char * name
Definition: tablecmds.c:216
ConstrType contype
Definition: tablecmds.c:217
bool conwithperiod
Definition: tablecmds.c:220
Node * qual
Definition: tablecmds.c:222
ExprState * qualstate
Definition: tablecmds.c:223
Definition: nodes.h:135
NullTestType nulltesttype
Definition: primnodes.h:1964
ParseLoc location
Definition: primnodes.h:1967
Expr * arg
Definition: primnodes.h:1963
SubTransactionId creating_subid
Definition: tablecmds.c:127
SubTransactionId deleting_subid
Definition: tablecmds.c:128
OnCommitAction oncommit
Definition: tablecmds.c:118
const char * p_sourcetext
Definition: parse_node.h:209
PartitionBoundSpec * bound
Definition: parsenodes.h:958
RangeVar * name
Definition: parsenodes.h:957
List * collation
Definition: parsenodes.h:876
ParseLoc location
Definition: parsenodes.h:878
List * opclass
Definition: parsenodes.h:877
List * partParams
Definition: parsenodes.h:897
ParseLoc location
Definition: parsenodes.h:898
PartitionStrategy strategy
Definition: parsenodes.h:896
char * relname
Definition: primnodes.h:83
bool inh
Definition: primnodes.h:86
char * schemaname
Definition: primnodes.h:80
Node * raw_default
Definition: heap.h:31
AttrNumber attnum
Definition: heap.h:30
char generated
Definition: heap.h:32
Node * stmt
Definition: parsenodes.h:2071
RelFileNumber relNumber
struct IndexAmRoutine * rd_indam
Definition: rel.h:206
SubTransactionId rd_firstRelfilelocatorSubid
Definition: rel.h:106
int rd_refcnt
Definition: rel.h:59
TriggerDesc * trigdesc
Definition: rel.h:117
bool rd_islocaltemp
Definition: rel.h:61
TupleDesc rd_att
Definition: rel.h:112
Form_pg_index rd_index
Definition: rel.h:192
bool rd_isnailed
Definition: rel.h:62
Oid rd_id
Definition: rel.h:113
SubTransactionId rd_newRelfilelocatorSubid
Definition: rel.h:104
SubTransactionId rd_createSubid
Definition: rel.h:103
RelFileLocator rd_locator
Definition: rel.h:57
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Form_pg_class rd_rel
Definition: rel.h:111
Relation ri_RelationDesc
Definition: execnodes.h:475
TransactionId xmin
Definition: snapshot.h:153
Definition: value.h:64
NodeTag type
Definition: trigger.h:33
Relation tg_relation
Definition: trigger.h:35
TriggerEvent tg_event
Definition: trigger.h:34
TupleTableSlot * tg_trigslot
Definition: trigger.h:39
Trigger * tg_trigger
Definition: trigger.h:38
HeapTuple tg_trigtuple
Definition: trigger.h:36
char tgenabled
Definition: reltrigger.h:30
Oid tgoid
Definition: reltrigger.h:25
Oid tgconstrindid
Definition: reltrigger.h:34
Oid tgconstraint
Definition: reltrigger.h:35
Oid tgconstrrelid
Definition: reltrigger.h:33
char * tgname
Definition: reltrigger.h:27
bool tgdeferrable
Definition: reltrigger.h:36
bool tginitdeferred
Definition: reltrigger.h:37
bool tgisinternal
Definition: reltrigger.h:31
bool has_generated_virtual
Definition: tupdesc.h:47
bool has_not_null
Definition: tupdesc.h:45
ConstrCheck * check
Definition: tupdesc.h:41
uint16 num_check
Definition: tupdesc.h:44
TupleConstr * constr
Definition: tupdesc.h:141
Oid tts_tableOid
Definition: tuptable.h:130
AttrNumber tts_nvalid
Definition: tuptable.h:120
bool * tts_isnull
Definition: tuptable.h:127
Datum * tts_values
Definition: tuptable.h:125
bool setof
Definition: parsenodes.h:281
List * arrayBounds
Definition: parsenodes.h:285
Definition: primnodes.h:262
const char * skipping_msg
Definition: tablecmds.c:250
int nonexistent_code
Definition: tablecmds.c:248
const char * nonexistent_msg
Definition: tablecmds.c:249
const char * drophint_msg
Definition: tablecmds.c:252
const char * nota_msg
Definition: tablecmds.c:251
Definition: type.h:96
Definition: c.h:697
Oid values[FLEXIBLE_ARRAY_MEMBER]
Definition: c.h:704
Definition: regguts.h:323
bool superuser(void)
Definition: superuser.c:46
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition: syscache.c:503
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:566
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCacheLocked1(int cacheId, Datum key1)
Definition: syscache.c:287
HeapTuple SearchSysCacheLockedCopy1(int cacheId, Datum key1)
Definition: syscache.c:404
HeapTuple SearchSysCache1(int cacheId, Datum key1)
Definition: syscache.c:221
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition: syscache.c:543
Datum SysCacheGetAttr(int cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition: syscache.c:600
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition: syscache.c:522
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:232
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition: syscache.c:480
Datum SysCacheGetAttrNotNull(int cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition: syscache.c:631
#define SearchSysCacheCopy1(cacheId, key1)
Definition: syscache.h:91
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition: syscache.h:102
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition: table.c:83
TupleTableSlot * table_slot_create(Relation relation, List **reglist)
Definition: tableam.c:92
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, struct ScanKeyData *key)
Definition: tableam.c:113
char * default_table_access_method
Definition: tableam.c:49
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition: tableam.c:59
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, struct ScanKeyData *key)
Definition: tableam.h:870
static void table_endscan(TableScanDesc scan)
Definition: tableam.h:979
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:253
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1555
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1362
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1611
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1015
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3834
void ResetRelRewrite(Oid myrelid)
Definition: tablecmds.c:4353
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10611
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17412
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17779
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:15867
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5292
static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12722
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17173
ObjectAddress RenameRelation(RenameStmt *stmt)
Definition: tablecmds.c:4196
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6552
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition: tablecmds.c:9543
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:18270
static ObjectAddress ATExecAlterConstraint(List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12160
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3406
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:11003
#define ATT_TABLE
Definition: tablecmds.c:328
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:20016
static const char * storage_name(char c)
Definition: tablecmds.c:2451
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8767
void AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
Definition: tablecmds.c:19371
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition: tablecmds.c:7689
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:8116
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:15540
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:16765
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
Definition: tablecmds.c:18076
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
Definition: tablecmds.c:11965
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:17862
#define AT_NUM_PASSES
Definition: tablecmds.c:166
void PreCommit_on_commit_actions(void)
Definition: tablecmds.c:19232
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:14015
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
Definition: tablecmds.c:11883
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8453
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:21934
static int findAttrByName(const char *attributeName, const List *columns)
Definition: tablecmds.c:3600
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15291
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:14614
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition: tablecmds.c:9835
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:12867
void AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4524
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15342
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:13574
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition: tablecmds.c:7707
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:7732
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13423
#define ATT_SEQUENCE
Definition: tablecmds.c:335
void RemoveRelations(DropStmt *drop)
Definition: tablecmds.c:1528
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:16527
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6881
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15204
static List * GetParentedForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21832
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18966
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9761
ObjectAddress renameatt(RenameStmt *stmt)
Definition: tablecmds.c:3999
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:15984
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:19498
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4439
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1501
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4598
struct ForeignTruncateInfo ForeignTruncateInfo
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:20814
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
Definition: tablecmds.c:19088
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3979
TupleDesc BuildDescForRelation(const List *columns)
Definition: tablecmds.c:1370
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:18546
void AtEOXact_on_commit_actions(bool isCommit)
Definition: tablecmds.c:19339
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:13603
struct OnCommitItem OnCommitItem
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4406
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12574
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13795
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:17116
static void AttachPartitionForeignKey(List **wqueue, Relation partition, Oid partConstrOid, Oid parentConstrOid, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11755
static List * MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
Definition: tablecmds.c:3157
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3785
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:21469
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6851
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2428
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:21708
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
Definition: tablecmds.c:12813
static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *fkconstraint, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:10032
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:15798
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12094
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
Definition: tablecmds.c:10687
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15219
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:9016
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
Definition: tablecmds.c:12374
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13158
static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode, ObjectAddresses *addrs)
Definition: tablecmds.c:9249
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:11412
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:9095
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4860
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3683
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:18516
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6926
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2410
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2362
#define ATT_INDEX
Definition: tablecmds.c:331
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:16290
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3555
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
Definition: tablecmds.c:14978
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8721
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:12303
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
Definition: tablecmds.c:21810
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:8201
static ObjectAddress rename_constraint_internal(Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
Definition: tablecmds.c:4037
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1453
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:15371
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5701
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:18929
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9877
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11211
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:17550
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9221
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3637
static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
Definition: tablecmds.c:3236
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6729
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6586
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)
Definition: tablecmds.c:12955
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:16557
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8336
ObjectAddress AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
Definition: tablecmds.c:18858
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:15235
addFkConstraintSides
Definition: tablecmds.c:354
@ addFkReferencingSide
Definition: tablecmds.c:356
@ addFkBothSides
Definition: tablecmds.c:357
@ addFkReferencedSide
Definition: tablecmds.c:355
void ExecuteTruncate(TruncateStmt *stmt)
Definition: tablecmds.c:1851
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)
Definition: tablecmds.c:18314
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:333
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7183
static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
Definition: tablecmds.c:19697
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:20657
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:20475
static void StoreCatalogInheritance(Oid relationId, List *supers, bool child_is_partition)
Definition: tablecmds.c:3511
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:18575
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:15827
struct AlteredTableInfo AlteredTableInfo
Oid AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:4465
static void AlterConstrDeferrabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12771
struct NewConstraint NewConstraint
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8871
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6806
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:20985
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:15754
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:11182
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)
Definition: tablecmds.c:10865
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition: tablecmds.c:7636
static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd **cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:7207
AlterTablePass
Definition: tablecmds.c:149
@ AT_PASS_ADD_CONSTR
Definition: tablecmds.c:158
@ AT_PASS_ADD_OTHERCONSTR
Definition: tablecmds.c:162
@ AT_PASS_OLD_CONSTR
Definition: tablecmds.c:156
@ AT_PASS_ADD_COL
Definition: tablecmds.c:153
@ AT_PASS_OLD_INDEX
Definition: tablecmds.c:155
@ AT_PASS_ALTER_TYPE
Definition: tablecmds.c:152
@ AT_PASS_ADD_INDEXCONSTR
Definition: tablecmds.c:160
@ AT_PASS_DROP
Definition: tablecmds.c:151
@ AT_PASS_MISC
Definition: tablecmds.c:163
@ AT_PASS_COL_ATTRS
Definition: tablecmds.c:159
@ AT_PASS_SET_EXPRESSION
Definition: tablecmds.c:154
@ AT_PASS_ADD_INDEX
Definition: tablecmds.c:161
@ AT_PASS_UNSET
Definition: tablecmds.c:150
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:14661
static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid, bool hasperiod)
Definition: tablecmds.c:13632
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:17355
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:13055
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3740
void remove_on_commit_action(Oid relid)
Definition: tablecmds.c:19209
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:21354
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16391
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:334
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:17286
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:255
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
Definition: tablecmds.c:18733
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8567
ObjectAddress DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
Definition: tablecmds.c:764
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:13732
static void AlterConstrTriggerDeferrability(Oid conoid, Relation tgrel, Relation rel, bool deferrable, bool initdeferred, List **otherrelids)
Definition: tablecmds.c:12653
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:9464
static void DropClonedTriggersFromPartition(Oid partitionId)
Definition: tablecmds.c:21396
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:20079
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:18128
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1692
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4553
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:12033
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:7133
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:17737
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:21885
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9669
#define ATT_VIEW
Definition: tablecmds.c:329
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:19043
#define ATT_PARTITIONED_TABLE
Definition: tablecmds.c:336
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5366
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)
Definition: tablecmds.c:12517
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:18402
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:13930
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:17151
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:21319
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:9158
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20152
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:7084
Oid AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
Definition: tablecmds.c:16897
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
Definition: tablecmds.c:13320
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8230
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:16437
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:13950
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9585
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:18656
static PartitionSpec * transformPartitionSpec(Relation rel, PartitionSpec *partspec)
Definition: tablecmds.c:19639
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4895
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:17059
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:7903
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4260
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition: tablecmds.c:7830
static bool tryAttachPartitionForeignKey(List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)
Definition: tablecmds.c:11659
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:16858
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:14311
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
Definition: tablecmds.c:13265
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:16225
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:332
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:21523
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:19961
void RangeVarCallbackMaintainsTable(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19406
static List * on_commits
Definition: tablecmds.c:131
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:17383
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9648
ObjectAddress RenameConstraint(RenameStmt *stmt)
Definition: tablecmds.c:4146
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5828
void register_on_commit_action(Oid relid, OnCommitAction action)
Definition: tablecmds.c:19173
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19442
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:16359
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:21972
void RangeVarCallbackOwnsRelation(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:19466
#define ATT_MATVIEW
Definition: tablecmds.c:330
#define child_dependency_type(child_is_partition)
Definition: tablecmds.c:365
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:21685
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1975
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
Definition: tablecmds.c:6116
static List * MergeAttributes(List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
Definition: tablecmds.c:2536
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:17134
struct NewColumnValue NewColumnValue
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:16403
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:8079
const char * GetCompressionMethodName(char method)
char CompressionNameToMethod(const char *compression)
#define CompressionMethodIsValid(cm)
#define InvalidCompressionMethod
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition: toasting.c:58
void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3231
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: trigger.c:1726
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition: trigger.c:2277
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition: trigger.c:3278
ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition: trigger.c:160
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition: trigger.c:1220
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition: trigger.c:177
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:6024
void AfterTriggerEndQuery(EState *estate)
Definition: trigger.c:5088
void AfterTriggerBeginQuery(void)
Definition: trigger.c:5053
#define RI_TRIGGER_FK
Definition: trigger.h:284
#define TRIGGER_FIRES_ON_ORIGIN
Definition: trigger.h:149
#define TRIGGER_DISABLED
Definition: trigger.h:152
#define TRIGGER_FIRES_ON_REPLICA
Definition: trigger.h:151
#define TRIGGER_EVENT_ROW
Definition: trigger.h:98
#define TRIGGER_FIRES_ALWAYS
Definition: trigger.h:150
#define TRIGGER_EVENT_INSERT
Definition: trigger.h:92
#define RI_TRIGGER_PK
Definition: trigger.h:283
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:333
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition: tupdesc.c:1085
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:175
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition: tupdesc.c:117
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition: tupdesc.c:1019
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:835
#define ReleaseTupleDesc(tupdesc)
Definition: tupdesc.h:219
#define ATTNULLABLE_VALID
Definition: tupdesc.h:86
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:160
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition: tupdesc.h:175
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:458
static void slot_getallattrs(TupleTableSlot *slot)
Definition: tuptable.h:372
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition: tuptable.h:385
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition: typcache.c:1922
bool DomainHasConstraints(Oid type_id)
Definition: typcache.c:1489
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3978
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition: typecmds.c:2927
void checkDomainOwner(HeapTuple tup)
Definition: typecmds.c:3477
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4147
List * roleSpecsToIds(List *memberNames)
Definition: user.c:1652
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition: usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition: usercontext.c:87
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition: utility.c:1959
#define MAX_STATISTICS_TARGET
Definition: vacuum.h:324
String * makeString(char *str)
Definition: value.c:63
#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
#define VARDATA_ANY(PTR)
Definition: varatt.h:324
const char * name
SubTransactionId GetCurrentSubTransactionId(void)
Definition: xact.c:791
void CommandCounterIncrement(void)
Definition: xact.c:1100
void StartTransactionCommand(void)
Definition: xact.c:3059
void CommitTransactionCommand(void)
Definition: xact.c:3157
int MyXactFlags
Definition: xact.c:136
CommandId GetCurrentCommandId(bool used)
Definition: xact.c:829
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition: xact.h:102
#define XLogLogicalInfoActive()
Definition: xlog.h:126
#define XLOG_INCLUDE_ORIGIN
Definition: xlog.h:154
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition: xloginsert.c:474
void XLogRegisterData(const void *data, uint32 len)
Definition: xloginsert.c:364
void XLogSetRecordFlags(uint8 flags)
Definition: xloginsert.c:456
void XLogBeginInsert(void)
Definition: xloginsert.c:149