Move routines to manipulate WAL into PostgreSQL::Test::Cluster
[pgsql.git] / src / backend / commands / tablecmds.c
blobd02d564883ae2a00366855098df2a52b38f7b1f5
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
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"
27 #include "access/toast_compression.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"
35 #include "catalog/objectaccess.h"
36 #include "catalog/partition.h"
37 #include "catalog/pg_am.h"
38 #include "catalog/pg_attrdef.h"
39 #include "catalog/pg_collation.h"
40 #include "catalog/pg_constraint.h"
41 #include "catalog/pg_depend.h"
42 #include "catalog/pg_foreign_table.h"
43 #include "catalog/pg_inherits.h"
44 #include "catalog/pg_largeobject.h"
45 #include "catalog/pg_namespace.h"
46 #include "catalog/pg_opclass.h"
47 #include "catalog/pg_policy.h"
48 #include "catalog/pg_proc.h"
49 #include "catalog/pg_publication_rel.h"
50 #include "catalog/pg_rewrite.h"
51 #include "catalog/pg_statistic_ext.h"
52 #include "catalog/pg_tablespace.h"
53 #include "catalog/pg_trigger.h"
54 #include "catalog/pg_type.h"
55 #include "catalog/storage.h"
56 #include "catalog/storage_xlog.h"
57 #include "catalog/toasting.h"
58 #include "commands/cluster.h"
59 #include "commands/comment.h"
60 #include "commands/defrem.h"
61 #include "commands/event_trigger.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"
79 #include "parser/parse_collate.h"
80 #include "parser/parse_expr.h"
81 #include "parser/parse_relation.h"
82 #include "parser/parse_type.h"
83 #include "parser/parse_utilcmd.h"
84 #include "parser/parser.h"
85 #include "partitioning/partbounds.h"
86 #include "partitioning/partdesc.h"
87 #include "pgstat.h"
88 #include "rewrite/rewriteDefine.h"
89 #include "rewrite/rewriteHandler.h"
90 #include "rewrite/rewriteManip.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"
113 * ON COMMIT action list
115 typedef struct OnCommitItem
117 Oid relid; /* relid of relation */
118 OnCommitAction oncommit; /* what to do at end of xact */
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.
127 SubTransactionId creating_subid;
128 SubTransactionId deleting_subid;
129 } OnCommitItem;
131 static List *on_commits = NIL;
135 * State information for ALTER TABLE
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.
144 * Phase 2 is divided into multiple passes; subcommands are executed in
145 * a pass determined by subcommand type.
148 typedef enum AlterTablePass
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 */
164 } AlterTablePass;
166 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
168 typedef struct AlteredTableInfo
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 */
176 * Transiently set during Phase 2, normally set to NULL.
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.
182 Relation rel;
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 */
200 bool validate_default;
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 */
210 } AlteredTableInfo;
212 /* Struct describing one new constraint to check in Phase 3 scan */
213 /* Note: new not-null constraints are handled elsewhere */
214 typedef struct NewConstraint
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 */
224 } NewConstraint;
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.
234 typedef struct NewColumnValue
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? */
240 } NewColumnValue;
243 * Error-reporting support for RemoveRelations
245 struct dropmsgstrings
247 char kind;
248 int nonexistent_code;
249 const char *nonexistent_msg;
250 const char *skipping_msg;
251 const char *nota_msg;
252 const char *drophint_msg;
255 static const struct dropmsgstrings dropmsgstringarray[] = {
256 {RELKIND_RELATION,
257 ERRCODE_UNDEFINED_TABLE,
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,
263 ERRCODE_UNDEFINED_TABLE,
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,
269 ERRCODE_UNDEFINED_TABLE,
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,
275 ERRCODE_UNDEFINED_TABLE,
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,
299 ERRCODE_UNDEFINED_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}
313 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
314 struct DropRelationCallbackState
316 /* These fields are set by RemoveRelations: */
317 char expected_relkind;
318 LOCKMODE heap_lockmode;
319 /* These fields are state to track which subsidiary locks are held: */
320 Oid heapOid;
321 Oid partParentOid;
322 /* These fields are passed back by RangeVarCallbackForDropRelation: */
323 char actual_relkind;
324 char actual_relpersistence;
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
339 * ForeignTruncateInfo
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.
346 typedef struct ForeignTruncateInfo
348 Oid serverid;
349 List *rels;
350 } ForeignTruncateInfo;
352 /* Partial or complete FK creation in addFkConstraint() */
353 typedef enum addFkConstraintSides
355 addFkReferencedSide,
356 addFkReferencingSide,
357 addFkBothSides,
358 } addFkConstraintSides;
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.
365 #define child_dependency_type(child_is_partition) \
366 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
368 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
369 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
370 static void truncate_check_activity(Relation rel);
371 static void RangeVarCallbackForTruncate(const RangeVar *relation,
372 Oid relId, Oid oldRelId, void *arg);
373 static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
374 bool is_partition, List **supconstr,
375 List **supnotnulls);
376 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced);
377 static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
378 static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
379 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
380 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
381 static void StoreCatalogInheritance(Oid relationId, List *supers,
382 bool child_is_partition);
383 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
384 int32 seqNumber, Relation inhRelation,
385 bool child_is_partition);
386 static int findAttrByName(const char *attributeName, const List *columns);
387 static void AlterIndexNamespaces(Relation classRel, Relation rel,
388 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
389 static void AlterSeqNamespaces(Relation classRel, Relation rel,
390 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
391 LOCKMODE lockmode);
392 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
393 bool recurse, bool recursing, LOCKMODE lockmode);
394 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
395 Relation rel, HeapTuple contuple, List **otherrelids,
396 LOCKMODE lockmode);
397 static ObjectAddress ATExecValidateConstraint(List **wqueue,
398 Relation rel, char *constrName,
399 bool recurse, bool recursing, LOCKMODE lockmode);
400 static int transformColumnNameList(Oid relId, List *colList,
401 int16 *attnums, Oid *atttypids, Oid *attcollids);
402 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
403 List **attnamelist,
404 int16 *attnums, Oid *atttypids, Oid *attcollids,
405 Oid *opclasses, bool *pk_has_without_overlaps);
406 static Oid transformFkeyCheckAttrs(Relation pkrel,
407 int numattrs, int16 *attnums,
408 bool with_period, Oid *opclasses,
409 bool *pk_has_without_overlaps);
410 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
411 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
412 Oid *funcid);
413 static void validateForeignKeyConstraint(char *conname,
414 Relation rel, Relation pkrel,
415 Oid pkindOid, Oid constraintOid, bool hasperiod);
416 static void CheckAlterTableIsSafe(Relation rel);
417 static void ATController(AlterTableStmt *parsetree,
418 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
419 AlterTableUtilityContext *context);
420 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
421 bool recurse, bool recursing, LOCKMODE lockmode,
422 AlterTableUtilityContext *context);
423 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
424 AlterTableUtilityContext *context);
425 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
426 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
427 AlterTableUtilityContext *context);
428 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
429 Relation rel, AlterTableCmd *cmd,
430 bool recurse, LOCKMODE lockmode,
431 AlterTablePass cur_pass,
432 AlterTableUtilityContext *context);
433 static void ATRewriteTables(AlterTableStmt *parsetree,
434 List **wqueue, LOCKMODE lockmode,
435 AlterTableUtilityContext *context);
436 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap);
437 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
438 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
439 static void ATSimpleRecursion(List **wqueue, Relation rel,
440 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
441 AlterTableUtilityContext *context);
442 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
443 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
444 LOCKMODE lockmode,
445 AlterTableUtilityContext *context);
446 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
447 DropBehavior behavior);
448 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
449 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
450 AlterTableUtilityContext *context);
451 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
452 Relation rel, AlterTableCmd **cmd,
453 bool recurse, bool recursing,
454 LOCKMODE lockmode, AlterTablePass cur_pass,
455 AlterTableUtilityContext *context);
456 static bool check_for_column_name_collision(Relation rel, const char *colname,
457 bool if_not_exists);
458 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
459 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
460 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
461 LOCKMODE lockmode);
462 static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
463 LOCKMODE lockmode);
464 static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
465 char *constrname, char *colName,
466 bool recurse, bool recursing,
467 LOCKMODE lockmode);
468 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
469 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
470 List *testConstraint, List *provenConstraint);
471 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
472 Node *newDefault, LOCKMODE lockmode);
473 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
474 Node *newDefault);
475 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
476 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
477 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
478 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
479 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
480 bool recurse, bool recursing);
481 static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
482 Node *newExpr, LOCKMODE lockmode);
483 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
484 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
485 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
486 Node *newValue, LOCKMODE lockmode);
487 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
488 Node *options, bool isReset, LOCKMODE lockmode);
489 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
490 Node *newValue, LOCKMODE lockmode);
491 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
492 AlterTableCmd *cmd, LOCKMODE lockmode,
493 AlterTableUtilityContext *context);
494 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
495 DropBehavior behavior,
496 bool recurse, bool recursing,
497 bool missing_ok, LOCKMODE lockmode,
498 ObjectAddresses *addrs);
499 static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
500 bool recurse, LOCKMODE lockmode,
501 AlterTableUtilityContext *context);
502 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
503 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
504 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
505 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
506 static ObjectAddress ATExecAddConstraint(List **wqueue,
507 AlteredTableInfo *tab, Relation rel,
508 Constraint *newConstraint, bool recurse, bool is_readd,
509 LOCKMODE lockmode);
510 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
511 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
512 IndexStmt *stmt, LOCKMODE lockmode);
513 static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
514 AlteredTableInfo *tab, Relation rel,
515 Constraint *constr,
516 bool recurse, bool recursing, bool is_readd,
517 LOCKMODE lockmode);
518 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
519 Relation rel, Constraint *fkconstraint,
520 bool recurse, bool recursing,
521 LOCKMODE lockmode);
522 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
523 int numfksetcols, const int16 *fksetcolsattnums,
524 List *fksetcols);
525 static ObjectAddress addFkConstraint(addFkConstraintSides fkside,
526 char *constraintname,
527 Constraint *fkconstraint, Relation rel,
528 Relation pkrel, Oid indexOid,
529 Oid parentConstr,
530 int numfks, int16 *pkattnum, int16 *fkattnum,
531 Oid *pfeqoperators, Oid *ppeqoperators,
532 Oid *ffeqoperators, int numfkdelsetcols,
533 int16 *fkdelsetcols, bool is_internal,
534 bool with_period);
535 static void addFkRecurseReferenced(Constraint *fkconstraint,
536 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
537 int numfks, int16 *pkattnum, int16 *fkattnum,
538 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
539 int numfkdelsetcols, int16 *fkdelsetcols,
540 bool old_check_ok,
541 Oid parentDelTrigger, Oid parentUpdTrigger,
542 bool with_period);
543 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
544 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
545 int numfks, int16 *pkattnum, int16 *fkattnum,
546 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
547 int numfkdelsetcols, int16 *fkdelsetcols,
548 bool old_check_ok, LOCKMODE lockmode,
549 Oid parentInsTrigger, Oid parentUpdTrigger,
550 bool with_period);
551 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
552 Relation partitionRel);
553 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
554 static void CloneFkReferencing(List **wqueue, Relation parentRel,
555 Relation partRel);
556 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
557 Constraint *fkconstraint, Oid constraintOid,
558 Oid indexOid,
559 Oid parentInsTrigger, Oid parentUpdTrigger,
560 Oid *insertTrigOid, Oid *updateTrigOid);
561 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
562 Constraint *fkconstraint, Oid constraintOid,
563 Oid indexOid,
564 Oid parentDelTrigger, Oid parentUpdTrigger,
565 Oid *deleteTrigOid, Oid *updateTrigOid);
566 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
567 Oid partRelid,
568 Oid parentConstrOid, int numfks,
569 AttrNumber *mapped_conkey, AttrNumber *confkey,
570 Oid *conpfeqop,
571 Oid parentInsTrigger,
572 Oid parentUpdTrigger,
573 Relation trigrel);
574 static void GetForeignKeyActionTriggers(Relation trigrel,
575 Oid conoid, Oid confrelid, Oid conrelid,
576 Oid *deleteTriggerOid,
577 Oid *updateTriggerOid);
578 static void GetForeignKeyCheckTriggers(Relation trigrel,
579 Oid conoid, Oid confrelid, Oid conrelid,
580 Oid *insertTriggerOid,
581 Oid *updateTriggerOid);
582 static void ATExecDropConstraint(Relation rel, const char *constrName,
583 DropBehavior behavior, bool recurse,
584 bool missing_ok, LOCKMODE lockmode);
585 static ObjectAddress dropconstraint_internal(Relation rel,
586 HeapTuple constraintTup, DropBehavior behavior,
587 bool recurse, bool recursing,
588 bool missing_ok, LOCKMODE lockmode);
589 static void ATPrepAlterColumnType(List **wqueue,
590 AlteredTableInfo *tab, Relation rel,
591 bool recurse, bool recursing,
592 AlterTableCmd *cmd, LOCKMODE lockmode,
593 AlterTableUtilityContext *context);
594 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
595 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
596 AlterTableCmd *cmd, LOCKMODE lockmode);
597 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
598 Relation rel, AttrNumber attnum, const char *colName);
599 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
600 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
601 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
602 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
603 LOCKMODE lockmode);
604 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
605 char *cmd, List **wqueue, LOCKMODE lockmode,
606 bool rewrite);
607 static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
608 Oid objid, Relation rel, List *domname,
609 const char *conname);
610 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
611 static void TryReuseForeignKey(Oid oldId, Constraint *con);
612 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
613 List *options, LOCKMODE lockmode);
614 static void change_owner_fix_column_acls(Oid relationOid,
615 Oid oldOwnerId, Oid newOwnerId);
616 static void change_owner_recurse_to_sequences(Oid relationOid,
617 Oid newOwnerId, LOCKMODE lockmode);
618 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
619 LOCKMODE lockmode);
620 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
621 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
622 static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
623 static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel,
624 bool toLogged);
625 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
626 const char *tablespacename, LOCKMODE lockmode);
627 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
628 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
629 static void ATExecSetRelOptions(Relation rel, List *defList,
630 AlterTableType operation,
631 LOCKMODE lockmode);
632 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
633 char fires_when, bool skip_system, bool recurse,
634 LOCKMODE lockmode);
635 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
636 char fires_when, LOCKMODE lockmode);
637 static void ATPrepAddInherit(Relation child_rel);
638 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
639 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
640 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
641 DependencyType deptype);
642 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
643 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
644 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
645 static void ATExecGenericOptions(Relation rel, List *options);
646 static void ATExecSetRowSecurity(Relation rel, bool rls);
647 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
648 static ObjectAddress ATExecSetCompression(Relation rel,
649 const char *column, Node *newValue, LOCKMODE lockmode);
651 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
652 static const char *storage_name(char c);
654 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
655 Oid oldRelOid, void *arg);
656 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
657 Oid oldrelid, void *arg);
658 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
659 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
660 List **partexprs, Oid *partopclass, Oid *partcollation,
661 PartitionStrategy strategy);
662 static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
663 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
664 bool expect_detached);
665 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
666 PartitionCmd *cmd,
667 AlterTableUtilityContext *context);
668 static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
669 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
670 List *partConstraint,
671 bool validate_default);
672 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
673 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
674 static void DropClonedTriggersFromPartition(Oid partitionId);
675 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
676 Relation rel, RangeVar *name,
677 bool concurrent);
678 static void DetachPartitionFinalize(Relation rel, Relation partRel,
679 bool concurrent, Oid defaultPartOid);
680 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
681 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
682 RangeVar *name);
683 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
684 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
685 Relation partitionTbl);
686 static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
687 static List *GetParentedForeignKeyRefs(Relation partition);
688 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
689 static char GetAttributeCompression(Oid atttypid, const char *compression);
690 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
693 /* ----------------------------------------------------------------
694 * DefineRelation
695 * Creates a new relation.
697 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
698 * The other arguments are used to extend the behavior for other cases:
699 * relkind: relkind to assign to the new relation
700 * ownerId: if not InvalidOid, use this as the new relation's owner.
701 * typaddress: if not null, it's set to the pg_type entry's address.
702 * queryString: for error reporting
704 * Note that permissions checks are done against current user regardless of
705 * ownerId. A nonzero ownerId is used when someone is creating a relation
706 * "on behalf of" someone else, so we still want to see that the current user
707 * has permissions to do it.
709 * If successful, returns the address of the new relation.
710 * ----------------------------------------------------------------
712 ObjectAddress
713 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
714 ObjectAddress *typaddress, const char *queryString)
716 char relname[NAMEDATALEN];
717 Oid namespaceId;
718 Oid relationId;
719 Oid tablespaceId;
720 Relation rel;
721 TupleDesc descriptor;
722 List *inheritOids;
723 List *old_constraints;
724 List *old_notnulls;
725 List *rawDefaults;
726 List *cookedDefaults;
727 List *nncols;
728 Datum reloptions;
729 ListCell *listptr;
730 AttrNumber attnum;
731 bool partitioned;
732 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
733 Oid ofTypeId;
734 ObjectAddress address;
735 LOCKMODE parentLockmode;
736 Oid accessMethodId = InvalidOid;
739 * Truncate relname to appropriate length (probably a waste of time, as
740 * parser should have done this already).
742 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
745 * Check consistency of arguments
747 if (stmt->oncommit != ONCOMMIT_NOOP
748 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
749 ereport(ERROR,
750 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
751 errmsg("ON COMMIT can only be used on temporary tables")));
753 if (stmt->partspec != NULL)
755 if (relkind != RELKIND_RELATION)
756 elog(ERROR, "unexpected relkind: %d", (int) relkind);
758 relkind = RELKIND_PARTITIONED_TABLE;
759 partitioned = true;
761 else
762 partitioned = false;
764 if (relkind == RELKIND_PARTITIONED_TABLE &&
765 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
766 ereport(ERROR,
767 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
768 errmsg("partitioned tables cannot be unlogged")));
771 * Look up the namespace in which we are supposed to create the relation,
772 * check we have permission to create there, lock it against concurrent
773 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
774 * namespace is selected.
776 namespaceId =
777 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
780 * Security check: disallow creating temp tables from security-restricted
781 * code. This is needed because calling code might not expect untrusted
782 * tables to appear in pg_temp at the front of its search path.
784 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
785 && InSecurityRestrictedOperation())
786 ereport(ERROR,
787 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
788 errmsg("cannot create temporary table within security-restricted operation")));
791 * Determine the lockmode to use when scanning parents. A self-exclusive
792 * lock is needed here.
794 * For regular inheritance, if two backends attempt to add children to the
795 * same parent simultaneously, and that parent has no pre-existing
796 * children, then both will attempt to update the parent's relhassubclass
797 * field, leading to a "tuple concurrently updated" error. Also, this
798 * interlocks against a concurrent ANALYZE on the parent table, which
799 * might otherwise be attempting to clear the parent's relhassubclass
800 * field, if its previous children were recently dropped.
802 * If the child table is a partition, then we instead grab an exclusive
803 * lock on the parent because its partition descriptor will be changed by
804 * addition of the new partition.
806 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
807 ShareUpdateExclusiveLock);
809 /* Determine the list of OIDs of the parents. */
810 inheritOids = NIL;
811 foreach(listptr, stmt->inhRelations)
813 RangeVar *rv = (RangeVar *) lfirst(listptr);
814 Oid parentOid;
816 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
819 * Reject duplications in the list of parents.
821 if (list_member_oid(inheritOids, parentOid))
822 ereport(ERROR,
823 (errcode(ERRCODE_DUPLICATE_TABLE),
824 errmsg("relation \"%s\" would be inherited from more than once",
825 get_rel_name(parentOid))));
827 inheritOids = lappend_oid(inheritOids, parentOid);
831 * Select tablespace to use: an explicitly indicated one, or (in the case
832 * of a partitioned table) the parent's, if it has one.
834 if (stmt->tablespacename)
836 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
838 if (partitioned && tablespaceId == MyDatabaseTableSpace)
839 ereport(ERROR,
840 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
841 errmsg("cannot specify default tablespace for partitioned relations")));
843 else if (stmt->partbound)
845 Assert(list_length(inheritOids) == 1);
846 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
848 else
849 tablespaceId = InvalidOid;
851 /* still nothing? use the default */
852 if (!OidIsValid(tablespaceId))
853 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
854 partitioned);
856 /* Check permissions except when using database's default */
857 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
859 AclResult aclresult;
861 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
862 ACL_CREATE);
863 if (aclresult != ACLCHECK_OK)
864 aclcheck_error(aclresult, OBJECT_TABLESPACE,
865 get_tablespace_name(tablespaceId));
868 /* In all cases disallow placing user relations in pg_global */
869 if (tablespaceId == GLOBALTABLESPACE_OID)
870 ereport(ERROR,
871 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
872 errmsg("only shared relations can be placed in pg_global tablespace")));
874 /* Identify user ID that will own the table */
875 if (!OidIsValid(ownerId))
876 ownerId = GetUserId();
879 * Parse and validate reloptions, if any.
881 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
882 true, false);
884 switch (relkind)
886 case RELKIND_VIEW:
887 (void) view_reloptions(reloptions, true);
888 break;
889 case RELKIND_PARTITIONED_TABLE:
890 (void) partitioned_table_reloptions(reloptions, true);
891 break;
892 default:
893 (void) heap_reloptions(relkind, reloptions, true);
896 if (stmt->ofTypename)
898 AclResult aclresult;
900 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
902 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
903 if (aclresult != ACLCHECK_OK)
904 aclcheck_error_type(aclresult, ofTypeId);
906 else
907 ofTypeId = InvalidOid;
910 * Look up inheritance ancestors and generate relation schema, including
911 * inherited attributes. (Note that stmt->tableElts is destructively
912 * modified by MergeAttributes.)
914 stmt->tableElts =
915 MergeAttributes(stmt->tableElts, inheritOids,
916 stmt->relation->relpersistence,
917 stmt->partbound != NULL,
918 &old_constraints, &old_notnulls);
921 * Create a tuple descriptor from the relation schema. Note that this
922 * deals with column names, types, and in-descriptor NOT NULL flags, but
923 * not default values, NOT NULL or CHECK constraints; we handle those
924 * below.
926 descriptor = BuildDescForRelation(stmt->tableElts);
929 * Find columns with default values and prepare for insertion of the
930 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
931 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
932 * while raw defaults go into a list of RawColumnDefault structs that will
933 * be processed by AddRelationNewConstraints. (We can't deal with raw
934 * expressions until we can do transformExpr.)
936 * We can set the atthasdef flags now in the tuple descriptor; this just
937 * saves StoreAttrDefault from having to do an immediate update of the
938 * pg_attribute rows.
940 rawDefaults = NIL;
941 cookedDefaults = NIL;
942 attnum = 0;
944 foreach(listptr, stmt->tableElts)
946 ColumnDef *colDef = lfirst(listptr);
947 Form_pg_attribute attr;
949 attnum++;
950 attr = TupleDescAttr(descriptor, attnum - 1);
952 if (colDef->raw_default != NULL)
954 RawColumnDefault *rawEnt;
956 Assert(colDef->cooked_default == NULL);
958 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
959 rawEnt->attnum = attnum;
960 rawEnt->raw_default = colDef->raw_default;
961 rawEnt->missingMode = false;
962 rawEnt->generated = colDef->generated;
963 rawDefaults = lappend(rawDefaults, rawEnt);
964 attr->atthasdef = true;
966 else if (colDef->cooked_default != NULL)
968 CookedConstraint *cooked;
970 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
971 cooked->contype = CONSTR_DEFAULT;
972 cooked->conoid = InvalidOid; /* until created */
973 cooked->name = NULL;
974 cooked->attnum = attnum;
975 cooked->expr = colDef->cooked_default;
976 cooked->is_enforced = true;
977 cooked->skip_validation = false;
978 cooked->is_local = true; /* not used for defaults */
979 cooked->inhcount = 0; /* ditto */
980 cooked->is_no_inherit = false;
981 cookedDefaults = lappend(cookedDefaults, cooked);
982 attr->atthasdef = true;
985 populate_compact_attribute(descriptor, attnum - 1);
989 * For relations with table AM and partitioned tables, select access
990 * method to use: an explicitly indicated one, or (in the case of a
991 * partitioned table) the parent's, if it has one.
993 if (stmt->accessMethod != NULL)
995 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
996 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
998 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1000 if (stmt->partbound)
1002 Assert(list_length(inheritOids) == 1);
1003 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
1006 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
1007 accessMethodId = get_table_am_oid(default_table_access_method, false);
1011 * Create the relation. Inherited defaults and CHECK constraints are
1012 * passed in for immediate handling --- since they don't need parsing,
1013 * they can be stored immediately.
1015 relationId = heap_create_with_catalog(relname,
1016 namespaceId,
1017 tablespaceId,
1018 InvalidOid,
1019 InvalidOid,
1020 ofTypeId,
1021 ownerId,
1022 accessMethodId,
1023 descriptor,
1024 list_concat(cookedDefaults,
1025 old_constraints),
1026 relkind,
1027 stmt->relation->relpersistence,
1028 false,
1029 false,
1030 stmt->oncommit,
1031 reloptions,
1032 true,
1033 allowSystemTableMods,
1034 false,
1035 InvalidOid,
1036 typaddress);
1039 * We must bump the command counter to make the newly-created relation
1040 * tuple visible for opening.
1042 CommandCounterIncrement();
1045 * Open the new relation and acquire exclusive lock on it. This isn't
1046 * really necessary for locking out other backends (since they can't see
1047 * the new rel anyway until we commit), but it keeps the lock manager from
1048 * complaining about deadlock risks.
1050 rel = relation_open(relationId, AccessExclusiveLock);
1053 * Now add any newly specified column default and generation expressions
1054 * to the new relation. These are passed to us in the form of raw
1055 * parsetrees; we need to transform them to executable expression trees
1056 * before they can be added. The most convenient way to do that is to
1057 * apply the parser's transformExpr routine, but transformExpr doesn't
1058 * work unless we have a pre-existing relation. So, the transformation has
1059 * to be postponed to this final step of CREATE TABLE.
1061 * This needs to be before processing the partitioning clauses because
1062 * those could refer to generated columns.
1064 if (rawDefaults)
1065 AddRelationNewConstraints(rel, rawDefaults, NIL,
1066 true, true, false, queryString);
1069 * Make column generation expressions visible for use by partitioning.
1071 CommandCounterIncrement();
1073 /* Process and store partition bound, if any. */
1074 if (stmt->partbound)
1076 PartitionBoundSpec *bound;
1077 ParseState *pstate;
1078 Oid parentId = linitial_oid(inheritOids),
1079 defaultPartOid;
1080 Relation parent,
1081 defaultRel = NULL;
1082 ParseNamespaceItem *nsitem;
1084 /* Already have strong enough lock on the parent */
1085 parent = table_open(parentId, NoLock);
1088 * We are going to try to validate the partition bound specification
1089 * against the partition key of parentRel, so it better have one.
1091 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1092 ereport(ERROR,
1093 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1094 errmsg("\"%s\" is not partitioned",
1095 RelationGetRelationName(parent))));
1098 * The partition constraint of the default partition depends on the
1099 * partition bounds of every other partition. It is possible that
1100 * another backend might be about to execute a query on the default
1101 * partition table, and that the query relies on previously cached
1102 * default partition constraints. We must therefore take a table lock
1103 * strong enough to prevent all queries on the default partition from
1104 * proceeding until we commit and send out a shared-cache-inval notice
1105 * that will make them update their index lists.
1107 * Order of locking: The relation being added won't be visible to
1108 * other backends until it is committed, hence here in
1109 * DefineRelation() the order of locking the default partition and the
1110 * relation being added does not matter. But at all other places we
1111 * need to lock the default relation before we lock the relation being
1112 * added or removed i.e. we should take the lock in same order at all
1113 * the places such that lock parent, lock default partition and then
1114 * lock the partition so as to avoid a deadlock.
1116 defaultPartOid =
1117 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1118 true));
1119 if (OidIsValid(defaultPartOid))
1120 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1122 /* Transform the bound values */
1123 pstate = make_parsestate(NULL);
1124 pstate->p_sourcetext = queryString;
1127 * Add an nsitem containing this relation, so that transformExpr
1128 * called on partition bound expressions is able to report errors
1129 * using a proper context.
1131 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1132 NULL, false, false);
1133 addNSItemToQuery(pstate, nsitem, false, true, true);
1135 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1138 * Check first that the new partition's bound is valid and does not
1139 * overlap with any of existing partitions of the parent.
1141 check_new_partition_bound(relname, parent, bound, pstate);
1144 * If the default partition exists, its partition constraints will
1145 * change after the addition of this new partition such that it won't
1146 * allow any row that qualifies for this new partition. So, check that
1147 * the existing data in the default partition satisfies the constraint
1148 * as it will exist after adding this partition.
1150 if (OidIsValid(defaultPartOid))
1152 check_default_partition_contents(parent, defaultRel, bound);
1153 /* Keep the lock until commit. */
1154 table_close(defaultRel, NoLock);
1157 /* Update the pg_class entry. */
1158 StorePartitionBound(rel, parent, bound);
1160 table_close(parent, NoLock);
1163 /* Store inheritance information for new rel. */
1164 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1167 * Process the partitioning specification (if any) and store the partition
1168 * key information into the catalog.
1170 if (partitioned)
1172 ParseState *pstate;
1173 int partnatts;
1174 AttrNumber partattrs[PARTITION_MAX_KEYS];
1175 Oid partopclass[PARTITION_MAX_KEYS];
1176 Oid partcollation[PARTITION_MAX_KEYS];
1177 List *partexprs = NIL;
1179 pstate = make_parsestate(NULL);
1180 pstate->p_sourcetext = queryString;
1182 partnatts = list_length(stmt->partspec->partParams);
1184 /* Protect fixed-size arrays here and in executor */
1185 if (partnatts > PARTITION_MAX_KEYS)
1186 ereport(ERROR,
1187 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1188 errmsg("cannot partition using more than %d columns",
1189 PARTITION_MAX_KEYS)));
1192 * We need to transform the raw parsetrees corresponding to partition
1193 * expressions into executable expression trees. Like column defaults
1194 * and CHECK constraints, we could not have done the transformation
1195 * earlier.
1197 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1199 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1200 partattrs, &partexprs, partopclass,
1201 partcollation, stmt->partspec->strategy);
1203 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1204 partexprs,
1205 partopclass, partcollation);
1207 /* make it all visible */
1208 CommandCounterIncrement();
1212 * If we're creating a partition, create now all the indexes, triggers,
1213 * FKs defined in the parent.
1215 * We can't do it earlier, because DefineIndex wants to know the partition
1216 * key which we just stored.
1218 if (stmt->partbound)
1220 Oid parentId = linitial_oid(inheritOids);
1221 Relation parent;
1222 List *idxlist;
1223 ListCell *cell;
1225 /* Already have strong enough lock on the parent */
1226 parent = table_open(parentId, NoLock);
1227 idxlist = RelationGetIndexList(parent);
1230 * For each index in the parent table, create one in the partition
1232 foreach(cell, idxlist)
1234 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1235 AttrMap *attmap;
1236 IndexStmt *idxstmt;
1237 Oid constraintOid;
1239 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1241 if (idxRel->rd_index->indisunique)
1242 ereport(ERROR,
1243 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1244 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1245 RelationGetRelationName(parent)),
1246 errdetail("Table \"%s\" contains indexes that are unique.",
1247 RelationGetRelationName(parent))));
1248 else
1250 index_close(idxRel, AccessShareLock);
1251 continue;
1255 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1256 RelationGetDescr(parent),
1257 false);
1258 idxstmt =
1259 generateClonedIndexStmt(NULL, idxRel,
1260 attmap, &constraintOid);
1261 DefineIndex(RelationGetRelid(rel),
1262 idxstmt,
1263 InvalidOid,
1264 RelationGetRelid(idxRel),
1265 constraintOid,
1267 false, false, false, false, false);
1269 index_close(idxRel, AccessShareLock);
1272 list_free(idxlist);
1275 * If there are any row-level triggers, clone them to the new
1276 * partition.
1278 if (parent->trigdesc != NULL)
1279 CloneRowTriggersToPartition(parent, rel);
1282 * And foreign keys too. Note that because we're freshly creating the
1283 * table, there is no need to verify these new constraints.
1285 CloneForeignKeyConstraints(NULL, parent, rel);
1287 table_close(parent, NoLock);
1291 * Now add any newly specified CHECK constraints to the new relation. Same
1292 * as for defaults above, but these need to come after partitioning is set
1293 * up.
1295 if (stmt->constraints)
1296 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1297 true, true, false, queryString);
1300 * Finally, merge the not-null constraints that are declared directly with
1301 * those that come from parent relations (making sure to count inheritance
1302 * appropriately for each), create them, and set the attnotnull flag on
1303 * columns that don't yet have it.
1305 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1306 old_notnulls);
1307 foreach_int(attrnum, nncols)
1308 set_attnotnull(NULL, rel, attrnum, NoLock);
1310 ObjectAddressSet(address, RelationRelationId, relationId);
1313 * Clean up. We keep lock on new relation (although it shouldn't be
1314 * visible to anyone else anyway, until commit).
1316 relation_close(rel, NoLock);
1318 return address;
1322 * BuildDescForRelation
1324 * Given a list of ColumnDef nodes, build a TupleDesc.
1326 * Note: This is only for the limited purpose of table and view creation. Not
1327 * everything is filled in. A real tuple descriptor should be obtained from
1328 * the relcache.
1330 TupleDesc
1331 BuildDescForRelation(const List *columns)
1333 int natts;
1334 AttrNumber attnum;
1335 ListCell *l;
1336 TupleDesc desc;
1337 char *attname;
1338 Oid atttypid;
1339 int32 atttypmod;
1340 Oid attcollation;
1341 int attdim;
1344 * allocate a new tuple descriptor
1346 natts = list_length(columns);
1347 desc = CreateTemplateTupleDesc(natts);
1349 attnum = 0;
1351 foreach(l, columns)
1353 ColumnDef *entry = lfirst(l);
1354 AclResult aclresult;
1355 Form_pg_attribute att;
1358 * for each entry in the list, get the name and type information from
1359 * the list and have TupleDescInitEntry fill in the attribute
1360 * information we need.
1362 attnum++;
1364 attname = entry->colname;
1365 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1367 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1368 if (aclresult != ACLCHECK_OK)
1369 aclcheck_error_type(aclresult, atttypid);
1371 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1372 attdim = list_length(entry->typeName->arrayBounds);
1373 if (attdim > PG_INT16_MAX)
1374 ereport(ERROR,
1375 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1376 errmsg("too many array dimensions"));
1378 if (entry->typeName->setof)
1379 ereport(ERROR,
1380 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1381 errmsg("column \"%s\" cannot be declared SETOF",
1382 attname)));
1384 TupleDescInitEntry(desc, attnum, attname,
1385 atttypid, atttypmod, attdim);
1386 att = TupleDescAttr(desc, attnum - 1);
1388 /* Override TupleDescInitEntry's settings as requested */
1389 TupleDescInitEntryCollation(desc, attnum, attcollation);
1391 /* Fill in additional stuff not handled by TupleDescInitEntry */
1392 att->attnotnull = entry->is_not_null;
1393 att->attislocal = entry->is_local;
1394 att->attinhcount = entry->inhcount;
1395 att->attidentity = entry->identity;
1396 att->attgenerated = entry->generated;
1397 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1398 if (entry->storage)
1399 att->attstorage = entry->storage;
1400 else if (entry->storage_name)
1401 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1403 populate_compact_attribute(desc, attnum - 1);
1406 return desc;
1410 * Emit the right error or warning message for a "DROP" command issued on a
1411 * non-existent relation
1413 static void
1414 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1416 const struct dropmsgstrings *rentry;
1418 if (rel->schemaname != NULL &&
1419 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1421 if (!missing_ok)
1423 ereport(ERROR,
1424 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1425 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1427 else
1429 ereport(NOTICE,
1430 (errmsg("schema \"%s\" does not exist, skipping",
1431 rel->schemaname)));
1433 return;
1436 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1438 if (rentry->kind == rightkind)
1440 if (!missing_ok)
1442 ereport(ERROR,
1443 (errcode(rentry->nonexistent_code),
1444 errmsg(rentry->nonexistent_msg, rel->relname)));
1446 else
1448 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1449 break;
1454 Assert(rentry->kind != '\0'); /* Should be impossible */
1458 * Emit the right error message for a "DROP" command issued on a
1459 * relation of the wrong type
1461 static void
1462 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1464 const struct dropmsgstrings *rentry;
1465 const struct dropmsgstrings *wentry;
1467 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1468 if (rentry->kind == rightkind)
1469 break;
1470 Assert(rentry->kind != '\0');
1472 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1473 if (wentry->kind == wrongkind)
1474 break;
1475 /* wrongkind could be something we don't have in our table... */
1477 ereport(ERROR,
1478 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1479 errmsg(rentry->nota_msg, relname),
1480 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1484 * RemoveRelations
1485 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1486 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1488 void
1489 RemoveRelations(DropStmt *drop)
1491 ObjectAddresses *objects;
1492 char relkind;
1493 ListCell *cell;
1494 int flags = 0;
1495 LOCKMODE lockmode = AccessExclusiveLock;
1497 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1498 if (drop->concurrent)
1501 * Note that for temporary relations this lock may get upgraded later
1502 * on, but as no other session can access a temporary relation, this
1503 * is actually fine.
1505 lockmode = ShareUpdateExclusiveLock;
1506 Assert(drop->removeType == OBJECT_INDEX);
1507 if (list_length(drop->objects) != 1)
1508 ereport(ERROR,
1509 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1510 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1511 if (drop->behavior == DROP_CASCADE)
1512 ereport(ERROR,
1513 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1514 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1518 * First we identify all the relations, then we delete them in a single
1519 * performMultipleDeletions() call. This is to avoid unwanted DROP
1520 * RESTRICT errors if one of the relations depends on another.
1523 /* Determine required relkind */
1524 switch (drop->removeType)
1526 case OBJECT_TABLE:
1527 relkind = RELKIND_RELATION;
1528 break;
1530 case OBJECT_INDEX:
1531 relkind = RELKIND_INDEX;
1532 break;
1534 case OBJECT_SEQUENCE:
1535 relkind = RELKIND_SEQUENCE;
1536 break;
1538 case OBJECT_VIEW:
1539 relkind = RELKIND_VIEW;
1540 break;
1542 case OBJECT_MATVIEW:
1543 relkind = RELKIND_MATVIEW;
1544 break;
1546 case OBJECT_FOREIGN_TABLE:
1547 relkind = RELKIND_FOREIGN_TABLE;
1548 break;
1550 default:
1551 elog(ERROR, "unrecognized drop object type: %d",
1552 (int) drop->removeType);
1553 relkind = 0; /* keep compiler quiet */
1554 break;
1557 /* Lock and validate each relation; build a list of object addresses */
1558 objects = new_object_addresses();
1560 foreach(cell, drop->objects)
1562 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1563 Oid relOid;
1564 ObjectAddress obj;
1565 struct DropRelationCallbackState state;
1568 * These next few steps are a great deal like relation_openrv, but we
1569 * don't bother building a relcache entry since we don't need it.
1571 * Check for shared-cache-inval messages before trying to access the
1572 * relation. This is needed to cover the case where the name
1573 * identifies a rel that has been dropped and recreated since the
1574 * start of our transaction: if we don't flush the old syscache entry,
1575 * then we'll latch onto that entry and suffer an error later.
1577 AcceptInvalidationMessages();
1579 /* Look up the appropriate relation using namespace search. */
1580 state.expected_relkind = relkind;
1581 state.heap_lockmode = drop->concurrent ?
1582 ShareUpdateExclusiveLock : AccessExclusiveLock;
1583 /* We must initialize these fields to show that no locks are held: */
1584 state.heapOid = InvalidOid;
1585 state.partParentOid = InvalidOid;
1587 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1588 RangeVarCallbackForDropRelation,
1589 &state);
1591 /* Not there? */
1592 if (!OidIsValid(relOid))
1594 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1595 continue;
1599 * Decide if concurrent mode needs to be used here or not. The
1600 * callback retrieved the rel's persistence for us.
1602 if (drop->concurrent &&
1603 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1605 Assert(list_length(drop->objects) == 1 &&
1606 drop->removeType == OBJECT_INDEX);
1607 flags |= PERFORM_DELETION_CONCURRENTLY;
1611 * Concurrent index drop cannot be used with partitioned indexes,
1612 * either.
1614 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1615 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1616 ereport(ERROR,
1617 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1618 errmsg("cannot drop partitioned index \"%s\" concurrently",
1619 rel->relname)));
1622 * If we're told to drop a partitioned index, we must acquire lock on
1623 * all the children of its parent partitioned table before proceeding.
1624 * Otherwise we'd try to lock the child index partitions before their
1625 * tables, leading to potential deadlock against other sessions that
1626 * will lock those objects in the other order.
1628 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1629 (void) find_all_inheritors(state.heapOid,
1630 state.heap_lockmode,
1631 NULL);
1633 /* OK, we're ready to delete this one */
1634 obj.classId = RelationRelationId;
1635 obj.objectId = relOid;
1636 obj.objectSubId = 0;
1638 add_exact_object_address(&obj, objects);
1641 performMultipleDeletions(objects, drop->behavior, flags);
1643 free_object_addresses(objects);
1647 * Before acquiring a table lock, check whether we have sufficient rights.
1648 * In the case of DROP INDEX, also try to lock the table before the index.
1649 * Also, if the table to be dropped is a partition, we try to lock the parent
1650 * first.
1652 static void
1653 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1654 void *arg)
1656 HeapTuple tuple;
1657 struct DropRelationCallbackState *state;
1658 char expected_relkind;
1659 bool is_partition;
1660 Form_pg_class classform;
1661 LOCKMODE heap_lockmode;
1662 bool invalid_system_index = false;
1664 state = (struct DropRelationCallbackState *) arg;
1665 heap_lockmode = state->heap_lockmode;
1668 * If we previously locked some other index's heap, and the name we're
1669 * looking up no longer refers to that relation, release the now-useless
1670 * lock.
1672 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1674 UnlockRelationOid(state->heapOid, heap_lockmode);
1675 state->heapOid = InvalidOid;
1679 * Similarly, if we previously locked some other partition's heap, and the
1680 * name we're looking up no longer refers to that relation, release the
1681 * now-useless lock.
1683 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1685 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1686 state->partParentOid = InvalidOid;
1689 /* Didn't find a relation, so no need for locking or permission checks. */
1690 if (!OidIsValid(relOid))
1691 return;
1693 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1694 if (!HeapTupleIsValid(tuple))
1695 return; /* concurrently dropped, so nothing to do */
1696 classform = (Form_pg_class) GETSTRUCT(tuple);
1697 is_partition = classform->relispartition;
1699 /* Pass back some data to save lookups in RemoveRelations */
1700 state->actual_relkind = classform->relkind;
1701 state->actual_relpersistence = classform->relpersistence;
1704 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1705 * but RemoveRelations() can only pass one relkind for a given relation.
1706 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1707 * That means we must be careful before giving the wrong type error when
1708 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1709 * exists with indexes.
1711 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1712 expected_relkind = RELKIND_RELATION;
1713 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1714 expected_relkind = RELKIND_INDEX;
1715 else
1716 expected_relkind = classform->relkind;
1718 if (state->expected_relkind != expected_relkind)
1719 DropErrorMsgWrongType(rel->relname, classform->relkind,
1720 state->expected_relkind);
1722 /* Allow DROP to either table owner or schema owner */
1723 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1724 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1725 aclcheck_error(ACLCHECK_NOT_OWNER,
1726 get_relkind_objtype(classform->relkind),
1727 rel->relname);
1730 * Check the case of a system index that might have been invalidated by a
1731 * failed concurrent process and allow its drop. For the time being, this
1732 * only concerns indexes of toast relations that became invalid during a
1733 * REINDEX CONCURRENTLY process.
1735 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1737 HeapTuple locTuple;
1738 Form_pg_index indexform;
1739 bool indisvalid;
1741 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1742 if (!HeapTupleIsValid(locTuple))
1744 ReleaseSysCache(tuple);
1745 return;
1748 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1749 indisvalid = indexform->indisvalid;
1750 ReleaseSysCache(locTuple);
1752 /* Mark object as being an invalid index of system catalogs */
1753 if (!indisvalid)
1754 invalid_system_index = true;
1757 /* In the case of an invalid index, it is fine to bypass this check */
1758 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1759 ereport(ERROR,
1760 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1761 errmsg("permission denied: \"%s\" is a system catalog",
1762 rel->relname)));
1764 ReleaseSysCache(tuple);
1767 * In DROP INDEX, attempt to acquire lock on the parent table before
1768 * locking the index. index_drop() will need this anyway, and since
1769 * regular queries lock tables before their indexes, we risk deadlock if
1770 * we do it the other way around. No error if we don't find a pg_index
1771 * entry, though --- the relation may have been dropped. Note that this
1772 * code will execute for either plain or partitioned indexes.
1774 if (expected_relkind == RELKIND_INDEX &&
1775 relOid != oldRelOid)
1777 state->heapOid = IndexGetRelation(relOid, true);
1778 if (OidIsValid(state->heapOid))
1779 LockRelationOid(state->heapOid, heap_lockmode);
1783 * Similarly, if the relation is a partition, we must acquire lock on its
1784 * parent before locking the partition. That's because queries lock the
1785 * parent before its partitions, so we risk deadlock if we do it the other
1786 * way around.
1788 if (is_partition && relOid != oldRelOid)
1790 state->partParentOid = get_partition_parent(relOid, true);
1791 if (OidIsValid(state->partParentOid))
1792 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1797 * ExecuteTruncate
1798 * Executes a TRUNCATE command.
1800 * This is a multi-relation truncate. We first open and grab exclusive
1801 * lock on all relations involved, checking permissions and otherwise
1802 * verifying that the relation is OK for truncation. Note that if relations
1803 * are foreign tables, at this stage, we have not yet checked that their
1804 * foreign data in external data sources are OK for truncation. These are
1805 * checked when foreign data are actually truncated later. In CASCADE mode,
1806 * relations having FK references to the targeted relations are automatically
1807 * added to the group; in RESTRICT mode, we check that all FK references are
1808 * internal to the group that's being truncated. Finally all the relations
1809 * are truncated and reindexed.
1811 void
1812 ExecuteTruncate(TruncateStmt *stmt)
1814 List *rels = NIL;
1815 List *relids = NIL;
1816 List *relids_logged = NIL;
1817 ListCell *cell;
1820 * Open, exclusive-lock, and check all the explicitly-specified relations
1822 foreach(cell, stmt->relations)
1824 RangeVar *rv = lfirst(cell);
1825 Relation rel;
1826 bool recurse = rv->inh;
1827 Oid myrelid;
1828 LOCKMODE lockmode = AccessExclusiveLock;
1830 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1831 0, RangeVarCallbackForTruncate,
1832 NULL);
1834 /* don't throw error for "TRUNCATE foo, foo" */
1835 if (list_member_oid(relids, myrelid))
1836 continue;
1838 /* open the relation, we already hold a lock on it */
1839 rel = table_open(myrelid, NoLock);
1842 * RangeVarGetRelidExtended() has done most checks with its callback,
1843 * but other checks with the now-opened Relation remain.
1845 truncate_check_activity(rel);
1847 rels = lappend(rels, rel);
1848 relids = lappend_oid(relids, myrelid);
1850 /* Log this relation only if needed for logical decoding */
1851 if (RelationIsLogicallyLogged(rel))
1852 relids_logged = lappend_oid(relids_logged, myrelid);
1854 if (recurse)
1856 ListCell *child;
1857 List *children;
1859 children = find_all_inheritors(myrelid, lockmode, NULL);
1861 foreach(child, children)
1863 Oid childrelid = lfirst_oid(child);
1865 if (list_member_oid(relids, childrelid))
1866 continue;
1868 /* find_all_inheritors already got lock */
1869 rel = table_open(childrelid, NoLock);
1872 * It is possible that the parent table has children that are
1873 * temp tables of other backends. We cannot safely access
1874 * such tables (because of buffering issues), and the best
1875 * thing to do is to silently ignore them. Note that this
1876 * check is the same as one of the checks done in
1877 * truncate_check_activity() called below, still it is kept
1878 * here for simplicity.
1880 if (RELATION_IS_OTHER_TEMP(rel))
1882 table_close(rel, lockmode);
1883 continue;
1887 * Inherited TRUNCATE commands perform access permission
1888 * checks on the parent table only. So we skip checking the
1889 * children's permissions and don't call
1890 * truncate_check_perms() here.
1892 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1893 truncate_check_activity(rel);
1895 rels = lappend(rels, rel);
1896 relids = lappend_oid(relids, childrelid);
1898 /* Log this relation only if needed for logical decoding */
1899 if (RelationIsLogicallyLogged(rel))
1900 relids_logged = lappend_oid(relids_logged, childrelid);
1903 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1904 ereport(ERROR,
1905 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1906 errmsg("cannot truncate only a partitioned table"),
1907 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1910 ExecuteTruncateGuts(rels, relids, relids_logged,
1911 stmt->behavior, stmt->restart_seqs, false);
1913 /* And close the rels */
1914 foreach(cell, rels)
1916 Relation rel = (Relation) lfirst(cell);
1918 table_close(rel, NoLock);
1923 * ExecuteTruncateGuts
1925 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1926 * command (see above) as well as replication subscribers that execute a
1927 * replicated TRUNCATE action.
1929 * explicit_rels is the list of Relations to truncate that the command
1930 * specified. relids is the list of Oids corresponding to explicit_rels.
1931 * relids_logged is the list of Oids (a subset of relids) that require
1932 * WAL-logging. This is all a bit redundant, but the existing callers have
1933 * this information handy in this form.
1935 void
1936 ExecuteTruncateGuts(List *explicit_rels,
1937 List *relids,
1938 List *relids_logged,
1939 DropBehavior behavior, bool restart_seqs,
1940 bool run_as_table_owner)
1942 List *rels;
1943 List *seq_relids = NIL;
1944 HTAB *ft_htab = NULL;
1945 EState *estate;
1946 ResultRelInfo *resultRelInfos;
1947 ResultRelInfo *resultRelInfo;
1948 SubTransactionId mySubid;
1949 ListCell *cell;
1950 Oid *logrelids;
1953 * Check the explicitly-specified relations.
1955 * In CASCADE mode, suck in all referencing relations as well. This
1956 * requires multiple iterations to find indirectly-dependent relations. At
1957 * each phase, we need to exclusive-lock new rels before looking for their
1958 * dependencies, else we might miss something. Also, we check each rel as
1959 * soon as we open it, to avoid a faux pas such as holding lock for a long
1960 * time on a rel we have no permissions for.
1962 rels = list_copy(explicit_rels);
1963 if (behavior == DROP_CASCADE)
1965 for (;;)
1967 List *newrelids;
1969 newrelids = heap_truncate_find_FKs(relids);
1970 if (newrelids == NIL)
1971 break; /* nothing else to add */
1973 foreach(cell, newrelids)
1975 Oid relid = lfirst_oid(cell);
1976 Relation rel;
1978 rel = table_open(relid, AccessExclusiveLock);
1979 ereport(NOTICE,
1980 (errmsg("truncate cascades to table \"%s\"",
1981 RelationGetRelationName(rel))));
1982 truncate_check_rel(relid, rel->rd_rel);
1983 truncate_check_perms(relid, rel->rd_rel);
1984 truncate_check_activity(rel);
1985 rels = lappend(rels, rel);
1986 relids = lappend_oid(relids, relid);
1988 /* Log this relation only if needed for logical decoding */
1989 if (RelationIsLogicallyLogged(rel))
1990 relids_logged = lappend_oid(relids_logged, relid);
1996 * Check foreign key references. In CASCADE mode, this should be
1997 * unnecessary since we just pulled in all the references; but as a
1998 * cross-check, do it anyway if in an Assert-enabled build.
2000 #ifdef USE_ASSERT_CHECKING
2001 heap_truncate_check_FKs(rels, false);
2002 #else
2003 if (behavior == DROP_RESTRICT)
2004 heap_truncate_check_FKs(rels, false);
2005 #endif
2008 * If we are asked to restart sequences, find all the sequences, lock them
2009 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2010 * We want to do this early since it's pointless to do all the truncation
2011 * work only to fail on sequence permissions.
2013 if (restart_seqs)
2015 foreach(cell, rels)
2017 Relation rel = (Relation) lfirst(cell);
2018 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2019 ListCell *seqcell;
2021 foreach(seqcell, seqlist)
2023 Oid seq_relid = lfirst_oid(seqcell);
2024 Relation seq_rel;
2026 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2028 /* This check must match AlterSequence! */
2029 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2030 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2031 RelationGetRelationName(seq_rel));
2033 seq_relids = lappend_oid(seq_relids, seq_relid);
2035 relation_close(seq_rel, NoLock);
2040 /* Prepare to catch AFTER triggers. */
2041 AfterTriggerBeginQuery();
2044 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2045 * each relation. We don't need to call ExecOpenIndices, though.
2047 * We put the ResultRelInfos in the es_opened_result_relations list, even
2048 * though we don't have a range table and don't populate the
2049 * es_result_relations array. That's a bit bogus, but it's enough to make
2050 * ExecGetTriggerResultRel() find them.
2052 estate = CreateExecutorState();
2053 resultRelInfos = (ResultRelInfo *)
2054 palloc(list_length(rels) * sizeof(ResultRelInfo));
2055 resultRelInfo = resultRelInfos;
2056 foreach(cell, rels)
2058 Relation rel = (Relation) lfirst(cell);
2060 InitResultRelInfo(resultRelInfo,
2061 rel,
2062 0, /* dummy rangetable index */
2063 NULL,
2065 estate->es_opened_result_relations =
2066 lappend(estate->es_opened_result_relations, resultRelInfo);
2067 resultRelInfo++;
2071 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2072 * truncating (this is because one of them might throw an error). Also, if
2073 * we were to allow them to prevent statement execution, that would need
2074 * to be handled here.
2076 resultRelInfo = resultRelInfos;
2077 foreach(cell, rels)
2079 UserContext ucxt;
2081 if (run_as_table_owner)
2082 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2083 &ucxt);
2084 ExecBSTruncateTriggers(estate, resultRelInfo);
2085 if (run_as_table_owner)
2086 RestoreUserContext(&ucxt);
2087 resultRelInfo++;
2091 * OK, truncate each table.
2093 mySubid = GetCurrentSubTransactionId();
2095 foreach(cell, rels)
2097 Relation rel = (Relation) lfirst(cell);
2099 /* Skip partitioned tables as there is nothing to do */
2100 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2101 continue;
2104 * Build the lists of foreign tables belonging to each foreign server
2105 * and pass each list to the foreign data wrapper's callback function,
2106 * so that each server can truncate its all foreign tables in bulk.
2107 * Each list is saved as a single entry in a hash table that uses the
2108 * server OID as lookup key.
2110 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2112 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2113 bool found;
2114 ForeignTruncateInfo *ft_info;
2116 /* First time through, initialize hashtable for foreign tables */
2117 if (!ft_htab)
2119 HASHCTL hctl;
2121 memset(&hctl, 0, sizeof(HASHCTL));
2122 hctl.keysize = sizeof(Oid);
2123 hctl.entrysize = sizeof(ForeignTruncateInfo);
2124 hctl.hcxt = CurrentMemoryContext;
2126 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2127 32, /* start small and extend */
2128 &hctl,
2129 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2132 /* Find or create cached entry for the foreign table */
2133 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2134 if (!found)
2135 ft_info->rels = NIL;
2138 * Save the foreign table in the entry of the server that the
2139 * foreign table belongs to.
2141 ft_info->rels = lappend(ft_info->rels, rel);
2142 continue;
2146 * Normally, we need a transaction-safe truncation here. However, if
2147 * the table was either created in the current (sub)transaction or has
2148 * a new relfilenumber in the current (sub)transaction, then we can
2149 * just truncate it in-place, because a rollback would cause the whole
2150 * table or the current physical file to be thrown away anyway.
2152 if (rel->rd_createSubid == mySubid ||
2153 rel->rd_newRelfilelocatorSubid == mySubid)
2155 /* Immediate, non-rollbackable truncation is OK */
2156 heap_truncate_one_rel(rel);
2158 else
2160 Oid heap_relid;
2161 Oid toast_relid;
2162 ReindexParams reindex_params = {0};
2165 * This effectively deletes all rows in the table, and may be done
2166 * in a serializable transaction. In that case we must record a
2167 * rw-conflict in to this transaction from each transaction
2168 * holding a predicate lock on the table.
2170 CheckTableForSerializableConflictIn(rel);
2173 * Need the full transaction-safe pushups.
2175 * Create a new empty storage file for the relation, and assign it
2176 * as the relfilenumber value. The old storage file is scheduled
2177 * for deletion at commit.
2179 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2181 heap_relid = RelationGetRelid(rel);
2184 * The same for the toast table, if any.
2186 toast_relid = rel->rd_rel->reltoastrelid;
2187 if (OidIsValid(toast_relid))
2189 Relation toastrel = relation_open(toast_relid,
2190 AccessExclusiveLock);
2192 RelationSetNewRelfilenumber(toastrel,
2193 toastrel->rd_rel->relpersistence);
2194 table_close(toastrel, NoLock);
2198 * Reconstruct the indexes to match, and we're done.
2200 reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2201 &reindex_params);
2204 pgstat_count_truncate(rel);
2207 /* Now go through the hash table, and truncate foreign tables */
2208 if (ft_htab)
2210 ForeignTruncateInfo *ft_info;
2211 HASH_SEQ_STATUS seq;
2213 hash_seq_init(&seq, ft_htab);
2215 PG_TRY();
2217 while ((ft_info = hash_seq_search(&seq)) != NULL)
2219 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2221 /* truncate_check_rel() has checked that already */
2222 Assert(routine->ExecForeignTruncate != NULL);
2224 routine->ExecForeignTruncate(ft_info->rels,
2225 behavior,
2226 restart_seqs);
2229 PG_FINALLY();
2231 hash_destroy(ft_htab);
2233 PG_END_TRY();
2237 * Restart owned sequences if we were asked to.
2239 foreach(cell, seq_relids)
2241 Oid seq_relid = lfirst_oid(cell);
2243 ResetSequence(seq_relid);
2247 * Write a WAL record to allow this set of actions to be logically
2248 * decoded.
2250 * Assemble an array of relids so we can write a single WAL record for the
2251 * whole action.
2253 if (relids_logged != NIL)
2255 xl_heap_truncate xlrec;
2256 int i = 0;
2258 /* should only get here if wal_level >= logical */
2259 Assert(XLogLogicalInfoActive());
2261 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2262 foreach(cell, relids_logged)
2263 logrelids[i++] = lfirst_oid(cell);
2265 xlrec.dbId = MyDatabaseId;
2266 xlrec.nrelids = list_length(relids_logged);
2267 xlrec.flags = 0;
2268 if (behavior == DROP_CASCADE)
2269 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2270 if (restart_seqs)
2271 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2273 XLogBeginInsert();
2274 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2275 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2277 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2279 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2283 * Process all AFTER STATEMENT TRUNCATE triggers.
2285 resultRelInfo = resultRelInfos;
2286 foreach(cell, rels)
2288 UserContext ucxt;
2290 if (run_as_table_owner)
2291 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2292 &ucxt);
2293 ExecASTruncateTriggers(estate, resultRelInfo);
2294 if (run_as_table_owner)
2295 RestoreUserContext(&ucxt);
2296 resultRelInfo++;
2299 /* Handle queued AFTER triggers */
2300 AfterTriggerEndQuery(estate);
2302 /* We can clean up the EState now */
2303 FreeExecutorState(estate);
2306 * Close any rels opened by CASCADE (can't do this while EState still
2307 * holds refs)
2309 rels = list_difference_ptr(rels, explicit_rels);
2310 foreach(cell, rels)
2312 Relation rel = (Relation) lfirst(cell);
2314 table_close(rel, NoLock);
2319 * Check that a given relation is safe to truncate. Subroutine for
2320 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2322 static void
2323 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2325 char *relname = NameStr(reltuple->relname);
2328 * Only allow truncate on regular tables, foreign tables using foreign
2329 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2330 * latter are only being included here for the following checks; no
2331 * physical truncation will occur in their case.).
2333 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2335 Oid serverid = GetForeignServerIdByRelId(relid);
2336 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2338 if (!fdwroutine->ExecForeignTruncate)
2339 ereport(ERROR,
2340 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2341 errmsg("cannot truncate foreign table \"%s\"",
2342 relname)));
2344 else if (reltuple->relkind != RELKIND_RELATION &&
2345 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2346 ereport(ERROR,
2347 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2348 errmsg("\"%s\" is not a table", relname)));
2351 * Most system catalogs can't be truncated at all, or at least not unless
2352 * allow_system_table_mods=on. As an exception, however, we allow
2353 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2354 * to change its relfilenode to match the old cluster, and allowing a
2355 * TRUNCATE command to be executed is the easiest way of doing that.
2357 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2358 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2359 ereport(ERROR,
2360 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2361 errmsg("permission denied: \"%s\" is a system catalog",
2362 relname)));
2364 InvokeObjectTruncateHook(relid);
2368 * Check that current user has the permission to truncate given relation.
2370 static void
2371 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2373 char *relname = NameStr(reltuple->relname);
2374 AclResult aclresult;
2376 /* Permissions checks */
2377 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2378 if (aclresult != ACLCHECK_OK)
2379 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2380 relname);
2384 * Set of extra sanity checks to check if a given relation is safe to
2385 * truncate. This is split with truncate_check_rel() as
2386 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2388 static void
2389 truncate_check_activity(Relation rel)
2392 * Don't allow truncate on temp tables of other backends ... their local
2393 * buffer manager is not going to cope.
2395 if (RELATION_IS_OTHER_TEMP(rel))
2396 ereport(ERROR,
2397 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2398 errmsg("cannot truncate temporary tables of other sessions")));
2401 * Also check for active uses of the relation in the current transaction,
2402 * including open scans and pending AFTER trigger events.
2404 CheckTableNotInUse(rel, "TRUNCATE");
2408 * storage_name
2409 * returns the name corresponding to a typstorage/attstorage enum value
2411 static const char *
2412 storage_name(char c)
2414 switch (c)
2416 case TYPSTORAGE_PLAIN:
2417 return "PLAIN";
2418 case TYPSTORAGE_EXTERNAL:
2419 return "EXTERNAL";
2420 case TYPSTORAGE_EXTENDED:
2421 return "EXTENDED";
2422 case TYPSTORAGE_MAIN:
2423 return "MAIN";
2424 default:
2425 return "???";
2429 /*----------
2430 * MergeAttributes
2431 * Returns new schema given initial schema and superclasses.
2433 * Input arguments:
2434 * 'columns' is the column/attribute definition for the table. (It's a list
2435 * of ColumnDef's.) It is destructively changed.
2436 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2437 * 'relpersistence' is the persistence type of the table.
2438 * 'is_partition' tells if the table is a partition.
2440 * Output arguments:
2441 * 'supconstr' receives a list of CookedConstraint representing
2442 * CHECK constraints belonging to parent relations, updated as
2443 * necessary to be valid for the child.
2444 * 'supnotnulls' receives a list of CookedConstraint representing
2445 * not-null constraints based on those from parent relations.
2447 * Return value:
2448 * Completed schema list.
2450 * Notes:
2451 * The order in which the attributes are inherited is very important.
2452 * Intuitively, the inherited attributes should come first. If a table
2453 * inherits from multiple parents, the order of those attributes are
2454 * according to the order of the parents specified in CREATE TABLE.
2456 * Here's an example:
2458 * create table person (name text, age int4, location point);
2459 * create table emp (salary int4, manager text) inherits(person);
2460 * create table student (gpa float8) inherits (person);
2461 * create table stud_emp (percent int4) inherits (emp, student);
2463 * The order of the attributes of stud_emp is:
2465 * person {1:name, 2:age, 3:location}
2466 * / \
2467 * {6:gpa} student emp {4:salary, 5:manager}
2468 * \ /
2469 * stud_emp {7:percent}
2471 * If the same attribute name appears multiple times, then it appears
2472 * in the result table in the proper location for its first appearance.
2474 * Constraints (including not-null constraints) for the child table
2475 * are the union of all relevant constraints, from both the child schema
2476 * and parent tables. In addition, in legacy inheritance, each column that
2477 * appears in a primary key in any of the parents also gets a NOT NULL
2478 * constraint (partitioning doesn't need this, because the PK itself gets
2479 * inherited.)
2481 * The default value for a child column is defined as:
2482 * (1) If the child schema specifies a default, that value is used.
2483 * (2) If neither the child nor any parent specifies a default, then
2484 * the column will not have a default.
2485 * (3) If conflicting defaults are inherited from different parents
2486 * (and not overridden by the child), an error is raised.
2487 * (4) Otherwise the inherited default is used.
2489 * Note that the default-value infrastructure is used for generated
2490 * columns' expressions too, so most of the preceding paragraph applies
2491 * to generation expressions too. We insist that a child column be
2492 * generated if and only if its parent(s) are, but it need not have
2493 * the same generation expression.
2494 *----------
2496 static List *
2497 MergeAttributes(List *columns, const List *supers, char relpersistence,
2498 bool is_partition, List **supconstr, List **supnotnulls)
2500 List *inh_columns = NIL;
2501 List *constraints = NIL;
2502 List *nnconstraints = NIL;
2503 bool have_bogus_defaults = false;
2504 int child_attno;
2505 static Node bogus_marker = {0}; /* marks conflicting defaults */
2506 List *saved_columns = NIL;
2507 ListCell *lc;
2510 * Check for and reject tables with too many columns. We perform this
2511 * check relatively early for two reasons: (a) we don't run the risk of
2512 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2513 * okay if we're processing <= 1600 columns, but could take minutes to
2514 * execute if the user attempts to create a table with hundreds of
2515 * thousands of columns.
2517 * Note that we also need to check that we do not exceed this figure after
2518 * including columns from inherited relations.
2520 if (list_length(columns) > MaxHeapAttributeNumber)
2521 ereport(ERROR,
2522 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2523 errmsg("tables can have at most %d columns",
2524 MaxHeapAttributeNumber)));
2527 * Check for duplicate names in the explicit list of attributes.
2529 * Although we might consider merging such entries in the same way that we
2530 * handle name conflicts for inherited attributes, it seems to make more
2531 * sense to assume such conflicts are errors.
2533 * We don't use foreach() here because we have two nested loops over the
2534 * columns list, with possible element deletions in the inner one. If we
2535 * used foreach_delete_current() it could only fix up the state of one of
2536 * the loops, so it seems cleaner to use looping over list indexes for
2537 * both loops. Note that any deletion will happen beyond where the outer
2538 * loop is, so its index never needs adjustment.
2540 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2542 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2544 if (!is_partition && coldef->typeName == NULL)
2547 * Typed table column option that does not belong to a column from
2548 * the type. This works because the columns from the type come
2549 * first in the list. (We omit this check for partition column
2550 * lists; those are processed separately below.)
2552 ereport(ERROR,
2553 (errcode(ERRCODE_UNDEFINED_COLUMN),
2554 errmsg("column \"%s\" does not exist",
2555 coldef->colname)));
2558 /* restpos scans all entries beyond coldef; incr is in loop body */
2559 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2561 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2563 if (strcmp(coldef->colname, restdef->colname) == 0)
2565 if (coldef->is_from_type)
2568 * merge the column options into the column from the type
2570 coldef->is_not_null = restdef->is_not_null;
2571 coldef->raw_default = restdef->raw_default;
2572 coldef->cooked_default = restdef->cooked_default;
2573 coldef->constraints = restdef->constraints;
2574 coldef->is_from_type = false;
2575 columns = list_delete_nth_cell(columns, restpos);
2577 else
2578 ereport(ERROR,
2579 (errcode(ERRCODE_DUPLICATE_COLUMN),
2580 errmsg("column \"%s\" specified more than once",
2581 coldef->colname)));
2583 else
2584 restpos++;
2589 * In case of a partition, there are no new column definitions, only dummy
2590 * ColumnDefs created for column constraints. Set them aside for now and
2591 * process them at the end.
2593 if (is_partition)
2595 saved_columns = columns;
2596 columns = NIL;
2600 * Scan the parents left-to-right, and merge their attributes to form a
2601 * list of inherited columns (inh_columns).
2603 child_attno = 0;
2604 foreach(lc, supers)
2606 Oid parent = lfirst_oid(lc);
2607 Relation relation;
2608 TupleDesc tupleDesc;
2609 TupleConstr *constr;
2610 AttrMap *newattmap;
2611 List *inherited_defaults;
2612 List *cols_with_defaults;
2613 List *nnconstrs;
2614 ListCell *lc1;
2615 ListCell *lc2;
2616 Bitmapset *nncols = NULL;
2618 /* caller already got lock */
2619 relation = table_open(parent, NoLock);
2622 * Check for active uses of the parent partitioned table in the
2623 * current transaction, such as being used in some manner by an
2624 * enclosing command.
2626 if (is_partition)
2627 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2630 * We do not allow partitioned tables and partitions to participate in
2631 * regular inheritance.
2633 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2634 ereport(ERROR,
2635 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2636 errmsg("cannot inherit from partitioned table \"%s\"",
2637 RelationGetRelationName(relation))));
2638 if (relation->rd_rel->relispartition && !is_partition)
2639 ereport(ERROR,
2640 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2641 errmsg("cannot inherit from partition \"%s\"",
2642 RelationGetRelationName(relation))));
2644 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2645 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2646 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2647 ereport(ERROR,
2648 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2649 errmsg("inherited relation \"%s\" is not a table or foreign table",
2650 RelationGetRelationName(relation))));
2653 * If the parent is permanent, so must be all of its partitions. Note
2654 * that inheritance allows that case.
2656 if (is_partition &&
2657 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2658 relpersistence == RELPERSISTENCE_TEMP)
2659 ereport(ERROR,
2660 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2661 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2662 RelationGetRelationName(relation))));
2664 /* Permanent rels cannot inherit from temporary ones */
2665 if (relpersistence != RELPERSISTENCE_TEMP &&
2666 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2667 ereport(ERROR,
2668 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2669 errmsg(!is_partition
2670 ? "cannot inherit from temporary relation \"%s\""
2671 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2672 RelationGetRelationName(relation))));
2674 /* If existing rel is temp, it must belong to this session */
2675 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2676 !relation->rd_islocaltemp)
2677 ereport(ERROR,
2678 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2679 errmsg(!is_partition
2680 ? "cannot inherit from temporary relation of another session"
2681 : "cannot create as partition of temporary relation of another session")));
2684 * We should have an UNDER permission flag for this, but for now,
2685 * demand that creator of a child table own the parent.
2687 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2688 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2689 RelationGetRelationName(relation));
2691 tupleDesc = RelationGetDescr(relation);
2692 constr = tupleDesc->constr;
2695 * newattmap->attnums[] will contain the child-table attribute numbers
2696 * for the attributes of this parent table. (They are not the same
2697 * for parents after the first one, nor if we have dropped columns.)
2699 newattmap = make_attrmap(tupleDesc->natts);
2701 /* We can't process inherited defaults until newattmap is complete. */
2702 inherited_defaults = cols_with_defaults = NIL;
2705 * Request attnotnull on columns that have a not-null constraint
2706 * that's not marked NO INHERIT.
2708 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation),
2709 true, false);
2710 foreach_ptr(CookedConstraint, cc, nnconstrs)
2711 nncols = bms_add_member(nncols, cc->attnum);
2713 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2714 parent_attno++)
2716 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2717 parent_attno - 1);
2718 char *attributeName = NameStr(attribute->attname);
2719 int exist_attno;
2720 ColumnDef *newdef;
2721 ColumnDef *mergeddef;
2724 * Ignore dropped columns in the parent.
2726 if (attribute->attisdropped)
2727 continue; /* leave newattmap->attnums entry as zero */
2730 * Create new column definition
2732 newdef = makeColumnDef(attributeName, attribute->atttypid,
2733 attribute->atttypmod, attribute->attcollation);
2734 newdef->storage = attribute->attstorage;
2735 newdef->generated = attribute->attgenerated;
2736 if (CompressionMethodIsValid(attribute->attcompression))
2737 newdef->compression =
2738 pstrdup(GetCompressionMethodName(attribute->attcompression));
2741 * Regular inheritance children are independent enough not to
2742 * inherit identity columns. But partitions are integral part of
2743 * a partitioned table and inherit identity column.
2745 if (is_partition)
2746 newdef->identity = attribute->attidentity;
2749 * Does it match some previously considered column from another
2750 * parent?
2752 exist_attno = findAttrByName(attributeName, inh_columns);
2753 if (exist_attno > 0)
2756 * Yes, try to merge the two column definitions.
2758 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2760 newattmap->attnums[parent_attno - 1] = exist_attno;
2763 * Partitions have only one parent, so conflict should never
2764 * occur.
2766 Assert(!is_partition);
2768 else
2771 * No, create a new inherited column
2773 newdef->inhcount = 1;
2774 newdef->is_local = false;
2775 inh_columns = lappend(inh_columns, newdef);
2777 newattmap->attnums[parent_attno - 1] = ++child_attno;
2778 mergeddef = newdef;
2782 * mark attnotnull if parent has it
2784 if (bms_is_member(parent_attno, nncols))
2785 mergeddef->is_not_null = true;
2788 * Locate default/generation expression if any
2790 if (attribute->atthasdef)
2792 Node *this_default;
2794 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2795 if (this_default == NULL)
2796 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2797 parent_attno, RelationGetRelationName(relation));
2800 * If it's a GENERATED default, it might contain Vars that
2801 * need to be mapped to the inherited column(s)' new numbers.
2802 * We can't do that till newattmap is ready, so just remember
2803 * all the inherited default expressions for the moment.
2805 inherited_defaults = lappend(inherited_defaults, this_default);
2806 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2811 * Now process any inherited default expressions, adjusting attnos
2812 * using the completed newattmap map.
2814 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2816 Node *this_default = (Node *) lfirst(lc1);
2817 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2818 bool found_whole_row;
2820 /* Adjust Vars to match new table's column numbering */
2821 this_default = map_variable_attnos(this_default,
2822 1, 0,
2823 newattmap,
2824 InvalidOid, &found_whole_row);
2827 * For the moment we have to reject whole-row variables. We could
2828 * convert them, if we knew the new table's rowtype OID, but that
2829 * hasn't been assigned yet. (A variable could only appear in a
2830 * generation expression, so the error message is correct.)
2832 if (found_whole_row)
2833 ereport(ERROR,
2834 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2835 errmsg("cannot convert whole-row table reference"),
2836 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2837 def->colname,
2838 RelationGetRelationName(relation))));
2841 * If we already had a default from some prior parent, check to
2842 * see if they are the same. If so, no problem; if not, mark the
2843 * column as having a bogus default. Below, we will complain if
2844 * the bogus default isn't overridden by the child columns.
2846 Assert(def->raw_default == NULL);
2847 if (def->cooked_default == NULL)
2848 def->cooked_default = this_default;
2849 else if (!equal(def->cooked_default, this_default))
2851 def->cooked_default = &bogus_marker;
2852 have_bogus_defaults = true;
2857 * Now copy the CHECK constraints of this parent, adjusting attnos
2858 * using the completed newattmap map. Identically named constraints
2859 * are merged if possible, else we throw error.
2861 if (constr && constr->num_check > 0)
2863 ConstrCheck *check = constr->check;
2865 for (int i = 0; i < constr->num_check; i++)
2867 char *name = check[i].ccname;
2868 Node *expr;
2869 bool found_whole_row;
2871 /* ignore if the constraint is non-inheritable */
2872 if (check[i].ccnoinherit)
2873 continue;
2875 /* Adjust Vars to match new table's column numbering */
2876 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2877 1, 0,
2878 newattmap,
2879 InvalidOid, &found_whole_row);
2882 * For the moment we have to reject whole-row variables. We
2883 * could convert them, if we knew the new table's rowtype OID,
2884 * but that hasn't been assigned yet.
2886 if (found_whole_row)
2887 ereport(ERROR,
2888 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2889 errmsg("cannot convert whole-row table reference"),
2890 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2891 name,
2892 RelationGetRelationName(relation))));
2894 constraints = MergeCheckConstraint(constraints, name, expr,
2895 check[i].ccenforced);
2900 * Also copy the not-null constraints from this parent. The
2901 * attnotnull markings were already installed above.
2903 foreach_ptr(CookedConstraint, nn, nnconstrs)
2905 Assert(nn->contype == CONSTR_NOTNULL);
2907 nn->attnum = newattmap->attnums[nn->attnum - 1];
2909 nnconstraints = lappend(nnconstraints, nn);
2912 free_attrmap(newattmap);
2915 * Close the parent rel, but keep our lock on it until xact commit.
2916 * That will prevent someone else from deleting or ALTERing the parent
2917 * before the child is committed.
2919 table_close(relation, NoLock);
2923 * If we had no inherited attributes, the result columns are just the
2924 * explicitly declared columns. Otherwise, we need to merge the declared
2925 * columns into the inherited column list. Although, we never have any
2926 * explicitly declared columns if the table is a partition.
2928 if (inh_columns != NIL)
2930 int newcol_attno = 0;
2932 foreach(lc, columns)
2934 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2935 char *attributeName = newdef->colname;
2936 int exist_attno;
2939 * Partitions have only one parent and have no column definitions
2940 * of their own, so conflict should never occur.
2942 Assert(!is_partition);
2944 newcol_attno++;
2947 * Does it match some inherited column?
2949 exist_attno = findAttrByName(attributeName, inh_columns);
2950 if (exist_attno > 0)
2953 * Yes, try to merge the two column definitions.
2955 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2957 else
2960 * No, attach new column unchanged to result columns.
2962 inh_columns = lappend(inh_columns, newdef);
2966 columns = inh_columns;
2969 * Check that we haven't exceeded the legal # of columns after merging
2970 * in inherited columns.
2972 if (list_length(columns) > MaxHeapAttributeNumber)
2973 ereport(ERROR,
2974 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2975 errmsg("tables can have at most %d columns",
2976 MaxHeapAttributeNumber)));
2980 * Now that we have the column definition list for a partition, we can
2981 * check whether the columns referenced in the column constraint specs
2982 * actually exist. Also, merge column defaults.
2984 if (is_partition)
2986 foreach(lc, saved_columns)
2988 ColumnDef *restdef = lfirst(lc);
2989 bool found = false;
2990 ListCell *l;
2992 foreach(l, columns)
2994 ColumnDef *coldef = lfirst(l);
2996 if (strcmp(coldef->colname, restdef->colname) == 0)
2998 found = true;
3001 * Check for conflicts related to generated columns.
3003 * Same rules as above: generated-ness has to match the
3004 * parent, but the contents of the generation expression
3005 * can be different.
3007 if (coldef->generated)
3009 if (restdef->raw_default && !restdef->generated)
3010 ereport(ERROR,
3011 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3012 errmsg("column \"%s\" inherits from generated column but specifies default",
3013 restdef->colname)));
3014 if (restdef->identity)
3015 ereport(ERROR,
3016 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3017 errmsg("column \"%s\" inherits from generated column but specifies identity",
3018 restdef->colname)));
3020 else
3022 if (restdef->generated)
3023 ereport(ERROR,
3024 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3025 errmsg("child column \"%s\" specifies generation expression",
3026 restdef->colname),
3027 errhint("A child table column cannot be generated unless its parent column is.")));
3031 * Override the parent's default value for this column
3032 * (coldef->cooked_default) with the partition's local
3033 * definition (restdef->raw_default), if there's one. It
3034 * should be physically impossible to get a cooked default
3035 * in the local definition or a raw default in the
3036 * inherited definition, but make sure they're nulls, for
3037 * future-proofing.
3039 Assert(restdef->cooked_default == NULL);
3040 Assert(coldef->raw_default == NULL);
3041 if (restdef->raw_default)
3043 coldef->raw_default = restdef->raw_default;
3044 coldef->cooked_default = NULL;
3049 /* complain for constraints on columns not in parent */
3050 if (!found)
3051 ereport(ERROR,
3052 (errcode(ERRCODE_UNDEFINED_COLUMN),
3053 errmsg("column \"%s\" does not exist",
3054 restdef->colname)));
3059 * If we found any conflicting parent default values, check to make sure
3060 * they were overridden by the child.
3062 if (have_bogus_defaults)
3064 foreach(lc, columns)
3066 ColumnDef *def = lfirst(lc);
3068 if (def->cooked_default == &bogus_marker)
3070 if (def->generated)
3071 ereport(ERROR,
3072 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3073 errmsg("column \"%s\" inherits conflicting generation expressions",
3074 def->colname),
3075 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3076 else
3077 ereport(ERROR,
3078 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3079 errmsg("column \"%s\" inherits conflicting default values",
3080 def->colname),
3081 errhint("To resolve the conflict, specify a default explicitly.")));
3086 *supconstr = constraints;
3087 *supnotnulls = nnconstraints;
3089 return columns;
3094 * MergeCheckConstraint
3095 * Try to merge an inherited CHECK constraint with previous ones
3097 * If we inherit identically-named constraints from multiple parents, we must
3098 * merge them, or throw an error if they don't have identical definitions.
3100 * constraints is a list of CookedConstraint structs for previous constraints.
3102 * If the new constraint matches an existing one, then the existing
3103 * constraint's inheritance count is updated. If there is a conflict (same
3104 * name but different expression), throw an error. If the constraint neither
3105 * matches nor conflicts with an existing one, a new constraint is appended to
3106 * the list.
3108 static List *
3109 MergeCheckConstraint(List *constraints, const char *name, Node *expr, bool is_enforced)
3111 ListCell *lc;
3112 CookedConstraint *newcon;
3114 foreach(lc, constraints)
3116 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3118 Assert(ccon->contype == CONSTR_CHECK);
3120 /* Non-matching names never conflict */
3121 if (strcmp(ccon->name, name) != 0)
3122 continue;
3124 if (equal(expr, ccon->expr))
3126 /* OK to merge constraint with existing */
3127 if (pg_add_s16_overflow(ccon->inhcount, 1,
3128 &ccon->inhcount))
3129 ereport(ERROR,
3130 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3131 errmsg("too many inheritance parents"));
3134 * When enforceability differs, the merged constraint should be
3135 * marked as ENFORCED because one of the parents is ENFORCED.
3137 if (!ccon->is_enforced && is_enforced)
3139 ccon->is_enforced = true;
3140 ccon->skip_validation = false;
3143 return constraints;
3146 ereport(ERROR,
3147 (errcode(ERRCODE_DUPLICATE_OBJECT),
3148 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3149 name)));
3153 * Constraint couldn't be merged with an existing one and also didn't
3154 * conflict with an existing one, so add it as a new one to the list.
3156 newcon = palloc0_object(CookedConstraint);
3157 newcon->contype = CONSTR_CHECK;
3158 newcon->name = pstrdup(name);
3159 newcon->expr = expr;
3160 newcon->inhcount = 1;
3161 newcon->is_enforced = is_enforced;
3162 newcon->skip_validation = !is_enforced;
3163 return lappend(constraints, newcon);
3167 * MergeChildAttribute
3168 * Merge given child attribute definition into given inherited attribute.
3170 * Input arguments:
3171 * 'inh_columns' is the list of inherited ColumnDefs.
3172 * 'exist_attno' is the number of the inherited attribute in inh_columns
3173 * 'newcol_attno' is the attribute number in child table's schema definition
3174 * 'newdef' is the column/attribute definition from the child table.
3176 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3177 * ColumnDef remains unchanged.
3179 * Notes:
3180 * - The attribute is merged according to the rules laid out in the prologue
3181 * of MergeAttributes().
3182 * - If matching inherited attribute exists but the child attribute can not be
3183 * merged into it, the function throws respective errors.
3184 * - A partition can not have its own column definitions. Hence this function
3185 * is applicable only to a regular inheritance child.
3187 static void
3188 MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3190 char *attributeName = newdef->colname;
3191 ColumnDef *inhdef;
3192 Oid inhtypeid,
3193 newtypeid;
3194 int32 inhtypmod,
3195 newtypmod;
3196 Oid inhcollid,
3197 newcollid;
3199 if (exist_attno == newcol_attno)
3200 ereport(NOTICE,
3201 (errmsg("merging column \"%s\" with inherited definition",
3202 attributeName)));
3203 else
3204 ereport(NOTICE,
3205 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3206 errdetail("User-specified column moved to the position of the inherited column.")));
3208 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3211 * Must have the same type and typmod
3213 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3214 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3215 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3216 ereport(ERROR,
3217 (errcode(ERRCODE_DATATYPE_MISMATCH),
3218 errmsg("column \"%s\" has a type conflict",
3219 attributeName),
3220 errdetail("%s versus %s",
3221 format_type_with_typemod(inhtypeid, inhtypmod),
3222 format_type_with_typemod(newtypeid, newtypmod))));
3225 * Must have the same collation
3227 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3228 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3229 if (inhcollid != newcollid)
3230 ereport(ERROR,
3231 (errcode(ERRCODE_COLLATION_MISMATCH),
3232 errmsg("column \"%s\" has a collation conflict",
3233 attributeName),
3234 errdetail("\"%s\" versus \"%s\"",
3235 get_collation_name(inhcollid),
3236 get_collation_name(newcollid))));
3239 * Identity is never inherited by a regular inheritance child. Pick
3240 * child's identity definition if there's one.
3242 inhdef->identity = newdef->identity;
3245 * Copy storage parameter
3247 if (inhdef->storage == 0)
3248 inhdef->storage = newdef->storage;
3249 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3250 ereport(ERROR,
3251 (errcode(ERRCODE_DATATYPE_MISMATCH),
3252 errmsg("column \"%s\" has a storage parameter conflict",
3253 attributeName),
3254 errdetail("%s versus %s",
3255 storage_name(inhdef->storage),
3256 storage_name(newdef->storage))));
3259 * Copy compression parameter
3261 if (inhdef->compression == NULL)
3262 inhdef->compression = newdef->compression;
3263 else if (newdef->compression != NULL)
3265 if (strcmp(inhdef->compression, newdef->compression) != 0)
3266 ereport(ERROR,
3267 (errcode(ERRCODE_DATATYPE_MISMATCH),
3268 errmsg("column \"%s\" has a compression method conflict",
3269 attributeName),
3270 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3274 * Merge of not-null constraints = OR 'em together
3276 inhdef->is_not_null |= newdef->is_not_null;
3279 * Check for conflicts related to generated columns.
3281 * If the parent column is generated, the child column will be made a
3282 * generated column if it isn't already. If it is a generated column,
3283 * we'll take its generation expression in preference to the parent's. We
3284 * must check that the child column doesn't specify a default value or
3285 * identity, which matches the rules for a single column in
3286 * parse_utilcmd.c.
3288 * Conversely, if the parent column is not generated, the child column
3289 * can't be either. (We used to allow that, but it results in being able
3290 * to override the generation expression via UPDATEs through the parent.)
3292 if (inhdef->generated)
3294 if (newdef->raw_default && !newdef->generated)
3295 ereport(ERROR,
3296 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3297 errmsg("column \"%s\" inherits from generated column but specifies default",
3298 inhdef->colname)));
3299 if (newdef->identity)
3300 ereport(ERROR,
3301 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3302 errmsg("column \"%s\" inherits from generated column but specifies identity",
3303 inhdef->colname)));
3305 else
3307 if (newdef->generated)
3308 ereport(ERROR,
3309 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3310 errmsg("child column \"%s\" specifies generation expression",
3311 inhdef->colname),
3312 errhint("A child table column cannot be generated unless its parent column is.")));
3316 * If new def has a default, override previous default
3318 if (newdef->raw_default != NULL)
3320 inhdef->raw_default = newdef->raw_default;
3321 inhdef->cooked_default = newdef->cooked_default;
3324 /* Mark the column as locally defined */
3325 inhdef->is_local = true;
3329 * MergeInheritedAttribute
3330 * Merge given parent attribute definition into specified attribute
3331 * inherited from the previous parents.
3333 * Input arguments:
3334 * 'inh_columns' is the list of previously inherited ColumnDefs.
3335 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3336 * 'newdef' is the new parent column/attribute definition to be merged.
3338 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3340 * Notes:
3341 * - The attribute is merged according to the rules laid out in the prologue
3342 * of MergeAttributes().
3343 * - If matching inherited attribute exists but the new attribute can not be
3344 * merged into it, the function throws respective errors.
3345 * - A partition inherits from only a single parent. Hence this function is
3346 * applicable only to a regular inheritance.
3348 static ColumnDef *
3349 MergeInheritedAttribute(List *inh_columns,
3350 int exist_attno,
3351 const ColumnDef *newdef)
3353 char *attributeName = newdef->colname;
3354 ColumnDef *prevdef;
3355 Oid prevtypeid,
3356 newtypeid;
3357 int32 prevtypmod,
3358 newtypmod;
3359 Oid prevcollid,
3360 newcollid;
3362 ereport(NOTICE,
3363 (errmsg("merging multiple inherited definitions of column \"%s\"",
3364 attributeName)));
3365 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3368 * Must have the same type and typmod
3370 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3371 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3372 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3373 ereport(ERROR,
3374 (errcode(ERRCODE_DATATYPE_MISMATCH),
3375 errmsg("inherited column \"%s\" has a type conflict",
3376 attributeName),
3377 errdetail("%s versus %s",
3378 format_type_with_typemod(prevtypeid, prevtypmod),
3379 format_type_with_typemod(newtypeid, newtypmod))));
3382 * Must have the same collation
3384 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3385 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3386 if (prevcollid != newcollid)
3387 ereport(ERROR,
3388 (errcode(ERRCODE_COLLATION_MISMATCH),
3389 errmsg("inherited column \"%s\" has a collation conflict",
3390 attributeName),
3391 errdetail("\"%s\" versus \"%s\"",
3392 get_collation_name(prevcollid),
3393 get_collation_name(newcollid))));
3396 * Copy/check storage parameter
3398 if (prevdef->storage == 0)
3399 prevdef->storage = newdef->storage;
3400 else if (prevdef->storage != newdef->storage)
3401 ereport(ERROR,
3402 (errcode(ERRCODE_DATATYPE_MISMATCH),
3403 errmsg("inherited column \"%s\" has a storage parameter conflict",
3404 attributeName),
3405 errdetail("%s versus %s",
3406 storage_name(prevdef->storage),
3407 storage_name(newdef->storage))));
3410 * Copy/check compression parameter
3412 if (prevdef->compression == NULL)
3413 prevdef->compression = newdef->compression;
3414 else if (newdef->compression != NULL)
3416 if (strcmp(prevdef->compression, newdef->compression) != 0)
3417 ereport(ERROR,
3418 (errcode(ERRCODE_DATATYPE_MISMATCH),
3419 errmsg("column \"%s\" has a compression method conflict",
3420 attributeName),
3421 errdetail("%s versus %s",
3422 prevdef->compression, newdef->compression)));
3426 * Check for GENERATED conflicts
3428 if (prevdef->generated != newdef->generated)
3429 ereport(ERROR,
3430 (errcode(ERRCODE_DATATYPE_MISMATCH),
3431 errmsg("inherited column \"%s\" has a generation conflict",
3432 attributeName)));
3435 * Default and other constraints are handled by the caller.
3438 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3439 &prevdef->inhcount))
3440 ereport(ERROR,
3441 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3442 errmsg("too many inheritance parents"));
3444 return prevdef;
3448 * StoreCatalogInheritance
3449 * Updates the system catalogs with proper inheritance information.
3451 * supers is a list of the OIDs of the new relation's direct ancestors.
3453 static void
3454 StoreCatalogInheritance(Oid relationId, List *supers,
3455 bool child_is_partition)
3457 Relation relation;
3458 int32 seqNumber;
3459 ListCell *entry;
3462 * sanity checks
3464 Assert(OidIsValid(relationId));
3466 if (supers == NIL)
3467 return;
3470 * Store INHERITS information in pg_inherits using direct ancestors only.
3471 * Also enter dependencies on the direct ancestors, and make sure they are
3472 * marked with relhassubclass = true.
3474 * (Once upon a time, both direct and indirect ancestors were found here
3475 * and then entered into pg_ipl. Since that catalog doesn't exist
3476 * anymore, there's no need to look for indirect ancestors.)
3478 relation = table_open(InheritsRelationId, RowExclusiveLock);
3480 seqNumber = 1;
3481 foreach(entry, supers)
3483 Oid parentOid = lfirst_oid(entry);
3485 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3486 child_is_partition);
3487 seqNumber++;
3490 table_close(relation, RowExclusiveLock);
3494 * Make catalog entries showing relationId as being an inheritance child
3495 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3497 static void
3498 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3499 int32 seqNumber, Relation inhRelation,
3500 bool child_is_partition)
3502 ObjectAddress childobject,
3503 parentobject;
3505 /* store the pg_inherits row */
3506 StoreSingleInheritance(relationId, parentOid, seqNumber);
3509 * Store a dependency too
3511 parentobject.classId = RelationRelationId;
3512 parentobject.objectId = parentOid;
3513 parentobject.objectSubId = 0;
3514 childobject.classId = RelationRelationId;
3515 childobject.objectId = relationId;
3516 childobject.objectSubId = 0;
3518 recordDependencyOn(&childobject, &parentobject,
3519 child_dependency_type(child_is_partition));
3522 * Post creation hook of this inheritance. Since object_access_hook
3523 * doesn't take multiple object identifiers, we relay oid of parent
3524 * relation using auxiliary_id argument.
3526 InvokeObjectPostAlterHookArg(InheritsRelationId,
3527 relationId, 0,
3528 parentOid, false);
3531 * Mark the parent as having subclasses.
3533 SetRelationHasSubclass(parentOid, true);
3537 * Look for an existing column entry with the given name.
3539 * Returns the index (starting with 1) if attribute already exists in columns,
3540 * 0 if it doesn't.
3542 static int
3543 findAttrByName(const char *attributeName, const List *columns)
3545 ListCell *lc;
3546 int i = 1;
3548 foreach(lc, columns)
3550 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3551 return i;
3553 i++;
3555 return 0;
3560 * SetRelationHasSubclass
3561 * Set the value of the relation's relhassubclass field in pg_class.
3563 * It's always safe to set this field to true, because all SQL commands are
3564 * ready to see true and then find no children. On the other hand, commands
3565 * generally assume zero children if this is false.
3567 * Caller must hold any self-exclusive lock until end of transaction. If the
3568 * new value is false, caller must have acquired that lock before reading the
3569 * evidence that justified the false value. That way, it properly waits if
3570 * another backend is simultaneously concluding no need to change the tuple
3571 * (new and old values are true).
3573 * NOTE: an important side-effect of this operation is that an SI invalidation
3574 * message is sent out to all backends --- including me --- causing plans
3575 * referencing the relation to be rebuilt with the new list of children.
3576 * This must happen even if we find that no change is needed in the pg_class
3577 * row.
3579 void
3580 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3582 Relation relationRelation;
3583 HeapTuple tuple;
3584 Form_pg_class classtuple;
3586 Assert(CheckRelationOidLockedByMe(relationId,
3587 ShareUpdateExclusiveLock, false) ||
3588 CheckRelationOidLockedByMe(relationId,
3589 ShareRowExclusiveLock, true));
3592 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3594 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3595 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3596 if (!HeapTupleIsValid(tuple))
3597 elog(ERROR, "cache lookup failed for relation %u", relationId);
3598 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3600 if (classtuple->relhassubclass != relhassubclass)
3602 classtuple->relhassubclass = relhassubclass;
3603 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3605 else
3607 /* no need to change tuple, but force relcache rebuild anyway */
3608 CacheInvalidateRelcacheByTuple(tuple);
3611 heap_freetuple(tuple);
3612 table_close(relationRelation, RowExclusiveLock);
3616 * CheckRelationTableSpaceMove
3617 * Check if relation can be moved to new tablespace.
3619 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3621 * Returns true if the relation can be moved to the new tablespace; raises
3622 * an error if it is not possible to do the move; returns false if the move
3623 * would have no effect.
3625 bool
3626 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3628 Oid oldTableSpaceId;
3631 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3632 * stored as 0.
3634 oldTableSpaceId = rel->rd_rel->reltablespace;
3635 if (newTableSpaceId == oldTableSpaceId ||
3636 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3637 return false;
3640 * We cannot support moving mapped relations into different tablespaces.
3641 * (In particular this eliminates all shared catalogs.)
3643 if (RelationIsMapped(rel))
3644 ereport(ERROR,
3645 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3646 errmsg("cannot move system relation \"%s\"",
3647 RelationGetRelationName(rel))));
3649 /* Cannot move a non-shared relation into pg_global */
3650 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3651 ereport(ERROR,
3652 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3653 errmsg("only shared relations can be placed in pg_global tablespace")));
3656 * Do not allow moving temp tables of other backends ... their local
3657 * buffer manager is not going to cope.
3659 if (RELATION_IS_OTHER_TEMP(rel))
3660 ereport(ERROR,
3661 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3662 errmsg("cannot move temporary tables of other sessions")));
3664 return true;
3668 * SetRelationTableSpace
3669 * Set new reltablespace and relfilenumber in pg_class entry.
3671 * newTableSpaceId is the new tablespace for the relation, and
3672 * newRelFilenumber its new filenumber. If newRelFilenumber is
3673 * InvalidRelFileNumber, this field is not updated.
3675 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3677 * The caller of this routine had better check if a relation can be
3678 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3679 * first, and is responsible for making the change visible with
3680 * CommandCounterIncrement().
3682 void
3683 SetRelationTableSpace(Relation rel,
3684 Oid newTableSpaceId,
3685 RelFileNumber newRelFilenumber)
3687 Relation pg_class;
3688 HeapTuple tuple;
3689 ItemPointerData otid;
3690 Form_pg_class rd_rel;
3691 Oid reloid = RelationGetRelid(rel);
3693 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3695 /* Get a modifiable copy of the relation's pg_class row. */
3696 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3698 tuple = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(reloid));
3699 if (!HeapTupleIsValid(tuple))
3700 elog(ERROR, "cache lookup failed for relation %u", reloid);
3701 otid = tuple->t_self;
3702 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3704 /* Update the pg_class row. */
3705 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3706 InvalidOid : newTableSpaceId;
3707 if (RelFileNumberIsValid(newRelFilenumber))
3708 rd_rel->relfilenode = newRelFilenumber;
3709 CatalogTupleUpdate(pg_class, &otid, tuple);
3710 UnlockTuple(pg_class, &otid, InplaceUpdateTupleLock);
3713 * Record dependency on tablespace. This is only required for relations
3714 * that have no physical storage.
3716 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3717 changeDependencyOnTablespace(RelationRelationId, reloid,
3718 rd_rel->reltablespace);
3720 heap_freetuple(tuple);
3721 table_close(pg_class, RowExclusiveLock);
3725 * renameatt_check - basic sanity checks before attribute rename
3727 static void
3728 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3730 char relkind = classform->relkind;
3732 if (classform->reloftype && !recursing)
3733 ereport(ERROR,
3734 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3735 errmsg("cannot rename column of typed table")));
3738 * Renaming the columns of sequences or toast tables doesn't actually
3739 * break anything from the system's point of view, since internal
3740 * references are by attnum. But it doesn't seem right to allow users to
3741 * change names that are hardcoded into the system, hence the following
3742 * restriction.
3744 if (relkind != RELKIND_RELATION &&
3745 relkind != RELKIND_VIEW &&
3746 relkind != RELKIND_MATVIEW &&
3747 relkind != RELKIND_COMPOSITE_TYPE &&
3748 relkind != RELKIND_INDEX &&
3749 relkind != RELKIND_PARTITIONED_INDEX &&
3750 relkind != RELKIND_FOREIGN_TABLE &&
3751 relkind != RELKIND_PARTITIONED_TABLE)
3752 ereport(ERROR,
3753 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3754 errmsg("cannot rename columns of relation \"%s\"",
3755 NameStr(classform->relname)),
3756 errdetail_relkind_not_supported(relkind)));
3759 * permissions checking. only the owner of a class can change its schema.
3761 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3762 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3763 NameStr(classform->relname));
3764 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3765 ereport(ERROR,
3766 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3767 errmsg("permission denied: \"%s\" is a system catalog",
3768 NameStr(classform->relname))));
3772 * renameatt_internal - workhorse for renameatt
3774 * Return value is the attribute number in the 'myrelid' relation.
3776 static AttrNumber
3777 renameatt_internal(Oid myrelid,
3778 const char *oldattname,
3779 const char *newattname,
3780 bool recurse,
3781 bool recursing,
3782 int expected_parents,
3783 DropBehavior behavior)
3785 Relation targetrelation;
3786 Relation attrelation;
3787 HeapTuple atttup;
3788 Form_pg_attribute attform;
3789 AttrNumber attnum;
3792 * Grab an exclusive lock on the target table, which we will NOT release
3793 * until end of transaction.
3795 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3796 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3799 * if the 'recurse' flag is set then we are supposed to rename this
3800 * attribute in all classes that inherit from 'relname' (as well as in
3801 * 'relname').
3803 * any permissions or problems with duplicate attributes will cause the
3804 * whole transaction to abort, which is what we want -- all or nothing.
3806 if (recurse)
3808 List *child_oids,
3809 *child_numparents;
3810 ListCell *lo,
3811 *li;
3814 * we need the number of parents for each child so that the recursive
3815 * calls to renameatt() can determine whether there are any parents
3816 * outside the inheritance hierarchy being processed.
3818 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3819 &child_numparents);
3822 * find_all_inheritors does the recursive search of the inheritance
3823 * hierarchy, so all we have to do is process all of the relids in the
3824 * list that it returns.
3826 forboth(lo, child_oids, li, child_numparents)
3828 Oid childrelid = lfirst_oid(lo);
3829 int numparents = lfirst_int(li);
3831 if (childrelid == myrelid)
3832 continue;
3833 /* note we need not recurse again */
3834 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3837 else
3840 * If we are told not to recurse, there had better not be any child
3841 * tables; else the rename would put them out of step.
3843 * expected_parents will only be 0 if we are not already recursing.
3845 if (expected_parents == 0 &&
3846 find_inheritance_children(myrelid, NoLock) != NIL)
3847 ereport(ERROR,
3848 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3849 errmsg("inherited column \"%s\" must be renamed in child tables too",
3850 oldattname)));
3853 /* rename attributes in typed tables of composite type */
3854 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3856 List *child_oids;
3857 ListCell *lo;
3859 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3860 RelationGetRelationName(targetrelation),
3861 behavior);
3863 foreach(lo, child_oids)
3864 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3867 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3869 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3870 if (!HeapTupleIsValid(atttup))
3871 ereport(ERROR,
3872 (errcode(ERRCODE_UNDEFINED_COLUMN),
3873 errmsg("column \"%s\" does not exist",
3874 oldattname)));
3875 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3877 attnum = attform->attnum;
3878 if (attnum <= 0)
3879 ereport(ERROR,
3880 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3881 errmsg("cannot rename system column \"%s\"",
3882 oldattname)));
3885 * if the attribute is inherited, forbid the renaming. if this is a
3886 * top-level call to renameatt(), then expected_parents will be 0, so the
3887 * effect of this code will be to prohibit the renaming if the attribute
3888 * is inherited at all. if this is a recursive call to renameatt(),
3889 * expected_parents will be the number of parents the current relation has
3890 * within the inheritance hierarchy being processed, so we'll prohibit the
3891 * renaming only if there are additional parents from elsewhere.
3893 if (attform->attinhcount > expected_parents)
3894 ereport(ERROR,
3895 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3896 errmsg("cannot rename inherited column \"%s\"",
3897 oldattname)));
3899 /* new name should not already exist */
3900 (void) check_for_column_name_collision(targetrelation, newattname, false);
3902 /* apply the update */
3903 namestrcpy(&(attform->attname), newattname);
3905 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3907 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3909 heap_freetuple(atttup);
3911 table_close(attrelation, RowExclusiveLock);
3913 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3915 return attnum;
3919 * Perform permissions and integrity checks before acquiring a relation lock.
3921 static void
3922 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3923 void *arg)
3925 HeapTuple tuple;
3926 Form_pg_class form;
3928 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3929 if (!HeapTupleIsValid(tuple))
3930 return; /* concurrently dropped */
3931 form = (Form_pg_class) GETSTRUCT(tuple);
3932 renameatt_check(relid, form, false);
3933 ReleaseSysCache(tuple);
3937 * renameatt - changes the name of an attribute in a relation
3939 * The returned ObjectAddress is that of the renamed column.
3941 ObjectAddress
3942 renameatt(RenameStmt *stmt)
3944 Oid relid;
3945 AttrNumber attnum;
3946 ObjectAddress address;
3948 /* lock level taken here should match renameatt_internal */
3949 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3950 stmt->missing_ok ? RVR_MISSING_OK : 0,
3951 RangeVarCallbackForRenameAttribute,
3952 NULL);
3954 if (!OidIsValid(relid))
3956 ereport(NOTICE,
3957 (errmsg("relation \"%s\" does not exist, skipping",
3958 stmt->relation->relname)));
3959 return InvalidObjectAddress;
3962 attnum =
3963 renameatt_internal(relid,
3964 stmt->subname, /* old att name */
3965 stmt->newname, /* new att name */
3966 stmt->relation->inh, /* recursive? */
3967 false, /* recursing? */
3968 0, /* expected inhcount */
3969 stmt->behavior);
3971 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3973 return address;
3977 * same logic as renameatt_internal
3979 static ObjectAddress
3980 rename_constraint_internal(Oid myrelid,
3981 Oid mytypid,
3982 const char *oldconname,
3983 const char *newconname,
3984 bool recurse,
3985 bool recursing,
3986 int expected_parents)
3988 Relation targetrelation = NULL;
3989 Oid constraintOid;
3990 HeapTuple tuple;
3991 Form_pg_constraint con;
3992 ObjectAddress address;
3994 Assert(!myrelid || !mytypid);
3996 if (mytypid)
3998 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4000 else
4002 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4005 * don't tell it whether we're recursing; we allow changing typed
4006 * tables here
4008 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4010 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4013 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4014 if (!HeapTupleIsValid(tuple))
4015 elog(ERROR, "cache lookup failed for constraint %u",
4016 constraintOid);
4017 con = (Form_pg_constraint) GETSTRUCT(tuple);
4019 if (myrelid &&
4020 (con->contype == CONSTRAINT_CHECK ||
4021 con->contype == CONSTRAINT_NOTNULL) &&
4022 !con->connoinherit)
4024 if (recurse)
4026 List *child_oids,
4027 *child_numparents;
4028 ListCell *lo,
4029 *li;
4031 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4032 &child_numparents);
4034 forboth(lo, child_oids, li, child_numparents)
4036 Oid childrelid = lfirst_oid(lo);
4037 int numparents = lfirst_int(li);
4039 if (childrelid == myrelid)
4040 continue;
4042 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4045 else
4047 if (expected_parents == 0 &&
4048 find_inheritance_children(myrelid, NoLock) != NIL)
4049 ereport(ERROR,
4050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4051 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4052 oldconname)));
4055 if (con->coninhcount > expected_parents)
4056 ereport(ERROR,
4057 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4058 errmsg("cannot rename inherited constraint \"%s\"",
4059 oldconname)));
4062 if (con->conindid
4063 && (con->contype == CONSTRAINT_PRIMARY
4064 || con->contype == CONSTRAINT_UNIQUE
4065 || con->contype == CONSTRAINT_EXCLUSION))
4066 /* rename the index; this renames the constraint as well */
4067 RenameRelationInternal(con->conindid, newconname, false, true);
4068 else
4069 RenameConstraintById(constraintOid, newconname);
4071 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4073 ReleaseSysCache(tuple);
4075 if (targetrelation)
4078 * Invalidate relcache so as others can see the new constraint name.
4080 CacheInvalidateRelcache(targetrelation);
4082 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4085 return address;
4088 ObjectAddress
4089 RenameConstraint(RenameStmt *stmt)
4091 Oid relid = InvalidOid;
4092 Oid typid = InvalidOid;
4094 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4096 Relation rel;
4097 HeapTuple tup;
4099 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4100 rel = table_open(TypeRelationId, RowExclusiveLock);
4101 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4102 if (!HeapTupleIsValid(tup))
4103 elog(ERROR, "cache lookup failed for type %u", typid);
4104 checkDomainOwner(tup);
4105 ReleaseSysCache(tup);
4106 table_close(rel, NoLock);
4108 else
4110 /* lock level taken here should match rename_constraint_internal */
4111 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4112 stmt->missing_ok ? RVR_MISSING_OK : 0,
4113 RangeVarCallbackForRenameAttribute,
4114 NULL);
4115 if (!OidIsValid(relid))
4117 ereport(NOTICE,
4118 (errmsg("relation \"%s\" does not exist, skipping",
4119 stmt->relation->relname)));
4120 return InvalidObjectAddress;
4124 return
4125 rename_constraint_internal(relid, typid,
4126 stmt->subname,
4127 stmt->newname,
4128 (stmt->relation &&
4129 stmt->relation->inh), /* recursive? */
4130 false, /* recursing? */
4131 0 /* expected inhcount */ );
4135 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4136 * RENAME
4138 ObjectAddress
4139 RenameRelation(RenameStmt *stmt)
4141 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4142 Oid relid;
4143 ObjectAddress address;
4146 * Grab an exclusive lock on the target table, index, sequence, view,
4147 * materialized view, or foreign table, which we will NOT release until
4148 * end of transaction.
4150 * Lock level used here should match RenameRelationInternal, to avoid lock
4151 * escalation. However, because ALTER INDEX can be used with any relation
4152 * type, we mustn't believe without verification.
4154 for (;;)
4156 LOCKMODE lockmode;
4157 char relkind;
4158 bool obj_is_index;
4160 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4162 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4163 stmt->missing_ok ? RVR_MISSING_OK : 0,
4164 RangeVarCallbackForAlterRelation,
4165 stmt);
4167 if (!OidIsValid(relid))
4169 ereport(NOTICE,
4170 (errmsg("relation \"%s\" does not exist, skipping",
4171 stmt->relation->relname)));
4172 return InvalidObjectAddress;
4176 * We allow mismatched statement and object types (e.g., ALTER INDEX
4177 * to rename a table), but we might've used the wrong lock level. If
4178 * that happens, retry with the correct lock level. We don't bother
4179 * if we already acquired AccessExclusiveLock with an index, however.
4181 relkind = get_rel_relkind(relid);
4182 obj_is_index = (relkind == RELKIND_INDEX ||
4183 relkind == RELKIND_PARTITIONED_INDEX);
4184 if (obj_is_index || is_index_stmt == obj_is_index)
4185 break;
4187 UnlockRelationOid(relid, lockmode);
4188 is_index_stmt = obj_is_index;
4191 /* Do the work */
4192 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4194 ObjectAddressSet(address, RelationRelationId, relid);
4196 return address;
4200 * RenameRelationInternal - change the name of a relation
4202 void
4203 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4205 Relation targetrelation;
4206 Relation relrelation; /* for RELATION relation */
4207 ItemPointerData otid;
4208 HeapTuple reltup;
4209 Form_pg_class relform;
4210 Oid namespaceId;
4213 * Grab a lock on the target relation, which we will NOT release until end
4214 * of transaction. We need at least a self-exclusive lock so that
4215 * concurrent DDL doesn't overwrite the rename if they start updating
4216 * while still seeing the old version. The lock also guards against
4217 * triggering relcache reloads in concurrent sessions, which might not
4218 * handle this information changing under them. For indexes, we can use a
4219 * reduced lock level because RelationReloadIndexInfo() handles indexes
4220 * specially.
4222 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4223 namespaceId = RelationGetNamespace(targetrelation);
4226 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4228 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4230 reltup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(myrelid));
4231 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4232 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4233 otid = reltup->t_self;
4234 relform = (Form_pg_class) GETSTRUCT(reltup);
4236 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4237 ereport(ERROR,
4238 (errcode(ERRCODE_DUPLICATE_TABLE),
4239 errmsg("relation \"%s\" already exists",
4240 newrelname)));
4243 * RenameRelation is careful not to believe the caller's idea of the
4244 * relation kind being handled. We don't have to worry about this, but
4245 * let's not be totally oblivious to it. We can process an index as
4246 * not-an-index, but not the other way around.
4248 Assert(!is_index ||
4249 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4250 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4253 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4254 * because it's a copy...)
4256 namestrcpy(&(relform->relname), newrelname);
4258 CatalogTupleUpdate(relrelation, &otid, reltup);
4259 UnlockTuple(relrelation, &otid, InplaceUpdateTupleLock);
4261 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4262 InvalidOid, is_internal);
4264 heap_freetuple(reltup);
4265 table_close(relrelation, RowExclusiveLock);
4268 * Also rename the associated type, if any.
4270 if (OidIsValid(targetrelation->rd_rel->reltype))
4271 RenameTypeInternal(targetrelation->rd_rel->reltype,
4272 newrelname, namespaceId);
4275 * Also rename the associated constraint, if any.
4277 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4278 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4280 Oid constraintId = get_index_constraint(myrelid);
4282 if (OidIsValid(constraintId))
4283 RenameConstraintById(constraintId, newrelname);
4287 * Close rel, but keep lock!
4289 relation_close(targetrelation, NoLock);
4293 * ResetRelRewrite - reset relrewrite
4295 void
4296 ResetRelRewrite(Oid myrelid)
4298 Relation relrelation; /* for RELATION relation */
4299 HeapTuple reltup;
4300 Form_pg_class relform;
4303 * Find relation's pg_class tuple.
4305 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4307 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4308 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4309 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4310 relform = (Form_pg_class) GETSTRUCT(reltup);
4313 * Update pg_class tuple.
4315 relform->relrewrite = InvalidOid;
4317 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4319 heap_freetuple(reltup);
4320 table_close(relrelation, RowExclusiveLock);
4324 * Disallow ALTER TABLE (and similar commands) when the current backend has
4325 * any open reference to the target table besides the one just acquired by
4326 * the calling command; this implies there's an open cursor or active plan.
4327 * We need this check because our lock doesn't protect us against stomping
4328 * on our own foot, only other people's feet!
4330 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4331 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4332 * possibly be relaxed to only error out for certain types of alterations.
4333 * But the use-case for allowing any of these things is not obvious, so we
4334 * won't work hard at it for now.
4336 * We also reject these commands if there are any pending AFTER trigger events
4337 * for the rel. This is certainly necessary for the rewriting variants of
4338 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4339 * events would try to fetch the wrong tuples. It might be overly cautious
4340 * in other cases, but again it seems better to err on the side of paranoia.
4342 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4343 * we are worried about active indexscans on the index. The trigger-event
4344 * check can be skipped, since we are doing no damage to the parent table.
4346 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4348 void
4349 CheckTableNotInUse(Relation rel, const char *stmt)
4351 int expected_refcnt;
4353 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4354 if (rel->rd_refcnt != expected_refcnt)
4355 ereport(ERROR,
4356 (errcode(ERRCODE_OBJECT_IN_USE),
4357 /* translator: first %s is a SQL command, eg ALTER TABLE */
4358 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4359 stmt, RelationGetRelationName(rel))));
4361 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4362 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4363 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4364 ereport(ERROR,
4365 (errcode(ERRCODE_OBJECT_IN_USE),
4366 /* translator: first %s is a SQL command, eg ALTER TABLE */
4367 errmsg("cannot %s \"%s\" because it has pending trigger events",
4368 stmt, RelationGetRelationName(rel))));
4372 * CheckAlterTableIsSafe
4373 * Verify that it's safe to allow ALTER TABLE on this relation.
4375 * This consists of CheckTableNotInUse() plus a check that the relation
4376 * isn't another session's temp table. We must split out the temp-table
4377 * check because there are callers of CheckTableNotInUse() that don't want
4378 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4379 * an orphaned temp schema.) Compare truncate_check_activity().
4381 static void
4382 CheckAlterTableIsSafe(Relation rel)
4385 * Don't allow ALTER on temp tables of other backends. Their local buffer
4386 * manager is not going to cope if we need to change the table's contents.
4387 * Even if we don't, there may be optimizations that assume temp tables
4388 * aren't subject to such interference.
4390 if (RELATION_IS_OTHER_TEMP(rel))
4391 ereport(ERROR,
4392 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4393 errmsg("cannot alter temporary tables of other sessions")));
4396 * Also check for active uses of the relation in the current transaction,
4397 * including open scans and pending AFTER trigger events.
4399 CheckTableNotInUse(rel, "ALTER TABLE");
4403 * AlterTableLookupRelation
4404 * Look up, and lock, the OID for the relation named by an alter table
4405 * statement.
4408 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4410 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4411 stmt->missing_ok ? RVR_MISSING_OK : 0,
4412 RangeVarCallbackForAlterRelation,
4413 stmt);
4417 * AlterTable
4418 * Execute ALTER TABLE, which can be a list of subcommands
4420 * ALTER TABLE is performed in three phases:
4421 * 1. Examine subcommands and perform pre-transformation checking.
4422 * 2. Validate and transform subcommands, and update system catalogs.
4423 * 3. Scan table(s) to check new constraints, and optionally recopy
4424 * the data into new table(s).
4425 * Phase 3 is not performed unless one or more of the subcommands requires
4426 * it. The intention of this design is to allow multiple independent
4427 * updates of the table schema to be performed with only one pass over the
4428 * data.
4430 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4431 * each table to be affected (there may be multiple affected tables if the
4432 * commands traverse a table inheritance hierarchy). Also we do preliminary
4433 * validation of the subcommands. Because earlier subcommands may change
4434 * the catalog state seen by later commands, there are limits to what can
4435 * be done in this phase. Generally, this phase acquires table locks,
4436 * checks permissions and relkind, and recurses to find child tables.
4438 * ATRewriteCatalogs performs phase 2 for each affected table.
4439 * Certain subcommands need to be performed before others to avoid
4440 * unnecessary conflicts; for example, DROP COLUMN should come before
4441 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4442 * lists, one for each logical "pass" of phase 2.
4444 * ATRewriteTables performs phase 3 for those tables that need it.
4446 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4447 * since phase 1 already does it. However, for certain subcommand types
4448 * it is only possible to determine how to recurse at phase 2 time; for
4449 * those cases, phase 1 sets the cmd->recurse flag.
4451 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4452 * the whole operation; we don't have to do anything special to clean up.
4454 * The caller must lock the relation, with an appropriate lock level
4455 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4456 * or higher. We pass the lock level down
4457 * so that we can apply it recursively to inherited tables. Note that the
4458 * lock level we want as we recurse might well be higher than required for
4459 * that specific subcommand. So we pass down the overall lock requirement,
4460 * rather than reassess it at lower levels.
4462 * The caller also provides a "context" which is to be passed back to
4463 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4464 * Some of the fields therein, such as the relid, are used here as well.
4466 void
4467 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4468 AlterTableUtilityContext *context)
4470 Relation rel;
4472 /* Caller is required to provide an adequate lock. */
4473 rel = relation_open(context->relid, NoLock);
4475 CheckAlterTableIsSafe(rel);
4477 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4481 * AlterTableInternal
4483 * ALTER TABLE with target specified by OID
4485 * We do not reject if the relation is already open, because it's quite
4486 * likely that one or more layers of caller have it open. That means it
4487 * is unsafe to use this entry point for alterations that could break
4488 * existing query plans. On the assumption it's not used for such, we
4489 * don't have to reject pending AFTER triggers, either.
4491 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4492 * used for any subcommand types that require parse transformation or
4493 * could generate subcommands that have to be passed to ProcessUtility.
4495 void
4496 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4498 Relation rel;
4499 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4501 rel = relation_open(relid, lockmode);
4503 EventTriggerAlterTableRelid(relid);
4505 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4509 * AlterTableGetLockLevel
4511 * Sets the overall lock level required for the supplied list of subcommands.
4512 * Policy for doing this set according to needs of AlterTable(), see
4513 * comments there for overall explanation.
4515 * Function is called before and after parsing, so it must give same
4516 * answer each time it is called. Some subcommands are transformed
4517 * into other subcommand types, so the transform must never be made to a
4518 * lower lock level than previously assigned. All transforms are noted below.
4520 * Since this is called before we lock the table we cannot use table metadata
4521 * to influence the type of lock we acquire.
4523 * There should be no lockmodes hardcoded into the subcommand functions. All
4524 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4525 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4526 * and does not travel through this section of code and cannot be combined with
4527 * any of the subcommands given here.
4529 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4530 * so any changes that might affect SELECTs running on standbys need to use
4531 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4532 * have a solution for that also.
4534 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4535 * that takes a lock less than AccessExclusiveLock can change object definitions
4536 * while pg_dump is running. Be careful to check that the appropriate data is
4537 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4538 * otherwise we might end up with an inconsistent dump that can't restore.
4540 LOCKMODE
4541 AlterTableGetLockLevel(List *cmds)
4544 * This only works if we read catalog tables using MVCC snapshots.
4546 ListCell *lcmd;
4547 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4549 foreach(lcmd, cmds)
4551 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4552 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4554 switch (cmd->subtype)
4557 * These subcommands rewrite the heap, so require full locks.
4559 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4560 * to SELECT */
4561 case AT_SetAccessMethod: /* must rewrite heap */
4562 case AT_SetTableSpace: /* must rewrite heap */
4563 case AT_AlterColumnType: /* must rewrite heap */
4564 cmd_lockmode = AccessExclusiveLock;
4565 break;
4568 * These subcommands may require addition of toast tables. If
4569 * we add a toast table to a table currently being scanned, we
4570 * might miss data added to the new toast table by concurrent
4571 * insert transactions.
4573 case AT_SetStorage: /* may add toast tables, see
4574 * ATRewriteCatalogs() */
4575 cmd_lockmode = AccessExclusiveLock;
4576 break;
4579 * Removing constraints can affect SELECTs that have been
4580 * optimized assuming the constraint holds true. See also
4581 * CloneFkReferenced.
4583 case AT_DropConstraint: /* as DROP INDEX */
4584 case AT_DropNotNull: /* may change some SQL plans */
4585 cmd_lockmode = AccessExclusiveLock;
4586 break;
4589 * Subcommands that may be visible to concurrent SELECTs
4591 case AT_DropColumn: /* change visible to SELECT */
4592 case AT_AddColumnToView: /* CREATE VIEW */
4593 case AT_DropOids: /* used to equiv to DropColumn */
4594 case AT_EnableAlwaysRule: /* may change SELECT rules */
4595 case AT_EnableReplicaRule: /* may change SELECT rules */
4596 case AT_EnableRule: /* may change SELECT rules */
4597 case AT_DisableRule: /* may change SELECT rules */
4598 cmd_lockmode = AccessExclusiveLock;
4599 break;
4602 * Changing owner may remove implicit SELECT privileges
4604 case AT_ChangeOwner: /* change visible to SELECT */
4605 cmd_lockmode = AccessExclusiveLock;
4606 break;
4609 * Changing foreign table options may affect optimization.
4611 case AT_GenericOptions:
4612 case AT_AlterColumnGenericOptions:
4613 cmd_lockmode = AccessExclusiveLock;
4614 break;
4617 * These subcommands affect write operations only.
4619 case AT_EnableTrig:
4620 case AT_EnableAlwaysTrig:
4621 case AT_EnableReplicaTrig:
4622 case AT_EnableTrigAll:
4623 case AT_EnableTrigUser:
4624 case AT_DisableTrig:
4625 case AT_DisableTrigAll:
4626 case AT_DisableTrigUser:
4627 cmd_lockmode = ShareRowExclusiveLock;
4628 break;
4631 * These subcommands affect write operations only. XXX
4632 * Theoretically, these could be ShareRowExclusiveLock.
4634 case AT_ColumnDefault:
4635 case AT_CookedColumnDefault:
4636 case AT_AlterConstraint:
4637 case AT_AddIndex: /* from ADD CONSTRAINT */
4638 case AT_AddIndexConstraint:
4639 case AT_ReplicaIdentity:
4640 case AT_SetNotNull:
4641 case AT_EnableRowSecurity:
4642 case AT_DisableRowSecurity:
4643 case AT_ForceRowSecurity:
4644 case AT_NoForceRowSecurity:
4645 case AT_AddIdentity:
4646 case AT_DropIdentity:
4647 case AT_SetIdentity:
4648 case AT_SetExpression:
4649 case AT_DropExpression:
4650 case AT_SetCompression:
4651 cmd_lockmode = AccessExclusiveLock;
4652 break;
4654 case AT_AddConstraint:
4655 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4656 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4657 if (IsA(cmd->def, Constraint))
4659 Constraint *con = (Constraint *) cmd->def;
4661 switch (con->contype)
4663 case CONSTR_EXCLUSION:
4664 case CONSTR_PRIMARY:
4665 case CONSTR_UNIQUE:
4668 * Cases essentially the same as CREATE INDEX. We
4669 * could reduce the lock strength to ShareLock if
4670 * we can work out how to allow concurrent catalog
4671 * updates. XXX Might be set down to
4672 * ShareRowExclusiveLock but requires further
4673 * analysis.
4675 cmd_lockmode = AccessExclusiveLock;
4676 break;
4677 case CONSTR_FOREIGN:
4680 * We add triggers to both tables when we add a
4681 * Foreign Key, so the lock level must be at least
4682 * as strong as CREATE TRIGGER.
4684 cmd_lockmode = ShareRowExclusiveLock;
4685 break;
4687 default:
4688 cmd_lockmode = AccessExclusiveLock;
4691 break;
4694 * These subcommands affect inheritance behaviour. Queries
4695 * started before us will continue to see the old inheritance
4696 * behaviour, while queries started after we commit will see
4697 * new behaviour. No need to prevent reads or writes to the
4698 * subtable while we hook it up though. Changing the TupDesc
4699 * may be a problem, so keep highest lock.
4701 case AT_AddInherit:
4702 case AT_DropInherit:
4703 cmd_lockmode = AccessExclusiveLock;
4704 break;
4707 * These subcommands affect implicit row type conversion. They
4708 * have affects similar to CREATE/DROP CAST on queries. don't
4709 * provide for invalidating parse trees as a result of such
4710 * changes, so we keep these at AccessExclusiveLock.
4712 case AT_AddOf:
4713 case AT_DropOf:
4714 cmd_lockmode = AccessExclusiveLock;
4715 break;
4718 * Only used by CREATE OR REPLACE VIEW which must conflict
4719 * with an SELECTs currently using the view.
4721 case AT_ReplaceRelOptions:
4722 cmd_lockmode = AccessExclusiveLock;
4723 break;
4726 * These subcommands affect general strategies for performance
4727 * and maintenance, though don't change the semantic results
4728 * from normal data reads and writes. Delaying an ALTER TABLE
4729 * behind currently active writes only delays the point where
4730 * the new strategy begins to take effect, so there is no
4731 * benefit in waiting. In this case the minimum restriction
4732 * applies: we don't currently allow concurrent catalog
4733 * updates.
4735 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4736 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4737 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4738 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4739 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4740 cmd_lockmode = ShareUpdateExclusiveLock;
4741 break;
4743 case AT_SetLogged:
4744 case AT_SetUnLogged:
4745 cmd_lockmode = AccessExclusiveLock;
4746 break;
4748 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4749 cmd_lockmode = ShareUpdateExclusiveLock;
4750 break;
4753 * Rel options are more complex than first appears. Options
4754 * are set here for tables, views and indexes; for historical
4755 * reasons these can all be used with ALTER TABLE, so we can't
4756 * decide between them using the basic grammar.
4758 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4759 * getTables() */
4760 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4761 * getTables() */
4762 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4763 break;
4765 case AT_AttachPartition:
4766 cmd_lockmode = ShareUpdateExclusiveLock;
4767 break;
4769 case AT_DetachPartition:
4770 if (((PartitionCmd *) cmd->def)->concurrent)
4771 cmd_lockmode = ShareUpdateExclusiveLock;
4772 else
4773 cmd_lockmode = AccessExclusiveLock;
4774 break;
4776 case AT_DetachPartitionFinalize:
4777 cmd_lockmode = ShareUpdateExclusiveLock;
4778 break;
4780 default: /* oops */
4781 elog(ERROR, "unrecognized alter table type: %d",
4782 (int) cmd->subtype);
4783 break;
4787 * Take the greatest lockmode from any subcommand
4789 if (cmd_lockmode > lockmode)
4790 lockmode = cmd_lockmode;
4793 return lockmode;
4797 * ATController provides top level control over the phases.
4799 * parsetree is passed in to allow it to be passed to event triggers
4800 * when requested.
4802 static void
4803 ATController(AlterTableStmt *parsetree,
4804 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4805 AlterTableUtilityContext *context)
4807 List *wqueue = NIL;
4808 ListCell *lcmd;
4810 /* Phase 1: preliminary examination of commands, create work queue */
4811 foreach(lcmd, cmds)
4813 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4815 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4818 /* Close the relation, but keep lock until commit */
4819 relation_close(rel, NoLock);
4821 /* Phase 2: update system catalogs */
4822 ATRewriteCatalogs(&wqueue, lockmode, context);
4824 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4825 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4829 * ATPrepCmd
4831 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4832 * recursion and permission checks.
4834 * Caller must have acquired appropriate lock type on relation already.
4835 * This lock should be held until commit.
4837 static void
4838 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4839 bool recurse, bool recursing, LOCKMODE lockmode,
4840 AlterTableUtilityContext *context)
4842 AlteredTableInfo *tab;
4843 AlterTablePass pass = AT_PASS_UNSET;
4845 /* Find or create work queue entry for this table */
4846 tab = ATGetQueueEntry(wqueue, rel);
4849 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4850 * partitions that are pending detach.
4852 if (rel->rd_rel->relispartition &&
4853 cmd->subtype != AT_DetachPartitionFinalize &&
4854 PartitionHasPendingDetach(RelationGetRelid(rel)))
4855 ereport(ERROR,
4856 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4857 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4858 RelationGetRelationName(rel)),
4859 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4862 * Copy the original subcommand for each table, so we can scribble on it.
4863 * This avoids conflicts when different child tables need to make
4864 * different parse transformations (for example, the same column may have
4865 * different column numbers in different children).
4867 cmd = copyObject(cmd);
4870 * Do permissions and relkind checking, recursion to child tables if
4871 * needed, and any additional phase-1 processing needed. (But beware of
4872 * adding any processing that looks at table details that another
4873 * subcommand could change. In some cases we reject multiple subcommands
4874 * that could try to change the same state in contrary ways.)
4876 switch (cmd->subtype)
4878 case AT_AddColumn: /* ADD COLUMN */
4879 ATSimplePermissions(cmd->subtype, rel,
4880 ATT_TABLE | ATT_PARTITIONED_TABLE |
4881 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4882 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4883 lockmode, context);
4884 /* Recursion occurs during execution phase */
4885 pass = AT_PASS_ADD_COL;
4886 break;
4887 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4888 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4889 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4890 lockmode, context);
4891 /* Recursion occurs during execution phase */
4892 pass = AT_PASS_ADD_COL;
4893 break;
4894 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4897 * We allow defaults on views so that INSERT into a view can have
4898 * default-ish behavior. This works because the rewriter
4899 * substitutes default values into INSERTs before it expands
4900 * rules.
4902 ATSimplePermissions(cmd->subtype, rel,
4903 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4904 ATT_FOREIGN_TABLE);
4905 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4906 /* No command-specific prep needed */
4907 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4908 break;
4909 case AT_CookedColumnDefault: /* add a pre-cooked default */
4910 /* This is currently used only in CREATE TABLE */
4911 /* (so the permission check really isn't necessary) */
4912 ATSimplePermissions(cmd->subtype, rel,
4913 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4914 /* This command never recurses */
4915 pass = AT_PASS_ADD_OTHERCONSTR;
4916 break;
4917 case AT_AddIdentity:
4918 ATSimplePermissions(cmd->subtype, rel,
4919 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4920 ATT_FOREIGN_TABLE);
4921 /* Set up recursion for phase 2; no other prep needed */
4922 if (recurse)
4923 cmd->recurse = true;
4924 pass = AT_PASS_ADD_OTHERCONSTR;
4925 break;
4926 case AT_SetIdentity:
4927 ATSimplePermissions(cmd->subtype, rel,
4928 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4929 ATT_FOREIGN_TABLE);
4930 /* Set up recursion for phase 2; no other prep needed */
4931 if (recurse)
4932 cmd->recurse = true;
4933 /* This should run after AddIdentity, so do it in MISC pass */
4934 pass = AT_PASS_MISC;
4935 break;
4936 case AT_DropIdentity:
4937 ATSimplePermissions(cmd->subtype, rel,
4938 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
4939 ATT_FOREIGN_TABLE);
4940 /* Set up recursion for phase 2; no other prep needed */
4941 if (recurse)
4942 cmd->recurse = true;
4943 pass = AT_PASS_DROP;
4944 break;
4945 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4946 ATSimplePermissions(cmd->subtype, rel,
4947 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4948 /* Set up recursion for phase 2; no other prep needed */
4949 if (recurse)
4950 cmd->recurse = true;
4951 pass = AT_PASS_DROP;
4952 break;
4953 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4954 ATSimplePermissions(cmd->subtype, rel,
4955 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4956 /* Set up recursion for phase 2; no other prep needed */
4957 if (recurse)
4958 cmd->recurse = true;
4959 pass = AT_PASS_COL_ATTRS;
4960 break;
4961 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4962 ATSimplePermissions(cmd->subtype, rel,
4963 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4964 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4965 pass = AT_PASS_SET_EXPRESSION;
4966 break;
4967 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4968 ATSimplePermissions(cmd->subtype, rel,
4969 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
4970 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4971 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4972 pass = AT_PASS_DROP;
4973 break;
4974 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4975 ATSimplePermissions(cmd->subtype, rel,
4976 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW |
4977 ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4978 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4979 /* No command-specific prep needed */
4980 pass = AT_PASS_MISC;
4981 break;
4982 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4983 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4984 ATSimplePermissions(cmd->subtype, rel,
4985 ATT_TABLE | ATT_PARTITIONED_TABLE |
4986 ATT_MATVIEW | ATT_FOREIGN_TABLE);
4987 /* This command never recurses */
4988 pass = AT_PASS_MISC;
4989 break;
4990 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4991 ATSimplePermissions(cmd->subtype, rel,
4992 ATT_TABLE | ATT_PARTITIONED_TABLE |
4993 ATT_MATVIEW | ATT_FOREIGN_TABLE);
4994 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4995 /* No command-specific prep needed */
4996 pass = AT_PASS_MISC;
4997 break;
4998 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4999 ATSimplePermissions(cmd->subtype, rel,
5000 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5001 /* This command never recurses */
5002 /* No command-specific prep needed */
5003 pass = AT_PASS_MISC;
5004 break;
5005 case AT_DropColumn: /* DROP COLUMN */
5006 ATSimplePermissions(cmd->subtype, rel,
5007 ATT_TABLE | ATT_PARTITIONED_TABLE |
5008 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5009 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5010 lockmode, context);
5011 /* Recursion occurs during execution phase */
5012 pass = AT_PASS_DROP;
5013 break;
5014 case AT_AddIndex: /* ADD INDEX */
5015 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5016 /* This command never recurses */
5017 /* No command-specific prep needed */
5018 pass = AT_PASS_ADD_INDEX;
5019 break;
5020 case AT_AddConstraint: /* ADD CONSTRAINT */
5021 ATSimplePermissions(cmd->subtype, rel,
5022 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5023 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5024 if (recurse)
5026 /* recurses at exec time; lock descendants and set flag */
5027 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5028 cmd->recurse = true;
5030 pass = AT_PASS_ADD_CONSTR;
5031 break;
5032 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5033 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE);
5034 /* This command never recurses */
5035 /* No command-specific prep needed */
5036 pass = AT_PASS_ADD_INDEXCONSTR;
5037 break;
5038 case AT_DropConstraint: /* DROP CONSTRAINT */
5039 ATSimplePermissions(cmd->subtype, rel,
5040 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5041 ATCheckPartitionsNotInUse(rel, lockmode);
5042 /* Other recursion occurs during execution phase */
5043 /* No command-specific prep needed except saving recurse flag */
5044 if (recurse)
5045 cmd->recurse = true;
5046 pass = AT_PASS_DROP;
5047 break;
5048 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5049 ATSimplePermissions(cmd->subtype, rel,
5050 ATT_TABLE | ATT_PARTITIONED_TABLE |
5051 ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5052 /* See comments for ATPrepAlterColumnType */
5053 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5054 AT_PASS_UNSET, context);
5055 Assert(cmd != NULL);
5056 /* Performs own recursion */
5057 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5058 lockmode, context);
5059 pass = AT_PASS_ALTER_TYPE;
5060 break;
5061 case AT_AlterColumnGenericOptions:
5062 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5063 /* This command never recurses */
5064 /* No command-specific prep needed */
5065 pass = AT_PASS_MISC;
5066 break;
5067 case AT_ChangeOwner: /* ALTER OWNER */
5068 /* This command never recurses */
5069 /* No command-specific prep needed */
5070 pass = AT_PASS_MISC;
5071 break;
5072 case AT_ClusterOn: /* CLUSTER ON */
5073 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5074 ATSimplePermissions(cmd->subtype, rel,
5075 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5076 /* These commands never recurse */
5077 /* No command-specific prep needed */
5078 pass = AT_PASS_MISC;
5079 break;
5080 case AT_SetLogged: /* SET LOGGED */
5081 case AT_SetUnLogged: /* SET UNLOGGED */
5082 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5083 if (tab->chgPersistence)
5084 ereport(ERROR,
5085 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5086 errmsg("cannot change persistence setting twice")));
5087 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5088 pass = AT_PASS_MISC;
5089 break;
5090 case AT_DropOids: /* SET WITHOUT OIDS */
5091 ATSimplePermissions(cmd->subtype, rel,
5092 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5093 pass = AT_PASS_DROP;
5094 break;
5095 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5096 ATSimplePermissions(cmd->subtype, rel,
5097 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5099 /* check if another access method change was already requested */
5100 if (tab->chgAccessMethod)
5101 ereport(ERROR,
5102 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5103 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5105 ATPrepSetAccessMethod(tab, rel, cmd->name);
5106 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5107 break;
5108 case AT_SetTableSpace: /* SET TABLESPACE */
5109 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_TABLE |
5110 ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX);
5111 /* This command never recurses */
5112 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5113 pass = AT_PASS_MISC; /* doesn't actually matter */
5114 break;
5115 case AT_SetRelOptions: /* SET (...) */
5116 case AT_ResetRelOptions: /* RESET (...) */
5117 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5118 ATSimplePermissions(cmd->subtype, rel,
5119 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_VIEW |
5120 ATT_MATVIEW | ATT_INDEX);
5121 /* This command never recurses */
5122 /* No command-specific prep needed */
5123 pass = AT_PASS_MISC;
5124 break;
5125 case AT_AddInherit: /* INHERIT */
5126 ATSimplePermissions(cmd->subtype, rel,
5127 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5128 /* This command never recurses */
5129 ATPrepAddInherit(rel);
5130 pass = AT_PASS_MISC;
5131 break;
5132 case AT_DropInherit: /* NO INHERIT */
5133 ATSimplePermissions(cmd->subtype, rel,
5134 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5135 /* This command never recurses */
5136 /* No command-specific prep needed */
5137 pass = AT_PASS_MISC;
5138 break;
5139 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5140 ATSimplePermissions(cmd->subtype, rel,
5141 ATT_TABLE | ATT_PARTITIONED_TABLE);
5142 /* Recursion occurs during execution phase */
5143 pass = AT_PASS_MISC;
5144 break;
5145 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5146 ATSimplePermissions(cmd->subtype, rel,
5147 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5148 /* Recursion occurs during execution phase */
5149 /* No command-specific prep needed except saving recurse flag */
5150 if (recurse)
5151 cmd->recurse = true;
5152 pass = AT_PASS_MISC;
5153 break;
5154 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5155 ATSimplePermissions(cmd->subtype, rel,
5156 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_MATVIEW);
5157 pass = AT_PASS_MISC;
5158 /* This command never recurses */
5159 /* No command-specific prep needed */
5160 break;
5161 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5162 case AT_EnableAlwaysTrig:
5163 case AT_EnableReplicaTrig:
5164 case AT_EnableTrigAll:
5165 case AT_EnableTrigUser:
5166 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5167 case AT_DisableTrigAll:
5168 case AT_DisableTrigUser:
5169 ATSimplePermissions(cmd->subtype, rel,
5170 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
5171 /* Set up recursion for phase 2; no other prep needed */
5172 if (recurse)
5173 cmd->recurse = true;
5174 pass = AT_PASS_MISC;
5175 break;
5176 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5177 case AT_EnableAlwaysRule:
5178 case AT_EnableReplicaRule:
5179 case AT_DisableRule:
5180 case AT_AddOf: /* OF */
5181 case AT_DropOf: /* NOT OF */
5182 case AT_EnableRowSecurity:
5183 case AT_DisableRowSecurity:
5184 case AT_ForceRowSecurity:
5185 case AT_NoForceRowSecurity:
5186 ATSimplePermissions(cmd->subtype, rel,
5187 ATT_TABLE | ATT_PARTITIONED_TABLE);
5188 /* These commands never recurse */
5189 /* No command-specific prep needed */
5190 pass = AT_PASS_MISC;
5191 break;
5192 case AT_GenericOptions:
5193 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5194 /* No command-specific prep needed */
5195 pass = AT_PASS_MISC;
5196 break;
5197 case AT_AttachPartition:
5198 ATSimplePermissions(cmd->subtype, rel,
5199 ATT_PARTITIONED_TABLE | ATT_PARTITIONED_INDEX);
5200 /* No command-specific prep needed */
5201 pass = AT_PASS_MISC;
5202 break;
5203 case AT_DetachPartition:
5204 ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5205 /* No command-specific prep needed */
5206 pass = AT_PASS_MISC;
5207 break;
5208 case AT_DetachPartitionFinalize:
5209 ATSimplePermissions(cmd->subtype, rel, ATT_PARTITIONED_TABLE);
5210 /* No command-specific prep needed */
5211 pass = AT_PASS_MISC;
5212 break;
5213 default: /* oops */
5214 elog(ERROR, "unrecognized alter table type: %d",
5215 (int) cmd->subtype);
5216 pass = AT_PASS_UNSET; /* keep compiler quiet */
5217 break;
5219 Assert(pass > AT_PASS_UNSET);
5221 /* Add the subcommand to the appropriate list for phase 2 */
5222 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5226 * ATRewriteCatalogs
5228 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5229 * dispatched in a "safe" execution order (designed to avoid unnecessary
5230 * conflicts).
5232 static void
5233 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5234 AlterTableUtilityContext *context)
5236 ListCell *ltab;
5239 * We process all the tables "in parallel", one pass at a time. This is
5240 * needed because we may have to propagate work from one table to another
5241 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5242 * re-adding of the foreign key constraint to the other table). Work can
5243 * only be propagated into later passes, however.
5245 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5247 /* Go through each table that needs to be processed */
5248 foreach(ltab, *wqueue)
5250 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5251 List *subcmds = tab->subcmds[pass];
5252 ListCell *lcmd;
5254 if (subcmds == NIL)
5255 continue;
5258 * Open the relation and store it in tab. This allows subroutines
5259 * close and reopen, if necessary. Appropriate lock was obtained
5260 * by phase 1, needn't get it again.
5262 tab->rel = relation_open(tab->relid, NoLock);
5264 foreach(lcmd, subcmds)
5265 ATExecCmd(wqueue, tab,
5266 lfirst_node(AlterTableCmd, lcmd),
5267 lockmode, pass, context);
5270 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5271 * (this is not done in ATExecAlterColumnType since it should be
5272 * done only once if multiple columns of a table are altered).
5274 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5275 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5277 if (tab->rel)
5279 relation_close(tab->rel, NoLock);
5280 tab->rel = NULL;
5285 /* Check to see if a toast table must be added. */
5286 foreach(ltab, *wqueue)
5288 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5291 * If the table is source table of ATTACH PARTITION command, we did
5292 * not modify anything about it that will change its toasting
5293 * requirement, so no need to check.
5295 if (((tab->relkind == RELKIND_RELATION ||
5296 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5297 tab->partition_constraint == NULL) ||
5298 tab->relkind == RELKIND_MATVIEW)
5299 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5304 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5306 static void
5307 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5308 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5309 AlterTableUtilityContext *context)
5311 ObjectAddress address = InvalidObjectAddress;
5312 Relation rel = tab->rel;
5314 switch (cmd->subtype)
5316 case AT_AddColumn: /* ADD COLUMN */
5317 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5318 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5319 cmd->recurse, false,
5320 lockmode, cur_pass, context);
5321 break;
5322 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5323 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5324 break;
5325 case AT_CookedColumnDefault: /* add a pre-cooked default */
5326 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5327 break;
5328 case AT_AddIdentity:
5329 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5330 cur_pass, context);
5331 Assert(cmd != NULL);
5332 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5333 break;
5334 case AT_SetIdentity:
5335 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5336 cur_pass, context);
5337 Assert(cmd != NULL);
5338 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5339 break;
5340 case AT_DropIdentity:
5341 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5342 break;
5343 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5344 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5345 break;
5346 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5347 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5348 cmd->recurse, false, lockmode);
5349 break;
5350 case AT_SetExpression:
5351 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5352 break;
5353 case AT_DropExpression:
5354 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5355 break;
5356 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5357 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5358 break;
5359 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5360 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5361 break;
5362 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5363 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5364 break;
5365 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5366 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5367 break;
5368 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5369 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5370 lockmode);
5371 break;
5372 case AT_DropColumn: /* DROP COLUMN */
5373 address = ATExecDropColumn(wqueue, rel, cmd->name,
5374 cmd->behavior, cmd->recurse, false,
5375 cmd->missing_ok, lockmode,
5376 NULL);
5377 break;
5378 case AT_AddIndex: /* ADD INDEX */
5379 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5380 lockmode);
5381 break;
5382 case AT_ReAddIndex: /* ADD INDEX */
5383 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5384 lockmode);
5385 break;
5386 case AT_ReAddStatistics: /* ADD STATISTICS */
5387 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5388 true, lockmode);
5389 break;
5390 case AT_AddConstraint: /* ADD CONSTRAINT */
5391 /* Transform the command only during initial examination */
5392 if (cur_pass == AT_PASS_ADD_CONSTR)
5393 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5394 cmd->recurse, lockmode,
5395 cur_pass, context);
5396 /* Depending on constraint type, might be no more work to do now */
5397 if (cmd != NULL)
5398 address =
5399 ATExecAddConstraint(wqueue, tab, rel,
5400 (Constraint *) cmd->def,
5401 cmd->recurse, false, lockmode);
5402 break;
5403 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5404 address =
5405 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5406 true, true, lockmode);
5407 break;
5408 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5409 * constraint */
5410 address =
5411 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5412 ((AlterDomainStmt *) cmd->def)->def,
5413 NULL);
5414 break;
5415 case AT_ReAddComment: /* Re-add existing comment */
5416 address = CommentObject((CommentStmt *) cmd->def);
5417 break;
5418 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5419 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5420 lockmode);
5421 break;
5422 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5423 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5424 break;
5425 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5426 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5427 false, lockmode);
5428 break;
5429 case AT_DropConstraint: /* DROP CONSTRAINT */
5430 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5431 cmd->recurse,
5432 cmd->missing_ok, lockmode);
5433 break;
5434 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5435 /* parse transformation was done earlier */
5436 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5437 break;
5438 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5439 address =
5440 ATExecAlterColumnGenericOptions(rel, cmd->name,
5441 (List *) cmd->def, lockmode);
5442 break;
5443 case AT_ChangeOwner: /* ALTER OWNER */
5444 ATExecChangeOwner(RelationGetRelid(rel),
5445 get_rolespec_oid(cmd->newowner, false),
5446 false, lockmode);
5447 break;
5448 case AT_ClusterOn: /* CLUSTER ON */
5449 address = ATExecClusterOn(rel, cmd->name, lockmode);
5450 break;
5451 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5452 ATExecDropCluster(rel, lockmode);
5453 break;
5454 case AT_SetLogged: /* SET LOGGED */
5455 case AT_SetUnLogged: /* SET UNLOGGED */
5456 break;
5457 case AT_DropOids: /* SET WITHOUT OIDS */
5458 /* nothing to do here, oid columns don't exist anymore */
5459 break;
5460 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5463 * Only do this for partitioned tables, for which this is just a
5464 * catalog change. Tables with storage are handled by Phase 3.
5466 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5467 tab->chgAccessMethod)
5468 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5469 break;
5470 case AT_SetTableSpace: /* SET TABLESPACE */
5473 * Only do this for partitioned tables and indexes, for which this
5474 * is just a catalog change. Other relation types which have
5475 * storage are handled by Phase 3.
5477 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5478 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5479 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5481 break;
5482 case AT_SetRelOptions: /* SET (...) */
5483 case AT_ResetRelOptions: /* RESET (...) */
5484 case AT_ReplaceRelOptions: /* replace entire option list */
5485 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5486 break;
5487 case AT_EnableTrig: /* ENABLE TRIGGER name */
5488 ATExecEnableDisableTrigger(rel, cmd->name,
5489 TRIGGER_FIRES_ON_ORIGIN, false,
5490 cmd->recurse,
5491 lockmode);
5492 break;
5493 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5494 ATExecEnableDisableTrigger(rel, cmd->name,
5495 TRIGGER_FIRES_ALWAYS, false,
5496 cmd->recurse,
5497 lockmode);
5498 break;
5499 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5500 ATExecEnableDisableTrigger(rel, cmd->name,
5501 TRIGGER_FIRES_ON_REPLICA, false,
5502 cmd->recurse,
5503 lockmode);
5504 break;
5505 case AT_DisableTrig: /* DISABLE TRIGGER name */
5506 ATExecEnableDisableTrigger(rel, cmd->name,
5507 TRIGGER_DISABLED, false,
5508 cmd->recurse,
5509 lockmode);
5510 break;
5511 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5512 ATExecEnableDisableTrigger(rel, NULL,
5513 TRIGGER_FIRES_ON_ORIGIN, false,
5514 cmd->recurse,
5515 lockmode);
5516 break;
5517 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5518 ATExecEnableDisableTrigger(rel, NULL,
5519 TRIGGER_DISABLED, false,
5520 cmd->recurse,
5521 lockmode);
5522 break;
5523 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5524 ATExecEnableDisableTrigger(rel, NULL,
5525 TRIGGER_FIRES_ON_ORIGIN, true,
5526 cmd->recurse,
5527 lockmode);
5528 break;
5529 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5530 ATExecEnableDisableTrigger(rel, NULL,
5531 TRIGGER_DISABLED, true,
5532 cmd->recurse,
5533 lockmode);
5534 break;
5536 case AT_EnableRule: /* ENABLE RULE name */
5537 ATExecEnableDisableRule(rel, cmd->name,
5538 RULE_FIRES_ON_ORIGIN, lockmode);
5539 break;
5540 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5541 ATExecEnableDisableRule(rel, cmd->name,
5542 RULE_FIRES_ALWAYS, lockmode);
5543 break;
5544 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5545 ATExecEnableDisableRule(rel, cmd->name,
5546 RULE_FIRES_ON_REPLICA, lockmode);
5547 break;
5548 case AT_DisableRule: /* DISABLE RULE name */
5549 ATExecEnableDisableRule(rel, cmd->name,
5550 RULE_DISABLED, lockmode);
5551 break;
5553 case AT_AddInherit:
5554 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5555 break;
5556 case AT_DropInherit:
5557 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5558 break;
5559 case AT_AddOf:
5560 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5561 break;
5562 case AT_DropOf:
5563 ATExecDropOf(rel, lockmode);
5564 break;
5565 case AT_ReplicaIdentity:
5566 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5567 break;
5568 case AT_EnableRowSecurity:
5569 ATExecSetRowSecurity(rel, true);
5570 break;
5571 case AT_DisableRowSecurity:
5572 ATExecSetRowSecurity(rel, false);
5573 break;
5574 case AT_ForceRowSecurity:
5575 ATExecForceNoForceRowSecurity(rel, true);
5576 break;
5577 case AT_NoForceRowSecurity:
5578 ATExecForceNoForceRowSecurity(rel, false);
5579 break;
5580 case AT_GenericOptions:
5581 ATExecGenericOptions(rel, (List *) cmd->def);
5582 break;
5583 case AT_AttachPartition:
5584 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5585 cur_pass, context);
5586 Assert(cmd != NULL);
5587 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5588 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5589 context);
5590 else
5591 address = ATExecAttachPartitionIdx(wqueue, rel,
5592 ((PartitionCmd *) cmd->def)->name);
5593 break;
5594 case AT_DetachPartition:
5595 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5596 cur_pass, context);
5597 Assert(cmd != NULL);
5598 /* ATPrepCmd ensures it must be a table */
5599 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5600 address = ATExecDetachPartition(wqueue, tab, rel,
5601 ((PartitionCmd *) cmd->def)->name,
5602 ((PartitionCmd *) cmd->def)->concurrent);
5603 break;
5604 case AT_DetachPartitionFinalize:
5605 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5606 break;
5607 default: /* oops */
5608 elog(ERROR, "unrecognized alter table type: %d",
5609 (int) cmd->subtype);
5610 break;
5614 * Report the subcommand to interested event triggers.
5616 if (cmd)
5617 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5620 * Bump the command counter to ensure the next subcommand in the sequence
5621 * can see the changes so far
5623 CommandCounterIncrement();
5627 * ATParseTransformCmd: perform parse transformation for one subcommand
5629 * Returns the transformed subcommand tree, if there is one, else NULL.
5631 * The parser may hand back additional AlterTableCmd(s) and/or other
5632 * utility statements, either before or after the original subcommand.
5633 * Other AlterTableCmds are scheduled into the appropriate slot of the
5634 * AlteredTableInfo (they had better be for later passes than the current one).
5635 * Utility statements that are supposed to happen before the AlterTableCmd
5636 * are executed immediately. Those that are supposed to happen afterwards
5637 * are added to the tab->afterStmts list to be done at the very end.
5639 static AlterTableCmd *
5640 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5641 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5642 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5644 AlterTableCmd *newcmd = NULL;
5645 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5646 List *beforeStmts;
5647 List *afterStmts;
5648 ListCell *lc;
5650 /* Gin up an AlterTableStmt with just this subcommand and this table */
5651 atstmt->relation =
5652 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5653 pstrdup(RelationGetRelationName(rel)),
5654 -1);
5655 atstmt->relation->inh = recurse;
5656 atstmt->cmds = list_make1(cmd);
5657 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5658 atstmt->missing_ok = false;
5660 /* Transform the AlterTableStmt */
5661 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5662 atstmt,
5663 context->queryString,
5664 &beforeStmts,
5665 &afterStmts);
5667 /* Execute any statements that should happen before these subcommand(s) */
5668 foreach(lc, beforeStmts)
5670 Node *stmt = (Node *) lfirst(lc);
5672 ProcessUtilityForAlterTable(stmt, context);
5673 CommandCounterIncrement();
5676 /* Examine the transformed subcommands and schedule them appropriately */
5677 foreach(lc, atstmt->cmds)
5679 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5680 AlterTablePass pass;
5683 * This switch need only cover the subcommand types that can be added
5684 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5685 * executing the subcommand immediately, as a substitute for the
5686 * original subcommand. (Note, however, that this does cause
5687 * AT_AddConstraint subcommands to be rescheduled into later passes,
5688 * which is important for index and foreign key constraints.)
5690 * We assume we needn't do any phase-1 checks for added subcommands.
5692 switch (cmd2->subtype)
5694 case AT_AddIndex:
5695 pass = AT_PASS_ADD_INDEX;
5696 break;
5697 case AT_AddIndexConstraint:
5698 pass = AT_PASS_ADD_INDEXCONSTR;
5699 break;
5700 case AT_AddConstraint:
5701 /* Recursion occurs during execution phase */
5702 if (recurse)
5703 cmd2->recurse = true;
5704 switch (castNode(Constraint, cmd2->def)->contype)
5706 case CONSTR_NOTNULL:
5707 pass = AT_PASS_COL_ATTRS;
5708 break;
5709 case CONSTR_PRIMARY:
5710 case CONSTR_UNIQUE:
5711 case CONSTR_EXCLUSION:
5712 pass = AT_PASS_ADD_INDEXCONSTR;
5713 break;
5714 default:
5715 pass = AT_PASS_ADD_OTHERCONSTR;
5716 break;
5718 break;
5719 case AT_AlterColumnGenericOptions:
5720 /* This command never recurses */
5721 /* No command-specific prep needed */
5722 pass = AT_PASS_MISC;
5723 break;
5724 default:
5725 pass = cur_pass;
5726 break;
5729 if (pass < cur_pass)
5731 /* Cannot schedule into a pass we already finished */
5732 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5733 pass);
5735 else if (pass > cur_pass)
5737 /* OK, queue it up for later */
5738 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5740 else
5743 * We should see at most one subcommand for the current pass,
5744 * which is the transformed version of the original subcommand.
5746 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5748 /* Found the transformed version of our subcommand */
5749 newcmd = cmd2;
5751 else
5752 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5753 pass);
5757 /* Queue up any after-statements to happen at the end */
5758 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5760 return newcmd;
5764 * ATRewriteTables: ALTER TABLE phase 3
5766 static void
5767 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5768 AlterTableUtilityContext *context)
5770 ListCell *ltab;
5772 /* Go through each table that needs to be checked or rewritten */
5773 foreach(ltab, *wqueue)
5775 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5777 /* Relations without storage may be ignored here */
5778 if (!RELKIND_HAS_STORAGE(tab->relkind))
5779 continue;
5782 * If we change column data types, the operation has to be propagated
5783 * to tables that use this table's rowtype as a column type.
5784 * tab->newvals will also be non-NULL in the case where we're adding a
5785 * column with a default. We choose to forbid that case as well,
5786 * since composite types might eventually support defaults.
5788 * (Eventually we'll probably need to check for composite type
5789 * dependencies even when we're just scanning the table without a
5790 * rewrite, but at the moment a composite type does not enforce any
5791 * constraints, so it's not necessary/appropriate to enforce them just
5792 * during ALTER.)
5794 if (tab->newvals != NIL || tab->rewrite > 0)
5796 Relation rel;
5798 rel = table_open(tab->relid, NoLock);
5799 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5800 table_close(rel, NoLock);
5804 * We only need to rewrite the table if at least one column needs to
5805 * be recomputed, or we are changing its persistence or access method.
5807 * There are two reasons for requiring a rewrite when changing
5808 * persistence: on one hand, we need to ensure that the buffers
5809 * belonging to each of the two relations are marked with or without
5810 * BM_PERMANENT properly. On the other hand, since rewriting creates
5811 * and assigns a new relfilenumber, we automatically create or drop an
5812 * init fork for the relation as appropriate.
5814 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5816 /* Build a temporary relation and copy data */
5817 Relation OldHeap;
5818 Oid OIDNewHeap;
5819 Oid NewAccessMethod;
5820 Oid NewTableSpace;
5821 char persistence;
5823 OldHeap = table_open(tab->relid, NoLock);
5826 * We don't support rewriting of system catalogs; there are too
5827 * many corner cases and too little benefit. In particular this
5828 * is certainly not going to work for mapped catalogs.
5830 if (IsSystemRelation(OldHeap))
5831 ereport(ERROR,
5832 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5833 errmsg("cannot rewrite system relation \"%s\"",
5834 RelationGetRelationName(OldHeap))));
5836 if (RelationIsUsedAsCatalogTable(OldHeap))
5837 ereport(ERROR,
5838 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5839 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5840 RelationGetRelationName(OldHeap))));
5843 * Don't allow rewrite on temp tables of other backends ... their
5844 * local buffer manager is not going to cope. (This is redundant
5845 * with the check in CheckAlterTableIsSafe, but for safety we'll
5846 * check here too.)
5848 if (RELATION_IS_OTHER_TEMP(OldHeap))
5849 ereport(ERROR,
5850 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5851 errmsg("cannot rewrite temporary tables of other sessions")));
5854 * Select destination tablespace (same as original unless user
5855 * requested a change)
5857 if (tab->newTableSpace)
5858 NewTableSpace = tab->newTableSpace;
5859 else
5860 NewTableSpace = OldHeap->rd_rel->reltablespace;
5863 * Select destination access method (same as original unless user
5864 * requested a change)
5866 if (tab->chgAccessMethod)
5867 NewAccessMethod = tab->newAccessMethod;
5868 else
5869 NewAccessMethod = OldHeap->rd_rel->relam;
5872 * Select persistence of transient table (same as original unless
5873 * user requested a change)
5875 persistence = tab->chgPersistence ?
5876 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5878 table_close(OldHeap, NoLock);
5881 * Fire off an Event Trigger now, before actually rewriting the
5882 * table.
5884 * We don't support Event Trigger for nested commands anywhere,
5885 * here included, and parsetree is given NULL when coming from
5886 * AlterTableInternal.
5888 * And fire it only once.
5890 if (parsetree)
5891 EventTriggerTableRewrite((Node *) parsetree,
5892 tab->relid,
5893 tab->rewrite);
5896 * Create transient table that will receive the modified data.
5898 * Ensure it is marked correctly as logged or unlogged. We have
5899 * to do this here so that buffers for the new relfilenumber will
5900 * have the right persistence set, and at the same time ensure
5901 * that the original filenumbers's buffers will get read in with
5902 * the correct setting (i.e. the original one). Otherwise a
5903 * rollback after the rewrite would possibly result with buffers
5904 * for the original filenumbers having the wrong persistence
5905 * setting.
5907 * NB: This relies on swap_relation_files() also swapping the
5908 * persistence. That wouldn't work for pg_class, but that can't be
5909 * unlogged anyway.
5911 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5912 persistence, lockmode);
5915 * Copy the heap data into the new table with the desired
5916 * modifications, and test the current data within the table
5917 * against new constraints generated by ALTER TABLE commands.
5919 ATRewriteTable(tab, OIDNewHeap);
5922 * Swap the physical files of the old and new heaps, then rebuild
5923 * indexes and discard the old heap. We can use RecentXmin for
5924 * the table's new relfrozenxid because we rewrote all the tuples
5925 * in ATRewriteTable, so no older Xid remains in the table. Also,
5926 * we never try to swap toast tables by content, since we have no
5927 * interest in letting this code work on system catalogs.
5929 finish_heap_swap(tab->relid, OIDNewHeap,
5930 false, false, true,
5931 !OidIsValid(tab->newTableSpace),
5932 RecentXmin,
5933 ReadNextMultiXactId(),
5934 persistence);
5936 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5938 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5940 if (tab->chgPersistence)
5941 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5943 else
5946 * If required, test the current data within the table against new
5947 * constraints generated by ALTER TABLE commands, but don't
5948 * rebuild data.
5950 if (tab->constraints != NIL || tab->verify_new_notnull ||
5951 tab->partition_constraint != NULL)
5952 ATRewriteTable(tab, InvalidOid);
5955 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5956 * just do a block-by-block copy.
5958 if (tab->newTableSpace)
5959 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5963 * Also change persistence of owned sequences, so that it matches the
5964 * table persistence.
5966 if (tab->chgPersistence)
5968 List *seqlist = getOwnedSequences(tab->relid);
5969 ListCell *lc;
5971 foreach(lc, seqlist)
5973 Oid seq_relid = lfirst_oid(lc);
5975 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5981 * Foreign key constraints are checked in a final pass, since (a) it's
5982 * generally best to examine each one separately, and (b) it's at least
5983 * theoretically possible that we have changed both relations of the
5984 * foreign key, and we'd better have finished both rewrites before we try
5985 * to read the tables.
5987 foreach(ltab, *wqueue)
5989 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5990 Relation rel = NULL;
5991 ListCell *lcon;
5993 /* Relations without storage may be ignored here too */
5994 if (!RELKIND_HAS_STORAGE(tab->relkind))
5995 continue;
5997 foreach(lcon, tab->constraints)
5999 NewConstraint *con = lfirst(lcon);
6001 if (con->contype == CONSTR_FOREIGN)
6003 Constraint *fkconstraint = (Constraint *) con->qual;
6004 Relation refrel;
6006 if (rel == NULL)
6008 /* Long since locked, no need for another */
6009 rel = table_open(tab->relid, NoLock);
6012 refrel = table_open(con->refrelid, RowShareLock);
6014 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6015 con->refindid,
6016 con->conid,
6017 con->conwithperiod);
6020 * No need to mark the constraint row as validated, we did
6021 * that when we inserted the row earlier.
6024 table_close(refrel, NoLock);
6028 if (rel)
6029 table_close(rel, NoLock);
6032 /* Finally, run any afterStmts that were queued up */
6033 foreach(ltab, *wqueue)
6035 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6036 ListCell *lc;
6038 foreach(lc, tab->afterStmts)
6040 Node *stmt = (Node *) lfirst(lc);
6042 ProcessUtilityForAlterTable(stmt, context);
6043 CommandCounterIncrement();
6049 * ATRewriteTable: scan or rewrite one table
6051 * A rewrite is requested by passing a valid OIDNewHeap; in that case, caller
6052 * must already hold AccessExclusiveLock on it.
6054 static void
6055 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap)
6057 Relation oldrel;
6058 Relation newrel;
6059 TupleDesc oldTupDesc;
6060 TupleDesc newTupDesc;
6061 bool needscan = false;
6062 List *notnull_attrs;
6063 int i;
6064 ListCell *l;
6065 EState *estate;
6066 CommandId mycid;
6067 BulkInsertState bistate;
6068 int ti_options;
6069 ExprState *partqualstate = NULL;
6072 * Open the relation(s). We have surely already locked the existing
6073 * table.
6075 oldrel = table_open(tab->relid, NoLock);
6076 oldTupDesc = tab->oldDesc;
6077 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6079 if (OidIsValid(OIDNewHeap))
6081 Assert(CheckRelationOidLockedByMe(OIDNewHeap, AccessExclusiveLock,
6082 false));
6083 newrel = table_open(OIDNewHeap, NoLock);
6085 else
6086 newrel = NULL;
6089 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6090 * is empty, so don't bother using it.
6092 if (newrel)
6094 mycid = GetCurrentCommandId(true);
6095 bistate = GetBulkInsertState();
6096 ti_options = TABLE_INSERT_SKIP_FSM;
6098 else
6100 /* keep compiler quiet about using these uninitialized */
6101 mycid = 0;
6102 bistate = NULL;
6103 ti_options = 0;
6107 * Generate the constraint and default execution states
6110 estate = CreateExecutorState();
6112 /* Build the needed expression execution states */
6113 foreach(l, tab->constraints)
6115 NewConstraint *con = lfirst(l);
6117 switch (con->contype)
6119 case CONSTR_CHECK:
6120 needscan = true;
6121 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6122 break;
6123 case CONSTR_FOREIGN:
6124 /* Nothing to do here */
6125 break;
6126 default:
6127 elog(ERROR, "unrecognized constraint type: %d",
6128 (int) con->contype);
6132 /* Build expression execution states for partition check quals */
6133 if (tab->partition_constraint)
6135 needscan = true;
6136 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6139 foreach(l, tab->newvals)
6141 NewColumnValue *ex = lfirst(l);
6143 /* expr already planned */
6144 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6147 notnull_attrs = NIL;
6148 if (newrel || tab->verify_new_notnull)
6151 * If we are rebuilding the tuples OR if we added any new but not
6152 * verified not-null constraints, check all not-null constraints. This
6153 * is a bit of overkill but it minimizes risk of bugs.
6155 for (i = 0; i < newTupDesc->natts; i++)
6157 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6159 if (attr->attnotnull && !attr->attisdropped)
6160 notnull_attrs = lappend_int(notnull_attrs, i);
6162 if (notnull_attrs)
6163 needscan = true;
6166 if (newrel || needscan)
6168 ExprContext *econtext;
6169 TupleTableSlot *oldslot;
6170 TupleTableSlot *newslot;
6171 TableScanDesc scan;
6172 MemoryContext oldCxt;
6173 List *dropped_attrs = NIL;
6174 ListCell *lc;
6175 Snapshot snapshot;
6177 if (newrel)
6178 ereport(DEBUG1,
6179 (errmsg_internal("rewriting table \"%s\"",
6180 RelationGetRelationName(oldrel))));
6181 else
6182 ereport(DEBUG1,
6183 (errmsg_internal("verifying table \"%s\"",
6184 RelationGetRelationName(oldrel))));
6186 if (newrel)
6189 * All predicate locks on the tuples or pages are about to be made
6190 * invalid, because we move tuples around. Promote them to
6191 * relation locks.
6193 TransferPredicateLocksToHeapRelation(oldrel);
6196 econtext = GetPerTupleExprContext(estate);
6199 * Create necessary tuple slots. When rewriting, two slots are needed,
6200 * otherwise one suffices. In the case where one slot suffices, we
6201 * need to use the new tuple descriptor, otherwise some constraints
6202 * can't be evaluated. Note that even when the tuple layout is the
6203 * same and no rewrite is required, the tupDescs might not be
6204 * (consider ADD COLUMN without a default).
6206 if (tab->rewrite)
6208 Assert(newrel != NULL);
6209 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6210 table_slot_callbacks(oldrel));
6211 newslot = MakeSingleTupleTableSlot(newTupDesc,
6212 table_slot_callbacks(newrel));
6215 * Set all columns in the new slot to NULL initially, to ensure
6216 * columns added as part of the rewrite are initialized to NULL.
6217 * That is necessary as tab->newvals will not contain an
6218 * expression for columns with a NULL default, e.g. when adding a
6219 * column without a default together with a column with a default
6220 * requiring an actual rewrite.
6222 ExecStoreAllNullTuple(newslot);
6224 else
6226 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6227 table_slot_callbacks(oldrel));
6228 newslot = NULL;
6232 * Any attributes that are dropped according to the new tuple
6233 * descriptor can be set to NULL. We precompute the list of dropped
6234 * attributes to avoid needing to do so in the per-tuple loop.
6236 for (i = 0; i < newTupDesc->natts; i++)
6238 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6239 dropped_attrs = lappend_int(dropped_attrs, i);
6243 * Scan through the rows, generating a new row if needed and then
6244 * checking all the constraints.
6246 snapshot = RegisterSnapshot(GetLatestSnapshot());
6247 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6250 * Switch to per-tuple memory context and reset it for each tuple
6251 * produced, so we don't leak memory.
6253 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6255 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6257 TupleTableSlot *insertslot;
6259 if (tab->rewrite > 0)
6261 /* Extract data from old tuple */
6262 slot_getallattrs(oldslot);
6263 ExecClearTuple(newslot);
6265 /* copy attributes */
6266 memcpy(newslot->tts_values, oldslot->tts_values,
6267 sizeof(Datum) * oldslot->tts_nvalid);
6268 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6269 sizeof(bool) * oldslot->tts_nvalid);
6271 /* Set dropped attributes to null in new tuple */
6272 foreach(lc, dropped_attrs)
6273 newslot->tts_isnull[lfirst_int(lc)] = true;
6276 * Constraints and GENERATED expressions might reference the
6277 * tableoid column, so fill tts_tableOid with the desired
6278 * value. (We must do this each time, because it gets
6279 * overwritten with newrel's OID during storing.)
6281 newslot->tts_tableOid = RelationGetRelid(oldrel);
6284 * Process supplied expressions to replace selected columns.
6286 * First, evaluate expressions whose inputs come from the old
6287 * tuple.
6289 econtext->ecxt_scantuple = oldslot;
6291 foreach(l, tab->newvals)
6293 NewColumnValue *ex = lfirst(l);
6295 if (ex->is_generated)
6296 continue;
6298 newslot->tts_values[ex->attnum - 1]
6299 = ExecEvalExpr(ex->exprstate,
6300 econtext,
6301 &newslot->tts_isnull[ex->attnum - 1]);
6304 ExecStoreVirtualTuple(newslot);
6307 * Now, evaluate any expressions whose inputs come from the
6308 * new tuple. We assume these columns won't reference each
6309 * other, so that there's no ordering dependency.
6311 econtext->ecxt_scantuple = newslot;
6313 foreach(l, tab->newvals)
6315 NewColumnValue *ex = lfirst(l);
6317 if (!ex->is_generated)
6318 continue;
6320 newslot->tts_values[ex->attnum - 1]
6321 = ExecEvalExpr(ex->exprstate,
6322 econtext,
6323 &newslot->tts_isnull[ex->attnum - 1]);
6326 insertslot = newslot;
6328 else
6331 * If there's no rewrite, old and new table are guaranteed to
6332 * have the same AM, so we can just use the old slot to verify
6333 * new constraints etc.
6335 insertslot = oldslot;
6338 /* Now check any constraints on the possibly-changed tuple */
6339 econtext->ecxt_scantuple = insertslot;
6341 foreach(l, notnull_attrs)
6343 int attn = lfirst_int(l);
6345 if (slot_attisnull(insertslot, attn + 1))
6347 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6349 ereport(ERROR,
6350 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6351 errmsg("column \"%s\" of relation \"%s\" contains null values",
6352 NameStr(attr->attname),
6353 RelationGetRelationName(oldrel)),
6354 errtablecol(oldrel, attn + 1)));
6358 foreach(l, tab->constraints)
6360 NewConstraint *con = lfirst(l);
6362 switch (con->contype)
6364 case CONSTR_CHECK:
6365 if (!ExecCheck(con->qualstate, econtext))
6366 ereport(ERROR,
6367 (errcode(ERRCODE_CHECK_VIOLATION),
6368 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6369 con->name,
6370 RelationGetRelationName(oldrel)),
6371 errtableconstraint(oldrel, con->name)));
6372 break;
6373 case CONSTR_NOTNULL:
6374 case CONSTR_FOREIGN:
6375 /* Nothing to do here */
6376 break;
6377 default:
6378 elog(ERROR, "unrecognized constraint type: %d",
6379 (int) con->contype);
6383 if (partqualstate && !ExecCheck(partqualstate, econtext))
6385 if (tab->validate_default)
6386 ereport(ERROR,
6387 (errcode(ERRCODE_CHECK_VIOLATION),
6388 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6389 RelationGetRelationName(oldrel)),
6390 errtable(oldrel)));
6391 else
6392 ereport(ERROR,
6393 (errcode(ERRCODE_CHECK_VIOLATION),
6394 errmsg("partition constraint of relation \"%s\" is violated by some row",
6395 RelationGetRelationName(oldrel)),
6396 errtable(oldrel)));
6399 /* Write the tuple out to the new relation */
6400 if (newrel)
6401 table_tuple_insert(newrel, insertslot, mycid,
6402 ti_options, bistate);
6404 ResetExprContext(econtext);
6406 CHECK_FOR_INTERRUPTS();
6409 MemoryContextSwitchTo(oldCxt);
6410 table_endscan(scan);
6411 UnregisterSnapshot(snapshot);
6413 ExecDropSingleTupleTableSlot(oldslot);
6414 if (newslot)
6415 ExecDropSingleTupleTableSlot(newslot);
6418 FreeExecutorState(estate);
6420 table_close(oldrel, NoLock);
6421 if (newrel)
6423 FreeBulkInsertState(bistate);
6425 table_finish_bulk_insert(newrel, ti_options);
6427 table_close(newrel, NoLock);
6432 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6434 static AlteredTableInfo *
6435 ATGetQueueEntry(List **wqueue, Relation rel)
6437 Oid relid = RelationGetRelid(rel);
6438 AlteredTableInfo *tab;
6439 ListCell *ltab;
6441 foreach(ltab, *wqueue)
6443 tab = (AlteredTableInfo *) lfirst(ltab);
6444 if (tab->relid == relid)
6445 return tab;
6449 * Not there, so add it. Note that we make a copy of the relation's
6450 * existing descriptor before anything interesting can happen to it.
6452 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6453 tab->relid = relid;
6454 tab->rel = NULL; /* set later */
6455 tab->relkind = rel->rd_rel->relkind;
6456 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6457 tab->newAccessMethod = InvalidOid;
6458 tab->chgAccessMethod = false;
6459 tab->newTableSpace = InvalidOid;
6460 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6461 tab->chgPersistence = false;
6463 *wqueue = lappend(*wqueue, tab);
6465 return tab;
6468 static const char *
6469 alter_table_type_to_string(AlterTableType cmdtype)
6471 switch (cmdtype)
6473 case AT_AddColumn:
6474 case AT_AddColumnToView:
6475 return "ADD COLUMN";
6476 case AT_ColumnDefault:
6477 case AT_CookedColumnDefault:
6478 return "ALTER COLUMN ... SET DEFAULT";
6479 case AT_DropNotNull:
6480 return "ALTER COLUMN ... DROP NOT NULL";
6481 case AT_SetNotNull:
6482 return "ALTER COLUMN ... SET NOT NULL";
6483 case AT_SetExpression:
6484 return "ALTER COLUMN ... SET EXPRESSION";
6485 case AT_DropExpression:
6486 return "ALTER COLUMN ... DROP EXPRESSION";
6487 case AT_SetStatistics:
6488 return "ALTER COLUMN ... SET STATISTICS";
6489 case AT_SetOptions:
6490 return "ALTER COLUMN ... SET";
6491 case AT_ResetOptions:
6492 return "ALTER COLUMN ... RESET";
6493 case AT_SetStorage:
6494 return "ALTER COLUMN ... SET STORAGE";
6495 case AT_SetCompression:
6496 return "ALTER COLUMN ... SET COMPRESSION";
6497 case AT_DropColumn:
6498 return "DROP COLUMN";
6499 case AT_AddIndex:
6500 case AT_ReAddIndex:
6501 return NULL; /* not real grammar */
6502 case AT_AddConstraint:
6503 case AT_ReAddConstraint:
6504 case AT_ReAddDomainConstraint:
6505 case AT_AddIndexConstraint:
6506 return "ADD CONSTRAINT";
6507 case AT_AlterConstraint:
6508 return "ALTER CONSTRAINT";
6509 case AT_ValidateConstraint:
6510 return "VALIDATE CONSTRAINT";
6511 case AT_DropConstraint:
6512 return "DROP CONSTRAINT";
6513 case AT_ReAddComment:
6514 return NULL; /* not real grammar */
6515 case AT_AlterColumnType:
6516 return "ALTER COLUMN ... SET DATA TYPE";
6517 case AT_AlterColumnGenericOptions:
6518 return "ALTER COLUMN ... OPTIONS";
6519 case AT_ChangeOwner:
6520 return "OWNER TO";
6521 case AT_ClusterOn:
6522 return "CLUSTER ON";
6523 case AT_DropCluster:
6524 return "SET WITHOUT CLUSTER";
6525 case AT_SetAccessMethod:
6526 return "SET ACCESS METHOD";
6527 case AT_SetLogged:
6528 return "SET LOGGED";
6529 case AT_SetUnLogged:
6530 return "SET UNLOGGED";
6531 case AT_DropOids:
6532 return "SET WITHOUT OIDS";
6533 case AT_SetTableSpace:
6534 return "SET TABLESPACE";
6535 case AT_SetRelOptions:
6536 return "SET";
6537 case AT_ResetRelOptions:
6538 return "RESET";
6539 case AT_ReplaceRelOptions:
6540 return NULL; /* not real grammar */
6541 case AT_EnableTrig:
6542 return "ENABLE TRIGGER";
6543 case AT_EnableAlwaysTrig:
6544 return "ENABLE ALWAYS TRIGGER";
6545 case AT_EnableReplicaTrig:
6546 return "ENABLE REPLICA TRIGGER";
6547 case AT_DisableTrig:
6548 return "DISABLE TRIGGER";
6549 case AT_EnableTrigAll:
6550 return "ENABLE TRIGGER ALL";
6551 case AT_DisableTrigAll:
6552 return "DISABLE TRIGGER ALL";
6553 case AT_EnableTrigUser:
6554 return "ENABLE TRIGGER USER";
6555 case AT_DisableTrigUser:
6556 return "DISABLE TRIGGER USER";
6557 case AT_EnableRule:
6558 return "ENABLE RULE";
6559 case AT_EnableAlwaysRule:
6560 return "ENABLE ALWAYS RULE";
6561 case AT_EnableReplicaRule:
6562 return "ENABLE REPLICA RULE";
6563 case AT_DisableRule:
6564 return "DISABLE RULE";
6565 case AT_AddInherit:
6566 return "INHERIT";
6567 case AT_DropInherit:
6568 return "NO INHERIT";
6569 case AT_AddOf:
6570 return "OF";
6571 case AT_DropOf:
6572 return "NOT OF";
6573 case AT_ReplicaIdentity:
6574 return "REPLICA IDENTITY";
6575 case AT_EnableRowSecurity:
6576 return "ENABLE ROW SECURITY";
6577 case AT_DisableRowSecurity:
6578 return "DISABLE ROW SECURITY";
6579 case AT_ForceRowSecurity:
6580 return "FORCE ROW SECURITY";
6581 case AT_NoForceRowSecurity:
6582 return "NO FORCE ROW SECURITY";
6583 case AT_GenericOptions:
6584 return "OPTIONS";
6585 case AT_AttachPartition:
6586 return "ATTACH PARTITION";
6587 case AT_DetachPartition:
6588 return "DETACH PARTITION";
6589 case AT_DetachPartitionFinalize:
6590 return "DETACH PARTITION ... FINALIZE";
6591 case AT_AddIdentity:
6592 return "ALTER COLUMN ... ADD IDENTITY";
6593 case AT_SetIdentity:
6594 return "ALTER COLUMN ... SET";
6595 case AT_DropIdentity:
6596 return "ALTER COLUMN ... DROP IDENTITY";
6597 case AT_ReAddStatistics:
6598 return NULL; /* not real grammar */
6601 return NULL;
6605 * ATSimplePermissions
6607 * - Ensure that it is a relation (or possibly a view)
6608 * - Ensure this user is the owner
6609 * - Ensure that it is not a system table
6611 static void
6612 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6614 int actual_target;
6616 switch (rel->rd_rel->relkind)
6618 case RELKIND_RELATION:
6619 actual_target = ATT_TABLE;
6620 break;
6621 case RELKIND_PARTITIONED_TABLE:
6622 actual_target = ATT_PARTITIONED_TABLE;
6623 break;
6624 case RELKIND_VIEW:
6625 actual_target = ATT_VIEW;
6626 break;
6627 case RELKIND_MATVIEW:
6628 actual_target = ATT_MATVIEW;
6629 break;
6630 case RELKIND_INDEX:
6631 actual_target = ATT_INDEX;
6632 break;
6633 case RELKIND_PARTITIONED_INDEX:
6634 actual_target = ATT_PARTITIONED_INDEX;
6635 break;
6636 case RELKIND_COMPOSITE_TYPE:
6637 actual_target = ATT_COMPOSITE_TYPE;
6638 break;
6639 case RELKIND_FOREIGN_TABLE:
6640 actual_target = ATT_FOREIGN_TABLE;
6641 break;
6642 case RELKIND_SEQUENCE:
6643 actual_target = ATT_SEQUENCE;
6644 break;
6645 default:
6646 actual_target = 0;
6647 break;
6650 /* Wrong target type? */
6651 if ((actual_target & allowed_targets) == 0)
6653 const char *action_str = alter_table_type_to_string(cmdtype);
6655 if (action_str)
6656 ereport(ERROR,
6657 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6658 /* translator: %s is a group of some SQL keywords */
6659 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6660 action_str, RelationGetRelationName(rel)),
6661 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6662 else
6663 /* internal error? */
6664 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6665 RelationGetRelationName(rel));
6668 /* Permissions checks */
6669 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6670 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6671 RelationGetRelationName(rel));
6673 if (!allowSystemTableMods && IsSystemRelation(rel))
6674 ereport(ERROR,
6675 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6676 errmsg("permission denied: \"%s\" is a system catalog",
6677 RelationGetRelationName(rel))));
6681 * ATSimpleRecursion
6683 * Simple table recursion sufficient for most ALTER TABLE operations.
6684 * All direct and indirect children are processed in an unspecified order.
6685 * Note that if a child inherits from the original table via multiple
6686 * inheritance paths, it will be visited just once.
6688 static void
6689 ATSimpleRecursion(List **wqueue, Relation rel,
6690 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6691 AlterTableUtilityContext *context)
6694 * Propagate to children, if desired and if there are (or might be) any
6695 * children.
6697 if (recurse && rel->rd_rel->relhassubclass)
6699 Oid relid = RelationGetRelid(rel);
6700 ListCell *child;
6701 List *children;
6703 children = find_all_inheritors(relid, lockmode, NULL);
6706 * find_all_inheritors does the recursive search of the inheritance
6707 * hierarchy, so all we have to do is process all of the relids in the
6708 * list that it returns.
6710 foreach(child, children)
6712 Oid childrelid = lfirst_oid(child);
6713 Relation childrel;
6715 if (childrelid == relid)
6716 continue;
6717 /* find_all_inheritors already got lock */
6718 childrel = relation_open(childrelid, NoLock);
6719 CheckAlterTableIsSafe(childrel);
6720 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6721 relation_close(childrel, NoLock);
6727 * Obtain list of partitions of the given table, locking them all at the given
6728 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6730 * This function is a no-op if the given relation is not a partitioned table;
6731 * in particular, nothing is done if it's a legacy inheritance parent.
6733 static void
6734 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6736 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6738 List *inh;
6739 ListCell *cell;
6741 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6742 /* first element is the parent rel; must ignore it */
6743 for_each_from(cell, inh, 1)
6745 Relation childrel;
6747 /* find_all_inheritors already got lock */
6748 childrel = table_open(lfirst_oid(cell), NoLock);
6749 CheckAlterTableIsSafe(childrel);
6750 table_close(childrel, NoLock);
6752 list_free(inh);
6757 * ATTypedTableRecursion
6759 * Propagate ALTER TYPE operations to the typed tables of that type.
6760 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6761 * recursion to inheritance children of the typed tables.
6763 static void
6764 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6765 LOCKMODE lockmode, AlterTableUtilityContext *context)
6767 ListCell *child;
6768 List *children;
6770 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6772 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6773 RelationGetRelationName(rel),
6774 cmd->behavior);
6776 foreach(child, children)
6778 Oid childrelid = lfirst_oid(child);
6779 Relation childrel;
6781 childrel = relation_open(childrelid, lockmode);
6782 CheckAlterTableIsSafe(childrel);
6783 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6784 relation_close(childrel, NoLock);
6790 * find_composite_type_dependencies
6792 * Check to see if the type "typeOid" is being used as a column in some table
6793 * (possibly nested several levels deep in composite types, arrays, etc!).
6794 * Eventually, we'd like to propagate the check or rewrite operation
6795 * into such tables, but for now, just error out if we find any.
6797 * Caller should provide either the associated relation of a rowtype,
6798 * or a type name (not both) for use in the error message, if any.
6800 * Note that "typeOid" is not necessarily a composite type; it could also be
6801 * another container type such as an array or range, or a domain over one of
6802 * these things. The name of this function is therefore somewhat historical,
6803 * but it's not worth changing.
6805 * We assume that functions and views depending on the type are not reasons
6806 * to reject the ALTER. (How safe is this really?)
6808 void
6809 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6810 const char *origTypeName)
6812 Relation depRel;
6813 ScanKeyData key[2];
6814 SysScanDesc depScan;
6815 HeapTuple depTup;
6817 /* since this function recurses, it could be driven to stack overflow */
6818 check_stack_depth();
6821 * We scan pg_depend to find those things that depend on the given type.
6822 * (We assume we can ignore refobjsubid for a type.)
6824 depRel = table_open(DependRelationId, AccessShareLock);
6826 ScanKeyInit(&key[0],
6827 Anum_pg_depend_refclassid,
6828 BTEqualStrategyNumber, F_OIDEQ,
6829 ObjectIdGetDatum(TypeRelationId));
6830 ScanKeyInit(&key[1],
6831 Anum_pg_depend_refobjid,
6832 BTEqualStrategyNumber, F_OIDEQ,
6833 ObjectIdGetDatum(typeOid));
6835 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6836 NULL, 2, key);
6838 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6840 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6841 Relation rel;
6842 TupleDesc tupleDesc;
6843 Form_pg_attribute att;
6845 /* Check for directly dependent types */
6846 if (pg_depend->classid == TypeRelationId)
6849 * This must be an array, domain, or range containing the given
6850 * type, so recursively check for uses of this type. Note that
6851 * any error message will mention the original type not the
6852 * container; this is intentional.
6854 find_composite_type_dependencies(pg_depend->objid,
6855 origRelation, origTypeName);
6856 continue;
6859 /* Else, ignore dependees that aren't relations */
6860 if (pg_depend->classid != RelationRelationId)
6861 continue;
6863 rel = relation_open(pg_depend->objid, AccessShareLock);
6864 tupleDesc = RelationGetDescr(rel);
6867 * If objsubid identifies a specific column, refer to that in error
6868 * messages. Otherwise, search to see if there's a user column of the
6869 * type. (We assume system columns are never of interesting types.)
6870 * The search is needed because an index containing an expression
6871 * column of the target type will just be recorded as a whole-relation
6872 * dependency. If we do not find a column of the type, the dependency
6873 * must indicate that the type is transiently referenced in an index
6874 * expression but not stored on disk, which we assume is OK, just as
6875 * we do for references in views. (It could also be that the target
6876 * type is embedded in some container type that is stored in an index
6877 * column, but the previous recursion should catch such cases.)
6879 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6880 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6881 else
6883 att = NULL;
6884 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6886 att = TupleDescAttr(tupleDesc, attno - 1);
6887 if (att->atttypid == typeOid && !att->attisdropped)
6888 break;
6889 att = NULL;
6891 if (att == NULL)
6893 /* No such column, so assume OK */
6894 relation_close(rel, AccessShareLock);
6895 continue;
6900 * We definitely should reject if the relation has storage. If it's
6901 * partitioned, then perhaps we don't have to reject: if there are
6902 * partitions then we'll fail when we find one, else there is no
6903 * stored data to worry about. However, it's possible that the type
6904 * change would affect conclusions about whether the type is sortable
6905 * or hashable and thus (if it's a partitioning column) break the
6906 * partitioning rule. For now, reject for partitioned rels too.
6908 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6909 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6911 if (origTypeName)
6912 ereport(ERROR,
6913 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6914 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6915 origTypeName,
6916 RelationGetRelationName(rel),
6917 NameStr(att->attname))));
6918 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6919 ereport(ERROR,
6920 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6921 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6922 RelationGetRelationName(origRelation),
6923 RelationGetRelationName(rel),
6924 NameStr(att->attname))));
6925 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6926 ereport(ERROR,
6927 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6928 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6929 RelationGetRelationName(origRelation),
6930 RelationGetRelationName(rel),
6931 NameStr(att->attname))));
6932 else
6933 ereport(ERROR,
6934 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6935 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6936 RelationGetRelationName(origRelation),
6937 RelationGetRelationName(rel),
6938 NameStr(att->attname))));
6940 else if (OidIsValid(rel->rd_rel->reltype))
6943 * A view or composite type itself isn't a problem, but we must
6944 * recursively check for indirect dependencies via its rowtype.
6946 find_composite_type_dependencies(rel->rd_rel->reltype,
6947 origRelation, origTypeName);
6950 relation_close(rel, AccessShareLock);
6953 systable_endscan(depScan);
6955 relation_close(depRel, AccessShareLock);
6960 * find_typed_table_dependencies
6962 * Check to see if a composite type is being used as the type of a
6963 * typed table. Abort if any are found and behavior is RESTRICT.
6964 * Else return the list of tables.
6966 static List *
6967 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6969 Relation classRel;
6970 ScanKeyData key[1];
6971 TableScanDesc scan;
6972 HeapTuple tuple;
6973 List *result = NIL;
6975 classRel = table_open(RelationRelationId, AccessShareLock);
6977 ScanKeyInit(&key[0],
6978 Anum_pg_class_reloftype,
6979 BTEqualStrategyNumber, F_OIDEQ,
6980 ObjectIdGetDatum(typeOid));
6982 scan = table_beginscan_catalog(classRel, 1, key);
6984 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6986 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6988 if (behavior == DROP_RESTRICT)
6989 ereport(ERROR,
6990 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6991 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6992 typeName),
6993 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6994 else
6995 result = lappend_oid(result, classform->oid);
6998 table_endscan(scan);
6999 table_close(classRel, AccessShareLock);
7001 return result;
7006 * check_of_type
7008 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7009 * isn't suitable, throw an error. Currently, we require that the type
7010 * originated with CREATE TYPE AS. We could support any row type, but doing so
7011 * would require handling a number of extra corner cases in the DDL commands.
7012 * (Also, allowing domain-over-composite would open up a can of worms about
7013 * whether and how the domain's constraints should apply to derived tables.)
7015 void
7016 check_of_type(HeapTuple typetuple)
7018 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7019 bool typeOk = false;
7021 if (typ->typtype == TYPTYPE_COMPOSITE)
7023 Relation typeRelation;
7025 Assert(OidIsValid(typ->typrelid));
7026 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7027 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7030 * Close the parent rel, but keep our AccessShareLock on it until xact
7031 * commit. That will prevent someone else from deleting or ALTERing
7032 * the type before the typed table creation/conversion commits.
7034 relation_close(typeRelation, NoLock);
7036 if (!typeOk)
7037 ereport(ERROR,
7038 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7039 errmsg("type %s is the row type of another table",
7040 format_type_be(typ->oid)),
7041 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7043 else
7044 ereport(ERROR,
7045 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7046 errmsg("type %s is not a composite type",
7047 format_type_be(typ->oid))));
7052 * ALTER TABLE ADD COLUMN
7054 * Adds an additional attribute to a relation making the assumption that
7055 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7056 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7057 * AlterTableCmd's.
7059 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7060 * have to decide at runtime whether to recurse or not depending on whether we
7061 * actually add a column or merely merge with an existing column. (We can't
7062 * check this in a static pre-pass because it won't handle multiple inheritance
7063 * situations correctly.)
7065 static void
7066 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7067 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7068 AlterTableUtilityContext *context)
7070 if (rel->rd_rel->reloftype && !recursing)
7071 ereport(ERROR,
7072 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7073 errmsg("cannot add column to typed table")));
7075 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7076 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7078 if (recurse && !is_view)
7079 cmd->recurse = true;
7083 * Add a column to a table. The return value is the address of the
7084 * new column in the parent relation.
7086 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7087 * copy (but that happens only after we check for IF NOT EXISTS).
7089 static ObjectAddress
7090 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7091 AlterTableCmd **cmd, bool recurse, bool recursing,
7092 LOCKMODE lockmode, AlterTablePass cur_pass,
7093 AlterTableUtilityContext *context)
7095 Oid myrelid = RelationGetRelid(rel);
7096 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7097 bool if_not_exists = (*cmd)->missing_ok;
7098 Relation pgclass,
7099 attrdesc;
7100 HeapTuple reltup;
7101 Form_pg_class relform;
7102 Form_pg_attribute attribute;
7103 int newattnum;
7104 char relkind;
7105 Expr *defval;
7106 List *children;
7107 ListCell *child;
7108 AlterTableCmd *childcmd;
7109 ObjectAddress address;
7110 TupleDesc tupdesc;
7112 /* since this function recurses, it could be driven to stack overflow */
7113 check_stack_depth();
7115 /* At top level, permission check was done in ATPrepCmd, else do it */
7116 if (recursing)
7117 ATSimplePermissions((*cmd)->subtype, rel,
7118 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
7120 if (rel->rd_rel->relispartition && !recursing)
7121 ereport(ERROR,
7122 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7123 errmsg("cannot add column to a partition")));
7125 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7128 * Are we adding the column to a recursion child? If so, check whether to
7129 * merge with an existing definition for the column. If we do merge, we
7130 * must not recurse. Children will already have the column, and recursing
7131 * into them would mess up attinhcount.
7133 if (colDef->inhcount > 0)
7135 HeapTuple tuple;
7137 /* Does child already have a column by this name? */
7138 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7139 if (HeapTupleIsValid(tuple))
7141 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7142 Oid ctypeId;
7143 int32 ctypmod;
7144 Oid ccollid;
7146 /* Child column must match on type, typmod, and collation */
7147 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7148 if (ctypeId != childatt->atttypid ||
7149 ctypmod != childatt->atttypmod)
7150 ereport(ERROR,
7151 (errcode(ERRCODE_DATATYPE_MISMATCH),
7152 errmsg("child table \"%s\" has different type for column \"%s\"",
7153 RelationGetRelationName(rel), colDef->colname)));
7154 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7155 if (ccollid != childatt->attcollation)
7156 ereport(ERROR,
7157 (errcode(ERRCODE_COLLATION_MISMATCH),
7158 errmsg("child table \"%s\" has different collation for column \"%s\"",
7159 RelationGetRelationName(rel), colDef->colname),
7160 errdetail("\"%s\" versus \"%s\"",
7161 get_collation_name(ccollid),
7162 get_collation_name(childatt->attcollation))));
7164 /* Bump the existing child att's inhcount */
7165 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7166 &childatt->attinhcount))
7167 ereport(ERROR,
7168 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7169 errmsg("too many inheritance parents"));
7170 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7172 heap_freetuple(tuple);
7174 /* Inform the user about the merge */
7175 ereport(NOTICE,
7176 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7177 colDef->colname, RelationGetRelationName(rel))));
7179 table_close(attrdesc, RowExclusiveLock);
7181 /* Make the child column change visible */
7182 CommandCounterIncrement();
7184 return InvalidObjectAddress;
7188 /* skip if the name already exists and if_not_exists is true */
7189 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7191 table_close(attrdesc, RowExclusiveLock);
7192 return InvalidObjectAddress;
7196 * Okay, we need to add the column, so go ahead and do parse
7197 * transformation. This can result in queueing up, or even immediately
7198 * executing, subsidiary operations (such as creation of unique indexes);
7199 * so we mustn't do it until we have made the if_not_exists check.
7201 * When recursing, the command was already transformed and we needn't do
7202 * so again. Also, if context isn't given we can't transform. (That
7203 * currently happens only for AT_AddColumnToView; we expect that view.c
7204 * passed us a ColumnDef that doesn't need work.)
7206 if (context != NULL && !recursing)
7208 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7209 cur_pass, context);
7210 Assert(*cmd != NULL);
7211 colDef = castNode(ColumnDef, (*cmd)->def);
7215 * Regular inheritance children are independent enough not to inherit the
7216 * identity column from parent hence cannot recursively add identity
7217 * column if the table has inheritance children.
7219 * Partitions, on the other hand, are integral part of a partitioned table
7220 * and inherit identity column. Hence propagate identity column down the
7221 * partition hierarchy.
7223 if (colDef->identity &&
7224 recurse &&
7225 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7226 find_inheritance_children(myrelid, NoLock) != NIL)
7227 ereport(ERROR,
7228 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7229 errmsg("cannot recursively add identity column to table that has child tables")));
7231 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7233 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7234 if (!HeapTupleIsValid(reltup))
7235 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7236 relform = (Form_pg_class) GETSTRUCT(reltup);
7237 relkind = relform->relkind;
7239 /* Determine the new attribute's number */
7240 newattnum = relform->relnatts + 1;
7241 if (newattnum > MaxHeapAttributeNumber)
7242 ereport(ERROR,
7243 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7244 errmsg("tables can have at most %d columns",
7245 MaxHeapAttributeNumber)));
7248 * Construct new attribute's pg_attribute entry.
7250 tupdesc = BuildDescForRelation(list_make1(colDef));
7252 attribute = TupleDescAttr(tupdesc, 0);
7254 /* Fix up attribute number */
7255 attribute->attnum = newattnum;
7257 /* make sure datatype is legal for a column */
7258 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7259 list_make1_oid(rel->rd_rel->reltype),
7262 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7264 table_close(attrdesc, RowExclusiveLock);
7267 * Update pg_class tuple as appropriate
7269 relform->relnatts = newattnum;
7271 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7273 heap_freetuple(reltup);
7275 /* Post creation hook for new attribute */
7276 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7278 table_close(pgclass, RowExclusiveLock);
7280 /* Make the attribute's catalog entry visible */
7281 CommandCounterIncrement();
7284 * Store the DEFAULT, if any, in the catalogs
7286 if (colDef->raw_default)
7288 RawColumnDefault *rawEnt;
7290 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7291 rawEnt->attnum = attribute->attnum;
7292 rawEnt->raw_default = copyObject(colDef->raw_default);
7295 * Attempt to skip a complete table rewrite by storing the specified
7296 * DEFAULT value outside of the heap. This may be disabled inside
7297 * AddRelationNewConstraints if the optimization cannot be applied.
7299 rawEnt->missingMode = (!colDef->generated);
7301 rawEnt->generated = colDef->generated;
7304 * This function is intended for CREATE TABLE, so it processes a
7305 * _list_ of defaults, but we just do one.
7307 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7308 false, true, false, NULL);
7310 /* Make the additional catalog changes visible */
7311 CommandCounterIncrement();
7314 * Did the request for a missing value work? If not we'll have to do a
7315 * rewrite
7317 if (!rawEnt->missingMode)
7318 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7322 * Tell Phase 3 to fill in the default expression, if there is one.
7324 * If there is no default, Phase 3 doesn't have to do anything, because
7325 * that effectively means that the default is NULL. The heap tuple access
7326 * routines always check for attnum > # of attributes in tuple, and return
7327 * NULL if so, so without any modification of the tuple data we will get
7328 * the effect of NULL values in the new column.
7330 * An exception occurs when the new column is of a domain type: the domain
7331 * might have a not-null constraint, or a check constraint that indirectly
7332 * rejects nulls. If there are any domain constraints then we construct
7333 * an explicit NULL default value that will be passed through
7334 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7335 * rewriting the table which we really don't have to do, but the present
7336 * design of domain processing doesn't offer any simple way of checking
7337 * the constraints more directly.)
7339 * Note: we use build_column_default, and not just the cooked default
7340 * returned by AddRelationNewConstraints, so that the right thing happens
7341 * when a datatype's default applies.
7343 * Note: it might seem that this should happen at the end of Phase 2, so
7344 * that the effects of subsequent subcommands can be taken into account.
7345 * It's intentional that we do it now, though. The new column should be
7346 * filled according to what is said in the ADD COLUMN subcommand, so that
7347 * the effects are the same as if this subcommand had been run by itself
7348 * and the later subcommands had been issued in new ALTER TABLE commands.
7350 * We can skip this entirely for relations without storage, since Phase 3
7351 * is certainly not going to touch them. System attributes don't have
7352 * interesting defaults, either.
7354 if (RELKIND_HAS_STORAGE(relkind))
7357 * For an identity column, we can't use build_column_default(),
7358 * because the sequence ownership isn't set yet. So do it manually.
7360 if (colDef->identity)
7362 NextValueExpr *nve = makeNode(NextValueExpr);
7364 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7365 nve->typeId = attribute->atttypid;
7367 defval = (Expr *) nve;
7369 /* must do a rewrite for identity columns */
7370 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7372 else
7373 defval = (Expr *) build_column_default(rel, attribute->attnum);
7375 if (!defval && DomainHasConstraints(attribute->atttypid))
7377 Oid baseTypeId;
7378 int32 baseTypeMod;
7379 Oid baseTypeColl;
7381 baseTypeMod = attribute->atttypmod;
7382 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7383 baseTypeColl = get_typcollation(baseTypeId);
7384 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7385 defval = (Expr *) coerce_to_target_type(NULL,
7386 (Node *) defval,
7387 baseTypeId,
7388 attribute->atttypid,
7389 attribute->atttypmod,
7390 COERCION_ASSIGNMENT,
7391 COERCE_IMPLICIT_CAST,
7392 -1);
7393 if (defval == NULL) /* should not happen */
7394 elog(ERROR, "failed to coerce base type to domain");
7397 if (defval)
7399 NewColumnValue *newval;
7401 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7402 newval->attnum = attribute->attnum;
7403 newval->expr = expression_planner(defval);
7404 newval->is_generated = (colDef->generated != '\0');
7406 tab->newvals = lappend(tab->newvals, newval);
7409 if (DomainHasConstraints(attribute->atttypid))
7410 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7412 if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7415 * If the new column is NOT NULL, and there is no missing value,
7416 * tell Phase 3 it needs to check for NULLs.
7418 tab->verify_new_notnull |= colDef->is_not_null;
7423 * Add needed dependency entries for the new column.
7425 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7426 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7429 * Propagate to children as appropriate. Unlike most other ALTER
7430 * routines, we have to do this one level of recursion at a time; we can't
7431 * use find_all_inheritors to do it in one pass.
7433 children =
7434 find_inheritance_children(RelationGetRelid(rel), lockmode);
7437 * If we are told not to recurse, there had better not be any child
7438 * tables; else the addition would put them out of step.
7440 if (children && !recurse)
7441 ereport(ERROR,
7442 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7443 errmsg("column must be added to child tables too")));
7445 /* Children should see column as singly inherited */
7446 if (!recursing)
7448 childcmd = copyObject(*cmd);
7449 colDef = castNode(ColumnDef, childcmd->def);
7450 colDef->inhcount = 1;
7451 colDef->is_local = false;
7453 else
7454 childcmd = *cmd; /* no need to copy again */
7456 foreach(child, children)
7458 Oid childrelid = lfirst_oid(child);
7459 Relation childrel;
7460 AlteredTableInfo *childtab;
7462 /* find_inheritance_children already got lock */
7463 childrel = table_open(childrelid, NoLock);
7464 CheckAlterTableIsSafe(childrel);
7466 /* Find or create work queue entry for this table */
7467 childtab = ATGetQueueEntry(wqueue, childrel);
7469 /* Recurse to child; return value is ignored */
7470 ATExecAddColumn(wqueue, childtab, childrel,
7471 &childcmd, recurse, true,
7472 lockmode, cur_pass, context);
7474 table_close(childrel, NoLock);
7477 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7478 return address;
7482 * If a new or renamed column will collide with the name of an existing
7483 * column and if_not_exists is false then error out, else do nothing.
7485 static bool
7486 check_for_column_name_collision(Relation rel, const char *colname,
7487 bool if_not_exists)
7489 HeapTuple attTuple;
7490 int attnum;
7493 * this test is deliberately not attisdropped-aware, since if one tries to
7494 * add a column matching a dropped column name, it's gonna fail anyway.
7496 attTuple = SearchSysCache2(ATTNAME,
7497 ObjectIdGetDatum(RelationGetRelid(rel)),
7498 PointerGetDatum(colname));
7499 if (!HeapTupleIsValid(attTuple))
7500 return true;
7502 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7503 ReleaseSysCache(attTuple);
7506 * We throw a different error message for conflicts with system column
7507 * names, since they are normally not shown and the user might otherwise
7508 * be confused about the reason for the conflict.
7510 if (attnum <= 0)
7511 ereport(ERROR,
7512 (errcode(ERRCODE_DUPLICATE_COLUMN),
7513 errmsg("column name \"%s\" conflicts with a system column name",
7514 colname)));
7515 else
7517 if (if_not_exists)
7519 ereport(NOTICE,
7520 (errcode(ERRCODE_DUPLICATE_COLUMN),
7521 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7522 colname, RelationGetRelationName(rel))));
7523 return false;
7526 ereport(ERROR,
7527 (errcode(ERRCODE_DUPLICATE_COLUMN),
7528 errmsg("column \"%s\" of relation \"%s\" already exists",
7529 colname, RelationGetRelationName(rel))));
7532 return true;
7536 * Install a column's dependency on its datatype.
7538 static void
7539 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7541 ObjectAddress myself,
7542 referenced;
7544 myself.classId = RelationRelationId;
7545 myself.objectId = relid;
7546 myself.objectSubId = attnum;
7547 referenced.classId = TypeRelationId;
7548 referenced.objectId = typid;
7549 referenced.objectSubId = 0;
7550 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7554 * Install a column's dependency on its collation.
7556 static void
7557 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7559 ObjectAddress myself,
7560 referenced;
7562 /* We know the default collation is pinned, so don't bother recording it */
7563 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7565 myself.classId = RelationRelationId;
7566 myself.objectId = relid;
7567 myself.objectSubId = attnum;
7568 referenced.classId = CollationRelationId;
7569 referenced.objectId = collid;
7570 referenced.objectSubId = 0;
7571 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7576 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7578 * Return the address of the modified column. If the column was already
7579 * nullable, InvalidObjectAddress is returned.
7581 static ObjectAddress
7582 ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7583 LOCKMODE lockmode)
7585 HeapTuple tuple;
7586 HeapTuple conTup;
7587 Form_pg_attribute attTup;
7588 AttrNumber attnum;
7589 Relation attr_rel;
7590 ObjectAddress address;
7593 * lookup the attribute
7595 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7597 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7598 if (!HeapTupleIsValid(tuple))
7599 ereport(ERROR,
7600 (errcode(ERRCODE_UNDEFINED_COLUMN),
7601 errmsg("column \"%s\" of relation \"%s\" does not exist",
7602 colName, RelationGetRelationName(rel))));
7603 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7604 attnum = attTup->attnum;
7605 ObjectAddressSubSet(address, RelationRelationId,
7606 RelationGetRelid(rel), attnum);
7608 /* If the column is already nullable there's nothing to do. */
7609 if (!attTup->attnotnull)
7611 table_close(attr_rel, RowExclusiveLock);
7612 return InvalidObjectAddress;
7615 /* Prevent them from altering a system attribute */
7616 if (attnum <= 0)
7617 ereport(ERROR,
7618 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7619 errmsg("cannot alter system column \"%s\"",
7620 colName)));
7622 if (attTup->attidentity)
7623 ereport(ERROR,
7624 (errcode(ERRCODE_SYNTAX_ERROR),
7625 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7626 colName, RelationGetRelationName(rel))));
7629 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7631 if (rel->rd_rel->relispartition)
7633 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7634 Relation parent = table_open(parentId, AccessShareLock);
7635 TupleDesc tupDesc = RelationGetDescr(parent);
7636 AttrNumber parent_attnum;
7638 parent_attnum = get_attnum(parentId, colName);
7639 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7640 ereport(ERROR,
7641 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7642 errmsg("column \"%s\" is marked NOT NULL in parent table",
7643 colName)));
7644 table_close(parent, AccessShareLock);
7648 * Find the constraint that makes this column NOT NULL, and drop it.
7649 * dropconstraint_internal() resets attnotnull.
7651 conTup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7652 if (conTup == NULL)
7653 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7654 colName, RelationGetRelationName(rel));
7656 /* The normal case: we have a pg_constraint row, remove it */
7657 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7658 false, lockmode);
7659 heap_freetuple(conTup);
7661 InvokeObjectPostAlterHook(RelationRelationId,
7662 RelationGetRelid(rel), attnum);
7664 table_close(attr_rel, RowExclusiveLock);
7666 return address;
7670 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7671 * to verify it.
7673 * When called to alter an existing table, 'wqueue' must be given so that we
7674 * can queue a check that existing tuples pass the constraint. When called
7675 * from table creation, 'wqueue' should be passed as NULL.
7677 static void
7678 set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum,
7679 LOCKMODE lockmode)
7681 Form_pg_attribute attr;
7683 CheckAlterTableIsSafe(rel);
7686 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7687 * attribute.
7689 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7690 if (attr->attisdropped)
7691 return;
7693 if (!attr->attnotnull)
7695 Relation attr_rel;
7696 HeapTuple tuple;
7698 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7700 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7701 if (!HeapTupleIsValid(tuple))
7702 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7703 attnum, RelationGetRelid(rel));
7705 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7706 Assert(!attr->attnotnull);
7707 attr->attnotnull = true;
7708 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7711 * If the nullness isn't already proven by validated constraints, have
7712 * ALTER TABLE phase 3 test for it.
7714 if (wqueue && !NotNullImpliedByRelConstraints(rel, attr))
7716 AlteredTableInfo *tab;
7718 tab = ATGetQueueEntry(wqueue, rel);
7719 tab->verify_new_notnull = true;
7722 CommandCounterIncrement();
7724 table_close(attr_rel, RowExclusiveLock);
7725 heap_freetuple(tuple);
7730 * ALTER TABLE ALTER COLUMN SET NOT NULL
7732 * Add a not-null constraint to a single table and its children. Returns
7733 * the address of the constraint added to the parent relation, if one gets
7734 * added, or InvalidObjectAddress otherwise.
7736 * We must recurse to child tables during execution, rather than using
7737 * ALTER TABLE's normal prep-time recursion.
7739 static ObjectAddress
7740 ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7741 bool recurse, bool recursing, LOCKMODE lockmode)
7743 HeapTuple tuple;
7744 AttrNumber attnum;
7745 ObjectAddress address;
7746 Constraint *constraint;
7747 CookedConstraint *ccon;
7748 List *cooked;
7749 bool is_no_inherit = false;
7751 /* Guard against stack overflow due to overly deep inheritance tree. */
7752 check_stack_depth();
7754 /* At top level, permission check was done in ATPrepCmd, else do it */
7755 if (recursing)
7757 ATSimplePermissions(AT_AddConstraint, rel,
7758 ATT_PARTITIONED_TABLE | ATT_TABLE | ATT_FOREIGN_TABLE);
7759 Assert(conName != NULL);
7762 attnum = get_attnum(RelationGetRelid(rel), colName);
7763 if (attnum == InvalidAttrNumber)
7764 ereport(ERROR,
7765 (errcode(ERRCODE_UNDEFINED_COLUMN),
7766 errmsg("column \"%s\" of relation \"%s\" does not exist",
7767 colName, RelationGetRelationName(rel))));
7769 /* Prevent them from altering a system attribute */
7770 if (attnum <= 0)
7771 ereport(ERROR,
7772 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7773 errmsg("cannot alter system column \"%s\"",
7774 colName)));
7776 /* See if there's already a constraint */
7777 tuple = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
7778 if (HeapTupleIsValid(tuple))
7780 Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7781 bool changed = false;
7784 * Don't let a NO INHERIT constraint be changed into inherit.
7786 if (conForm->connoinherit && recurse)
7787 ereport(ERROR,
7788 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7789 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7790 NameStr(conForm->conname),
7791 RelationGetRelationName(rel)));
7794 * If we find an appropriate constraint, we're almost done, but just
7795 * need to change some properties on it: if we're recursing, increment
7796 * coninhcount; if not, set conislocal if not already set.
7798 if (recursing)
7800 if (pg_add_s16_overflow(conForm->coninhcount, 1,
7801 &conForm->coninhcount))
7802 ereport(ERROR,
7803 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7804 errmsg("too many inheritance parents"));
7805 changed = true;
7807 else if (!conForm->conislocal)
7809 conForm->conislocal = true;
7810 changed = true;
7813 if (changed)
7815 Relation constr_rel;
7817 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7819 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
7820 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7821 table_close(constr_rel, RowExclusiveLock);
7824 if (changed)
7825 return address;
7826 else
7827 return InvalidObjectAddress;
7831 * If we're asked not to recurse, and children exist, raise an error for
7832 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7833 * specified.
7835 if (!recurse &&
7836 find_inheritance_children(RelationGetRelid(rel),
7837 NoLock) != NIL)
7839 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7840 ereport(ERROR,
7841 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7842 errmsg("constraint must be added to child tables too"),
7843 errhint("Do not specify the ONLY keyword."));
7844 else
7845 is_no_inherit = true;
7849 * No constraint exists; we must add one. First determine a name to use,
7850 * if we haven't already.
7852 if (!recursing)
7854 Assert(conName == NULL);
7855 conName = ChooseConstraintName(RelationGetRelationName(rel),
7856 colName, "not_null",
7857 RelationGetNamespace(rel),
7858 NIL);
7861 constraint = makeNotNullConstraint(makeString(colName));
7862 constraint->is_no_inherit = is_no_inherit;
7863 constraint->conname = conName;
7865 /* and do it */
7866 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7867 false, !recursing, false, NULL);
7868 ccon = linitial(cooked);
7869 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7871 InvokeObjectPostAlterHook(RelationRelationId,
7872 RelationGetRelid(rel), attnum);
7874 /* Mark pg_attribute.attnotnull for the column */
7875 set_attnotnull(wqueue, rel, attnum, lockmode);
7878 * Recurse to propagate the constraint to children that don't have one.
7880 if (recurse)
7882 List *children;
7884 children = find_inheritance_children(RelationGetRelid(rel),
7885 lockmode);
7887 foreach_oid(childoid, children)
7889 Relation childrel = table_open(childoid, NoLock);
7891 CommandCounterIncrement();
7893 ATExecSetNotNull(wqueue, childrel, conName, colName,
7894 recurse, true, lockmode);
7895 table_close(childrel, NoLock);
7899 return address;
7903 * NotNullImpliedByRelConstraints
7904 * Does rel's existing constraints imply NOT NULL for the given attribute?
7906 static bool
7907 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7909 NullTest *nnulltest = makeNode(NullTest);
7911 nnulltest->arg = (Expr *) makeVar(1,
7912 attr->attnum,
7913 attr->atttypid,
7914 attr->atttypmod,
7915 attr->attcollation,
7917 nnulltest->nulltesttype = IS_NOT_NULL;
7920 * argisrow = false is correct even for a composite column, because
7921 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7922 * case, just IS DISTINCT FROM NULL.
7924 nnulltest->argisrow = false;
7925 nnulltest->location = -1;
7927 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7929 ereport(DEBUG1,
7930 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7931 RelationGetRelationName(rel), NameStr(attr->attname))));
7932 return true;
7935 return false;
7939 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7941 * Return the address of the affected column.
7943 static ObjectAddress
7944 ATExecColumnDefault(Relation rel, const char *colName,
7945 Node *newDefault, LOCKMODE lockmode)
7947 TupleDesc tupdesc = RelationGetDescr(rel);
7948 AttrNumber attnum;
7949 ObjectAddress address;
7952 * get the number of the attribute
7954 attnum = get_attnum(RelationGetRelid(rel), colName);
7955 if (attnum == InvalidAttrNumber)
7956 ereport(ERROR,
7957 (errcode(ERRCODE_UNDEFINED_COLUMN),
7958 errmsg("column \"%s\" of relation \"%s\" does not exist",
7959 colName, RelationGetRelationName(rel))));
7961 /* Prevent them from altering a system attribute */
7962 if (attnum <= 0)
7963 ereport(ERROR,
7964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7965 errmsg("cannot alter system column \"%s\"",
7966 colName)));
7968 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7969 ereport(ERROR,
7970 (errcode(ERRCODE_SYNTAX_ERROR),
7971 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7972 colName, RelationGetRelationName(rel)),
7973 /* translator: %s is an SQL ALTER command */
7974 newDefault ? 0 : errhint("Use %s instead.",
7975 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7977 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7978 ereport(ERROR,
7979 (errcode(ERRCODE_SYNTAX_ERROR),
7980 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7981 colName, RelationGetRelationName(rel)),
7982 newDefault ?
7983 /* translator: %s is an SQL ALTER command */
7984 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7985 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7986 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7989 * Remove any old default for the column. We use RESTRICT here for
7990 * safety, but at present we do not expect anything to depend on the
7991 * default.
7993 * We treat removing the existing default as an internal operation when it
7994 * is preparatory to adding a new default, but as a user-initiated
7995 * operation when the user asked for a drop.
7997 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7998 newDefault != NULL);
8000 if (newDefault)
8002 /* SET DEFAULT */
8003 RawColumnDefault *rawEnt;
8005 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8006 rawEnt->attnum = attnum;
8007 rawEnt->raw_default = newDefault;
8008 rawEnt->missingMode = false;
8009 rawEnt->generated = '\0';
8012 * This function is intended for CREATE TABLE, so it processes a
8013 * _list_ of defaults, but we just do one.
8015 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8016 false, true, false, NULL);
8019 ObjectAddressSubSet(address, RelationRelationId,
8020 RelationGetRelid(rel), attnum);
8021 return address;
8025 * Add a pre-cooked default expression.
8027 * Return the address of the affected column.
8029 static ObjectAddress
8030 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8031 Node *newDefault)
8033 ObjectAddress address;
8035 /* We assume no checking is required */
8038 * Remove any old default for the column. We use RESTRICT here for
8039 * safety, but at present we do not expect anything to depend on the
8040 * default. (In ordinary cases, there could not be a default in place
8041 * anyway, but it's possible when combining LIKE with inheritance.)
8043 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8044 true);
8046 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8048 ObjectAddressSubSet(address, RelationRelationId,
8049 RelationGetRelid(rel), attnum);
8050 return address;
8054 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8056 * Return the address of the affected column.
8058 static ObjectAddress
8059 ATExecAddIdentity(Relation rel, const char *colName,
8060 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8062 Relation attrelation;
8063 HeapTuple tuple;
8064 Form_pg_attribute attTup;
8065 AttrNumber attnum;
8066 ObjectAddress address;
8067 ColumnDef *cdef = castNode(ColumnDef, def);
8068 bool ispartitioned;
8070 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8071 if (ispartitioned && !recurse)
8072 ereport(ERROR,
8073 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8074 errmsg("cannot add identity to a column of only the partitioned table"),
8075 errhint("Do not specify the ONLY keyword.")));
8077 if (rel->rd_rel->relispartition && !recursing)
8078 ereport(ERROR,
8079 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8080 errmsg("cannot add identity to a column of a partition"));
8082 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8084 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8085 if (!HeapTupleIsValid(tuple))
8086 ereport(ERROR,
8087 (errcode(ERRCODE_UNDEFINED_COLUMN),
8088 errmsg("column \"%s\" of relation \"%s\" does not exist",
8089 colName, RelationGetRelationName(rel))));
8090 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8091 attnum = attTup->attnum;
8093 /* Can't alter a system attribute */
8094 if (attnum <= 0)
8095 ereport(ERROR,
8096 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8097 errmsg("cannot alter system column \"%s\"",
8098 colName)));
8101 * Creating a column as identity implies NOT NULL, so adding the identity
8102 * to an existing column that is not NOT NULL would create a state that
8103 * cannot be reproduced without contortions.
8105 if (!attTup->attnotnull)
8106 ereport(ERROR,
8107 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8108 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8109 colName, RelationGetRelationName(rel))));
8111 if (attTup->attidentity)
8112 ereport(ERROR,
8113 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8114 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8115 colName, RelationGetRelationName(rel))));
8117 if (attTup->atthasdef)
8118 ereport(ERROR,
8119 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8120 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8121 colName, RelationGetRelationName(rel))));
8123 attTup->attidentity = cdef->identity;
8124 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8126 InvokeObjectPostAlterHook(RelationRelationId,
8127 RelationGetRelid(rel),
8128 attTup->attnum);
8129 ObjectAddressSubSet(address, RelationRelationId,
8130 RelationGetRelid(rel), attnum);
8131 heap_freetuple(tuple);
8133 table_close(attrelation, RowExclusiveLock);
8136 * Recurse to propagate the identity column to partitions. Identity is
8137 * not inherited in regular inheritance children.
8139 if (recurse && ispartitioned)
8141 List *children;
8142 ListCell *lc;
8144 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8146 foreach(lc, children)
8148 Relation childrel;
8150 childrel = table_open(lfirst_oid(lc), NoLock);
8151 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8152 table_close(childrel, NoLock);
8156 return address;
8160 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8162 * Return the address of the affected column.
8164 static ObjectAddress
8165 ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8166 LOCKMODE lockmode, bool recurse, bool recursing)
8168 ListCell *option;
8169 DefElem *generatedEl = NULL;
8170 HeapTuple tuple;
8171 Form_pg_attribute attTup;
8172 AttrNumber attnum;
8173 Relation attrelation;
8174 ObjectAddress address;
8175 bool ispartitioned;
8177 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8178 if (ispartitioned && !recurse)
8179 ereport(ERROR,
8180 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8181 errmsg("cannot change identity column of only the partitioned table"),
8182 errhint("Do not specify the ONLY keyword.")));
8184 if (rel->rd_rel->relispartition && !recursing)
8185 ereport(ERROR,
8186 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8187 errmsg("cannot change identity column of a partition"));
8189 foreach(option, castNode(List, def))
8191 DefElem *defel = lfirst_node(DefElem, option);
8193 if (strcmp(defel->defname, "generated") == 0)
8195 if (generatedEl)
8196 ereport(ERROR,
8197 (errcode(ERRCODE_SYNTAX_ERROR),
8198 errmsg("conflicting or redundant options")));
8199 generatedEl = defel;
8201 else
8202 elog(ERROR, "option \"%s\" not recognized",
8203 defel->defname);
8207 * Even if there is nothing to change here, we run all the checks. There
8208 * will be a subsequent ALTER SEQUENCE that relies on everything being
8209 * there.
8212 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8213 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8214 if (!HeapTupleIsValid(tuple))
8215 ereport(ERROR,
8216 (errcode(ERRCODE_UNDEFINED_COLUMN),
8217 errmsg("column \"%s\" of relation \"%s\" does not exist",
8218 colName, RelationGetRelationName(rel))));
8220 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8221 attnum = attTup->attnum;
8223 if (attnum <= 0)
8224 ereport(ERROR,
8225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8226 errmsg("cannot alter system column \"%s\"",
8227 colName)));
8229 if (!attTup->attidentity)
8230 ereport(ERROR,
8231 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8232 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8233 colName, RelationGetRelationName(rel))));
8235 if (generatedEl)
8237 attTup->attidentity = defGetInt32(generatedEl);
8238 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8240 InvokeObjectPostAlterHook(RelationRelationId,
8241 RelationGetRelid(rel),
8242 attTup->attnum);
8243 ObjectAddressSubSet(address, RelationRelationId,
8244 RelationGetRelid(rel), attnum);
8246 else
8247 address = InvalidObjectAddress;
8249 heap_freetuple(tuple);
8250 table_close(attrelation, RowExclusiveLock);
8253 * Recurse to propagate the identity change to partitions. Identity is not
8254 * inherited in regular inheritance children.
8256 if (generatedEl && recurse && ispartitioned)
8258 List *children;
8259 ListCell *lc;
8261 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8263 foreach(lc, children)
8265 Relation childrel;
8267 childrel = table_open(lfirst_oid(lc), NoLock);
8268 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8269 table_close(childrel, NoLock);
8273 return address;
8277 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8279 * Return the address of the affected column.
8281 static ObjectAddress
8282 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8283 bool recurse, bool recursing)
8285 HeapTuple tuple;
8286 Form_pg_attribute attTup;
8287 AttrNumber attnum;
8288 Relation attrelation;
8289 ObjectAddress address;
8290 Oid seqid;
8291 ObjectAddress seqaddress;
8292 bool ispartitioned;
8294 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8295 if (ispartitioned && !recurse)
8296 ereport(ERROR,
8297 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8298 errmsg("cannot drop identity from a column of only the partitioned table"),
8299 errhint("Do not specify the ONLY keyword.")));
8301 if (rel->rd_rel->relispartition && !recursing)
8302 ereport(ERROR,
8303 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8304 errmsg("cannot drop identity from a column of a partition"));
8306 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8307 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8308 if (!HeapTupleIsValid(tuple))
8309 ereport(ERROR,
8310 (errcode(ERRCODE_UNDEFINED_COLUMN),
8311 errmsg("column \"%s\" of relation \"%s\" does not exist",
8312 colName, RelationGetRelationName(rel))));
8314 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8315 attnum = attTup->attnum;
8317 if (attnum <= 0)
8318 ereport(ERROR,
8319 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8320 errmsg("cannot alter system column \"%s\"",
8321 colName)));
8323 if (!attTup->attidentity)
8325 if (!missing_ok)
8326 ereport(ERROR,
8327 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8328 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8329 colName, RelationGetRelationName(rel))));
8330 else
8332 ereport(NOTICE,
8333 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8334 colName, RelationGetRelationName(rel))));
8335 heap_freetuple(tuple);
8336 table_close(attrelation, RowExclusiveLock);
8337 return InvalidObjectAddress;
8341 attTup->attidentity = '\0';
8342 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8344 InvokeObjectPostAlterHook(RelationRelationId,
8345 RelationGetRelid(rel),
8346 attTup->attnum);
8347 ObjectAddressSubSet(address, RelationRelationId,
8348 RelationGetRelid(rel), attnum);
8349 heap_freetuple(tuple);
8351 table_close(attrelation, RowExclusiveLock);
8354 * Recurse to drop the identity from column in partitions. Identity is
8355 * not inherited in regular inheritance children so ignore them.
8357 if (recurse && ispartitioned)
8359 List *children;
8360 ListCell *lc;
8362 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8364 foreach(lc, children)
8366 Relation childrel;
8368 childrel = table_open(lfirst_oid(lc), NoLock);
8369 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8370 table_close(childrel, NoLock);
8374 if (!recursing)
8376 /* drop the internal sequence */
8377 seqid = getIdentitySequence(rel, attnum, false);
8378 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8379 RelationRelationId, DEPENDENCY_INTERNAL);
8380 CommandCounterIncrement();
8381 seqaddress.classId = RelationRelationId;
8382 seqaddress.objectId = seqid;
8383 seqaddress.objectSubId = 0;
8384 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8387 return address;
8391 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8393 * Return the address of the affected column.
8395 static ObjectAddress
8396 ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8397 Node *newExpr, LOCKMODE lockmode)
8399 HeapTuple tuple;
8400 Form_pg_attribute attTup;
8401 AttrNumber attnum;
8402 Oid attrdefoid;
8403 ObjectAddress address;
8404 Expr *defval;
8405 NewColumnValue *newval;
8406 RawColumnDefault *rawEnt;
8408 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8409 if (!HeapTupleIsValid(tuple))
8410 ereport(ERROR,
8411 (errcode(ERRCODE_UNDEFINED_COLUMN),
8412 errmsg("column \"%s\" of relation \"%s\" does not exist",
8413 colName, RelationGetRelationName(rel))));
8415 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8416 attnum = attTup->attnum;
8418 if (attnum <= 0)
8419 ereport(ERROR,
8420 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8421 errmsg("cannot alter system column \"%s\"",
8422 colName)));
8424 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8425 ereport(ERROR,
8426 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8427 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8428 colName, RelationGetRelationName(rel))));
8429 ReleaseSysCache(tuple);
8432 * Clear all the missing values if we're rewriting the table, since this
8433 * renders them pointless.
8435 RelationClearMissing(rel);
8437 /* make sure we don't conflict with later attribute modifications */
8438 CommandCounterIncrement();
8441 * Find everything that depends on the column (constraints, indexes, etc),
8442 * and record enough information to let us recreate the objects after
8443 * rewrite.
8445 RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8448 * Drop the dependency records of the GENERATED expression, in particular
8449 * its INTERNAL dependency on the column, which would otherwise cause
8450 * dependency.c to refuse to perform the deletion.
8452 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8453 if (!OidIsValid(attrdefoid))
8454 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8455 RelationGetRelid(rel), attnum);
8456 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8458 /* Make above changes visible */
8459 CommandCounterIncrement();
8462 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8463 * safety, but at present we do not expect anything to depend on the
8464 * expression.
8466 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8467 false, false);
8469 /* Prepare to store the new expression, in the catalogs */
8470 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8471 rawEnt->attnum = attnum;
8472 rawEnt->raw_default = newExpr;
8473 rawEnt->missingMode = false;
8474 rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8476 /* Store the generated expression */
8477 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8478 false, true, false, NULL);
8480 /* Make above new expression visible */
8481 CommandCounterIncrement();
8483 /* Prepare for table rewrite */
8484 defval = (Expr *) build_column_default(rel, attnum);
8486 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8487 newval->attnum = attnum;
8488 newval->expr = expression_planner(defval);
8489 newval->is_generated = true;
8491 tab->newvals = lappend(tab->newvals, newval);
8492 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8494 /* Drop any pg_statistic entry for the column */
8495 RemoveStatistics(RelationGetRelid(rel), attnum);
8497 InvokeObjectPostAlterHook(RelationRelationId,
8498 RelationGetRelid(rel), attnum);
8500 ObjectAddressSubSet(address, RelationRelationId,
8501 RelationGetRelid(rel), attnum);
8502 return address;
8506 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8508 static void
8509 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8512 * Reject ONLY if there are child tables. We could implement this, but it
8513 * is a bit complicated. GENERATED clauses must be attached to the column
8514 * definition and cannot be added later like DEFAULT, so if a child table
8515 * has a generation expression that the parent does not have, the child
8516 * column will necessarily be an attislocal column. So to implement ONLY
8517 * here, we'd need extra code to update attislocal of the direct child
8518 * tables, somewhat similar to how DROP COLUMN does it, so that the
8519 * resulting state can be properly dumped and restored.
8521 if (!recurse &&
8522 find_inheritance_children(RelationGetRelid(rel), lockmode))
8523 ereport(ERROR,
8524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8525 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8528 * Cannot drop generation expression from inherited columns.
8530 if (!recursing)
8532 HeapTuple tuple;
8533 Form_pg_attribute attTup;
8535 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8536 if (!HeapTupleIsValid(tuple))
8537 ereport(ERROR,
8538 (errcode(ERRCODE_UNDEFINED_COLUMN),
8539 errmsg("column \"%s\" of relation \"%s\" does not exist",
8540 cmd->name, RelationGetRelationName(rel))));
8542 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8544 if (attTup->attinhcount > 0)
8545 ereport(ERROR,
8546 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8547 errmsg("cannot drop generation expression from inherited column")));
8552 * Return the address of the affected column.
8554 static ObjectAddress
8555 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8557 HeapTuple tuple;
8558 Form_pg_attribute attTup;
8559 AttrNumber attnum;
8560 Relation attrelation;
8561 Oid attrdefoid;
8562 ObjectAddress address;
8564 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8565 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8566 if (!HeapTupleIsValid(tuple))
8567 ereport(ERROR,
8568 (errcode(ERRCODE_UNDEFINED_COLUMN),
8569 errmsg("column \"%s\" of relation \"%s\" does not exist",
8570 colName, RelationGetRelationName(rel))));
8572 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8573 attnum = attTup->attnum;
8575 if (attnum <= 0)
8576 ereport(ERROR,
8577 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8578 errmsg("cannot alter system column \"%s\"",
8579 colName)));
8581 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8583 if (!missing_ok)
8584 ereport(ERROR,
8585 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8586 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8587 colName, RelationGetRelationName(rel))));
8588 else
8590 ereport(NOTICE,
8591 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8592 colName, RelationGetRelationName(rel))));
8593 heap_freetuple(tuple);
8594 table_close(attrelation, RowExclusiveLock);
8595 return InvalidObjectAddress;
8600 * Mark the column as no longer generated. (The atthasdef flag needs to
8601 * get cleared too, but RemoveAttrDefault will handle that.)
8603 attTup->attgenerated = '\0';
8604 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8606 InvokeObjectPostAlterHook(RelationRelationId,
8607 RelationGetRelid(rel),
8608 attnum);
8609 heap_freetuple(tuple);
8611 table_close(attrelation, RowExclusiveLock);
8614 * Drop the dependency records of the GENERATED expression, in particular
8615 * its INTERNAL dependency on the column, which would otherwise cause
8616 * dependency.c to refuse to perform the deletion.
8618 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8619 if (!OidIsValid(attrdefoid))
8620 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8621 RelationGetRelid(rel), attnum);
8622 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8624 /* Make above changes visible */
8625 CommandCounterIncrement();
8628 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8629 * safety, but at present we do not expect anything to depend on the
8630 * default.
8632 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8633 false, false);
8635 ObjectAddressSubSet(address, RelationRelationId,
8636 RelationGetRelid(rel), attnum);
8637 return address;
8641 * ALTER TABLE ALTER COLUMN SET STATISTICS
8643 * Return value is the address of the modified column
8645 static ObjectAddress
8646 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8648 int newtarget = 0;
8649 bool newtarget_default;
8650 Relation attrelation;
8651 HeapTuple tuple,
8652 newtuple;
8653 Form_pg_attribute attrtuple;
8654 AttrNumber attnum;
8655 ObjectAddress address;
8656 Datum repl_val[Natts_pg_attribute];
8657 bool repl_null[Natts_pg_attribute];
8658 bool repl_repl[Natts_pg_attribute];
8661 * We allow referencing columns by numbers only for indexes, since table
8662 * column numbers could contain gaps if columns are later dropped.
8664 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8665 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8666 !colName)
8667 ereport(ERROR,
8668 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8669 errmsg("cannot refer to non-index column by number")));
8671 /* -1 was used in previous versions for the default setting */
8672 if (newValue && intVal(newValue) != -1)
8674 newtarget = intVal(newValue);
8675 newtarget_default = false;
8677 else
8678 newtarget_default = true;
8680 if (!newtarget_default)
8683 * Limit target to a sane range
8685 if (newtarget < 0)
8687 ereport(ERROR,
8688 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8689 errmsg("statistics target %d is too low",
8690 newtarget)));
8692 else if (newtarget > MAX_STATISTICS_TARGET)
8694 newtarget = MAX_STATISTICS_TARGET;
8695 ereport(WARNING,
8696 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8697 errmsg("lowering statistics target to %d",
8698 newtarget)));
8702 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8704 if (colName)
8706 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8708 if (!HeapTupleIsValid(tuple))
8709 ereport(ERROR,
8710 (errcode(ERRCODE_UNDEFINED_COLUMN),
8711 errmsg("column \"%s\" of relation \"%s\" does not exist",
8712 colName, RelationGetRelationName(rel))));
8714 else
8716 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8718 if (!HeapTupleIsValid(tuple))
8719 ereport(ERROR,
8720 (errcode(ERRCODE_UNDEFINED_COLUMN),
8721 errmsg("column number %d of relation \"%s\" does not exist",
8722 colNum, RelationGetRelationName(rel))));
8725 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8727 attnum = attrtuple->attnum;
8728 if (attnum <= 0)
8729 ereport(ERROR,
8730 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8731 errmsg("cannot alter system column \"%s\"",
8732 colName)));
8734 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8735 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8737 if (attnum > rel->rd_index->indnkeyatts)
8738 ereport(ERROR,
8739 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8740 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8741 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8742 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8743 ereport(ERROR,
8744 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8745 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8746 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8747 errhint("Alter statistics on table column instead.")));
8750 /* Build new tuple. */
8751 memset(repl_null, false, sizeof(repl_null));
8752 memset(repl_repl, false, sizeof(repl_repl));
8753 if (!newtarget_default)
8754 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8755 else
8756 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8757 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8758 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8759 repl_val, repl_null, repl_repl);
8760 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8762 InvokeObjectPostAlterHook(RelationRelationId,
8763 RelationGetRelid(rel),
8764 attrtuple->attnum);
8765 ObjectAddressSubSet(address, RelationRelationId,
8766 RelationGetRelid(rel), attnum);
8768 heap_freetuple(newtuple);
8770 ReleaseSysCache(tuple);
8772 table_close(attrelation, RowExclusiveLock);
8774 return address;
8778 * Return value is the address of the modified column
8780 static ObjectAddress
8781 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8782 bool isReset, LOCKMODE lockmode)
8784 Relation attrelation;
8785 HeapTuple tuple,
8786 newtuple;
8787 Form_pg_attribute attrtuple;
8788 AttrNumber attnum;
8789 Datum datum,
8790 newOptions;
8791 bool isnull;
8792 ObjectAddress address;
8793 Datum repl_val[Natts_pg_attribute];
8794 bool repl_null[Natts_pg_attribute];
8795 bool repl_repl[Natts_pg_attribute];
8797 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8799 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8801 if (!HeapTupleIsValid(tuple))
8802 ereport(ERROR,
8803 (errcode(ERRCODE_UNDEFINED_COLUMN),
8804 errmsg("column \"%s\" of relation \"%s\" does not exist",
8805 colName, RelationGetRelationName(rel))));
8806 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8808 attnum = attrtuple->attnum;
8809 if (attnum <= 0)
8810 ereport(ERROR,
8811 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8812 errmsg("cannot alter system column \"%s\"",
8813 colName)));
8815 /* Generate new proposed attoptions (text array) */
8816 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8817 &isnull);
8818 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8819 castNode(List, options), NULL, NULL,
8820 false, isReset);
8821 /* Validate new options */
8822 (void) attribute_reloptions(newOptions, true);
8824 /* Build new tuple. */
8825 memset(repl_null, false, sizeof(repl_null));
8826 memset(repl_repl, false, sizeof(repl_repl));
8827 if (newOptions != (Datum) 0)
8828 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8829 else
8830 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8831 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8832 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8833 repl_val, repl_null, repl_repl);
8835 /* Update system catalog. */
8836 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8838 InvokeObjectPostAlterHook(RelationRelationId,
8839 RelationGetRelid(rel),
8840 attrtuple->attnum);
8841 ObjectAddressSubSet(address, RelationRelationId,
8842 RelationGetRelid(rel), attnum);
8844 heap_freetuple(newtuple);
8846 ReleaseSysCache(tuple);
8848 table_close(attrelation, RowExclusiveLock);
8850 return address;
8854 * Helper function for ATExecSetStorage and ATExecSetCompression
8856 * Set the attstorage and/or attcompression fields for index columns
8857 * associated with the specified table column.
8859 static void
8860 SetIndexStorageProperties(Relation rel, Relation attrelation,
8861 AttrNumber attnum,
8862 bool setstorage, char newstorage,
8863 bool setcompression, char newcompression,
8864 LOCKMODE lockmode)
8866 ListCell *lc;
8868 foreach(lc, RelationGetIndexList(rel))
8870 Oid indexoid = lfirst_oid(lc);
8871 Relation indrel;
8872 AttrNumber indattnum = 0;
8873 HeapTuple tuple;
8875 indrel = index_open(indexoid, lockmode);
8877 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8879 if (indrel->rd_index->indkey.values[i] == attnum)
8881 indattnum = i + 1;
8882 break;
8886 if (indattnum == 0)
8888 index_close(indrel, lockmode);
8889 continue;
8892 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8894 if (HeapTupleIsValid(tuple))
8896 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8898 if (setstorage)
8899 attrtuple->attstorage = newstorage;
8901 if (setcompression)
8902 attrtuple->attcompression = newcompression;
8904 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8906 InvokeObjectPostAlterHook(RelationRelationId,
8907 RelationGetRelid(rel),
8908 attrtuple->attnum);
8910 heap_freetuple(tuple);
8913 index_close(indrel, lockmode);
8918 * ALTER TABLE ALTER COLUMN SET STORAGE
8920 * Return value is the address of the modified column
8922 static ObjectAddress
8923 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8925 Relation attrelation;
8926 HeapTuple tuple;
8927 Form_pg_attribute attrtuple;
8928 AttrNumber attnum;
8929 ObjectAddress address;
8931 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8933 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8935 if (!HeapTupleIsValid(tuple))
8936 ereport(ERROR,
8937 (errcode(ERRCODE_UNDEFINED_COLUMN),
8938 errmsg("column \"%s\" of relation \"%s\" does not exist",
8939 colName, RelationGetRelationName(rel))));
8940 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8942 attnum = attrtuple->attnum;
8943 if (attnum <= 0)
8944 ereport(ERROR,
8945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8946 errmsg("cannot alter system column \"%s\"",
8947 colName)));
8949 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8951 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8953 InvokeObjectPostAlterHook(RelationRelationId,
8954 RelationGetRelid(rel),
8955 attrtuple->attnum);
8958 * Apply the change to indexes as well (only for simple index columns,
8959 * matching behavior of index.c ConstructTupleDescriptor()).
8961 SetIndexStorageProperties(rel, attrelation, attnum,
8962 true, attrtuple->attstorage,
8963 false, 0,
8964 lockmode);
8966 heap_freetuple(tuple);
8968 table_close(attrelation, RowExclusiveLock);
8970 ObjectAddressSubSet(address, RelationRelationId,
8971 RelationGetRelid(rel), attnum);
8972 return address;
8977 * ALTER TABLE DROP COLUMN
8979 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8980 * because we have to decide at runtime whether to recurse or not depending
8981 * on whether attinhcount goes to zero or not. (We can't check this in a
8982 * static pre-pass because it won't handle multiple inheritance situations
8983 * correctly.)
8985 static void
8986 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8987 AlterTableCmd *cmd, LOCKMODE lockmode,
8988 AlterTableUtilityContext *context)
8990 if (rel->rd_rel->reloftype && !recursing)
8991 ereport(ERROR,
8992 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8993 errmsg("cannot drop column from typed table")));
8995 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8996 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8998 if (recurse)
8999 cmd->recurse = true;
9003 * Drops column 'colName' from relation 'rel' and returns the address of the
9004 * dropped column. The column is also dropped (or marked as no longer
9005 * inherited from relation) from the relation's inheritance children, if any.
9007 * In the recursive invocations for inheritance child relations, instead of
9008 * dropping the column directly (if to be dropped at all), its object address
9009 * is added to 'addrs', which must be non-NULL in such invocations. All
9010 * columns are dropped at the same time after all the children have been
9011 * checked recursively.
9013 static ObjectAddress
9014 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9015 DropBehavior behavior,
9016 bool recurse, bool recursing,
9017 bool missing_ok, LOCKMODE lockmode,
9018 ObjectAddresses *addrs)
9020 HeapTuple tuple;
9021 Form_pg_attribute targetatt;
9022 AttrNumber attnum;
9023 List *children;
9024 ObjectAddress object;
9025 bool is_expr;
9027 /* At top level, permission check was done in ATPrepCmd, else do it */
9028 if (recursing)
9029 ATSimplePermissions(AT_DropColumn, rel,
9030 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9032 /* Initialize addrs on the first invocation */
9033 Assert(!recursing || addrs != NULL);
9035 /* since this function recurses, it could be driven to stack overflow */
9036 check_stack_depth();
9038 if (!recursing)
9039 addrs = new_object_addresses();
9042 * get the number of the attribute
9044 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9045 if (!HeapTupleIsValid(tuple))
9047 if (!missing_ok)
9049 ereport(ERROR,
9050 (errcode(ERRCODE_UNDEFINED_COLUMN),
9051 errmsg("column \"%s\" of relation \"%s\" does not exist",
9052 colName, RelationGetRelationName(rel))));
9054 else
9056 ereport(NOTICE,
9057 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9058 colName, RelationGetRelationName(rel))));
9059 return InvalidObjectAddress;
9062 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9064 attnum = targetatt->attnum;
9066 /* Can't drop a system attribute */
9067 if (attnum <= 0)
9068 ereport(ERROR,
9069 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9070 errmsg("cannot drop system column \"%s\"",
9071 colName)));
9074 * Don't drop inherited columns, unless recursing (presumably from a drop
9075 * of the parent column)
9077 if (targetatt->attinhcount > 0 && !recursing)
9078 ereport(ERROR,
9079 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9080 errmsg("cannot drop inherited column \"%s\"",
9081 colName)));
9084 * Don't drop columns used in the partition key, either. (If we let this
9085 * go through, the key column's dependencies would cause a cascaded drop
9086 * of the whole table, which is surely not what the user expected.)
9088 if (has_partition_attrs(rel,
9089 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9090 &is_expr))
9091 ereport(ERROR,
9092 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9093 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9094 colName, RelationGetRelationName(rel))));
9096 ReleaseSysCache(tuple);
9099 * Propagate to children as appropriate. Unlike most other ALTER
9100 * routines, we have to do this one level of recursion at a time; we can't
9101 * use find_all_inheritors to do it in one pass.
9103 children =
9104 find_inheritance_children(RelationGetRelid(rel), lockmode);
9106 if (children)
9108 Relation attr_rel;
9109 ListCell *child;
9112 * In case of a partitioned table, the column must be dropped from the
9113 * partitions as well.
9115 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9116 ereport(ERROR,
9117 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9118 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9119 errhint("Do not specify the ONLY keyword.")));
9121 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9122 foreach(child, children)
9124 Oid childrelid = lfirst_oid(child);
9125 Relation childrel;
9126 Form_pg_attribute childatt;
9128 /* find_inheritance_children already got lock */
9129 childrel = table_open(childrelid, NoLock);
9130 CheckAlterTableIsSafe(childrel);
9132 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9133 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9134 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9135 colName, childrelid);
9136 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9138 if (childatt->attinhcount <= 0) /* shouldn't happen */
9139 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9140 childrelid, colName);
9142 if (recurse)
9145 * If the child column has other definition sources, just
9146 * decrement its inheritance count; if not, recurse to delete
9147 * it.
9149 if (childatt->attinhcount == 1 && !childatt->attislocal)
9151 /* Time to delete this child column, too */
9152 ATExecDropColumn(wqueue, childrel, colName,
9153 behavior, true, true,
9154 false, lockmode, addrs);
9156 else
9158 /* Child column must survive my deletion */
9159 childatt->attinhcount--;
9161 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9163 /* Make update visible */
9164 CommandCounterIncrement();
9167 else
9170 * If we were told to drop ONLY in this table (no recursion),
9171 * we need to mark the inheritors' attributes as locally
9172 * defined rather than inherited.
9174 childatt->attinhcount--;
9175 childatt->attislocal = true;
9177 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9179 /* Make update visible */
9180 CommandCounterIncrement();
9183 heap_freetuple(tuple);
9185 table_close(childrel, NoLock);
9187 table_close(attr_rel, RowExclusiveLock);
9190 /* Add object to delete */
9191 object.classId = RelationRelationId;
9192 object.objectId = RelationGetRelid(rel);
9193 object.objectSubId = attnum;
9194 add_exact_object_address(&object, addrs);
9196 if (!recursing)
9198 /* Recursion has ended, drop everything that was collected */
9199 performMultipleDeletions(addrs, behavior, 0);
9200 free_object_addresses(addrs);
9203 return object;
9207 * Prepare to add a primary key on table, by adding not-null constraints
9208 * on all columns.
9210 static void
9211 ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9212 bool recurse, LOCKMODE lockmode,
9213 AlterTableUtilityContext *context)
9215 ListCell *lc;
9216 Constraint *pkconstr;
9218 pkconstr = castNode(Constraint, cmd->def);
9219 if (pkconstr->contype != CONSTR_PRIMARY)
9220 return;
9223 * If not recursing, we must ensure that all children have a NOT NULL
9224 * constraint on the columns, and error out if not.
9226 if (!recurse)
9228 List *children;
9230 children = find_inheritance_children(RelationGetRelid(rel),
9231 lockmode);
9232 foreach_oid(childrelid, children)
9234 foreach(lc, pkconstr->keys)
9236 HeapTuple tup;
9237 Form_pg_attribute attrForm;
9238 char *attname = strVal(lfirst(lc));
9240 tup = SearchSysCacheAttName(childrelid, attname);
9241 if (!tup)
9242 elog(ERROR, "cache lookup failed for attribute %s of relation %u",
9243 attname, childrelid);
9244 attrForm = (Form_pg_attribute) GETSTRUCT(tup);
9245 if (!attrForm->attnotnull)
9246 ereport(ERROR,
9247 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9248 attname, get_rel_name(childrelid)));
9249 ReleaseSysCache(tup);
9254 /* Insert not-null constraints in the queue for the PK columns */
9255 foreach(lc, pkconstr->keys)
9257 AlterTableCmd *newcmd;
9258 Constraint *nnconstr;
9260 nnconstr = makeNotNullConstraint(lfirst(lc));
9262 newcmd = makeNode(AlterTableCmd);
9263 newcmd->subtype = AT_AddConstraint;
9264 newcmd->recurse = true;
9265 newcmd->def = (Node *) nnconstr;
9267 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9272 * ALTER TABLE ADD INDEX
9274 * There is no such command in the grammar, but parse_utilcmd.c converts
9275 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9276 * us schedule creation of the index at the appropriate time during ALTER.
9278 * Return value is the address of the new index.
9280 static ObjectAddress
9281 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9282 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9284 bool check_rights;
9285 bool skip_build;
9286 bool quiet;
9287 ObjectAddress address;
9289 Assert(IsA(stmt, IndexStmt));
9290 Assert(!stmt->concurrent);
9292 /* The IndexStmt has already been through transformIndexStmt */
9293 Assert(stmt->transformed);
9295 /* suppress schema rights check when rebuilding existing index */
9296 check_rights = !is_rebuild;
9297 /* skip index build if phase 3 will do it or we're reusing an old one */
9298 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9299 /* suppress notices when rebuilding existing index */
9300 quiet = is_rebuild;
9302 address = DefineIndex(RelationGetRelid(rel),
9303 stmt,
9304 InvalidOid, /* no predefined OID */
9305 InvalidOid, /* no parent index */
9306 InvalidOid, /* no parent constraint */
9307 -1, /* total_parts unknown */
9308 true, /* is_alter_table */
9309 check_rights,
9310 false, /* check_not_in_use - we did it already */
9311 skip_build,
9312 quiet);
9315 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9316 * new index instead of building from scratch. Restore associated fields.
9317 * This may store InvalidSubTransactionId in both fields, in which case
9318 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9319 * this after the CCI that made catalog rows visible to any rebuild. The
9320 * DROP of the old edition of this index will have scheduled the storage
9321 * for deletion at commit, so cancel that pending deletion.
9323 if (RelFileNumberIsValid(stmt->oldNumber))
9325 Relation irel = index_open(address.objectId, NoLock);
9327 irel->rd_createSubid = stmt->oldCreateSubid;
9328 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9329 RelationPreserveStorage(irel->rd_locator, true);
9330 index_close(irel, NoLock);
9333 return address;
9337 * ALTER TABLE ADD STATISTICS
9339 * This is no such command in the grammar, but we use this internally to add
9340 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9341 * column type change.
9343 static ObjectAddress
9344 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9345 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9347 ObjectAddress address;
9349 Assert(IsA(stmt, CreateStatsStmt));
9351 /* The CreateStatsStmt has already been through transformStatsStmt */
9352 Assert(stmt->transformed);
9354 address = CreateStatistics(stmt);
9356 return address;
9360 * ALTER TABLE ADD CONSTRAINT USING INDEX
9362 * Returns the address of the new constraint.
9364 static ObjectAddress
9365 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9366 IndexStmt *stmt, LOCKMODE lockmode)
9368 Oid index_oid = stmt->indexOid;
9369 Relation indexRel;
9370 char *indexName;
9371 IndexInfo *indexInfo;
9372 char *constraintName;
9373 char constraintType;
9374 ObjectAddress address;
9375 bits16 flags;
9377 Assert(IsA(stmt, IndexStmt));
9378 Assert(OidIsValid(index_oid));
9379 Assert(stmt->isconstraint);
9382 * Doing this on partitioned tables is not a simple feature to implement,
9383 * so let's punt for now.
9385 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9386 ereport(ERROR,
9387 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9388 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9390 indexRel = index_open(index_oid, AccessShareLock);
9392 indexName = pstrdup(RelationGetRelationName(indexRel));
9394 indexInfo = BuildIndexInfo(indexRel);
9396 /* this should have been checked at parse time */
9397 if (!indexInfo->ii_Unique)
9398 elog(ERROR, "index \"%s\" is not unique", indexName);
9401 * Determine name to assign to constraint. We require a constraint to
9402 * have the same name as the underlying index; therefore, use the index's
9403 * existing name as the default constraint name, and if the user
9404 * explicitly gives some other name for the constraint, rename the index
9405 * to match.
9407 constraintName = stmt->idxname;
9408 if (constraintName == NULL)
9409 constraintName = indexName;
9410 else if (strcmp(constraintName, indexName) != 0)
9412 ereport(NOTICE,
9413 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9414 indexName, constraintName)));
9415 RenameRelationInternal(index_oid, constraintName, false, true);
9418 /* Extra checks needed if making primary key */
9419 if (stmt->primary)
9420 index_check_primary_key(rel, indexInfo, true, stmt);
9422 /* Note we currently don't support EXCLUSION constraints here */
9423 if (stmt->primary)
9424 constraintType = CONSTRAINT_PRIMARY;
9425 else
9426 constraintType = CONSTRAINT_UNIQUE;
9428 /* Create the catalog entries for the constraint */
9429 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9430 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9431 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9432 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9433 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9435 address = index_constraint_create(rel,
9436 index_oid,
9437 InvalidOid,
9438 indexInfo,
9439 constraintName,
9440 constraintType,
9441 flags,
9442 allowSystemTableMods,
9443 false); /* is_internal */
9445 index_close(indexRel, NoLock);
9447 return address;
9451 * ALTER TABLE ADD CONSTRAINT
9453 * Return value is the address of the new constraint; if no constraint was
9454 * added, InvalidObjectAddress is returned.
9456 static ObjectAddress
9457 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9458 Constraint *newConstraint, bool recurse, bool is_readd,
9459 LOCKMODE lockmode)
9461 ObjectAddress address = InvalidObjectAddress;
9463 Assert(IsA(newConstraint, Constraint));
9466 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9467 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9468 * parse_utilcmd.c).
9470 switch (newConstraint->contype)
9472 case CONSTR_CHECK:
9473 case CONSTR_NOTNULL:
9474 address =
9475 ATAddCheckNNConstraint(wqueue, tab, rel,
9476 newConstraint, recurse, false, is_readd,
9477 lockmode);
9478 break;
9480 case CONSTR_FOREIGN:
9483 * Assign or validate constraint name
9485 if (newConstraint->conname)
9487 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9488 RelationGetRelid(rel),
9489 newConstraint->conname))
9490 ereport(ERROR,
9491 (errcode(ERRCODE_DUPLICATE_OBJECT),
9492 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9493 newConstraint->conname,
9494 RelationGetRelationName(rel))));
9496 else
9497 newConstraint->conname =
9498 ChooseConstraintName(RelationGetRelationName(rel),
9499 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9500 "fkey",
9501 RelationGetNamespace(rel),
9502 NIL);
9504 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9505 newConstraint,
9506 recurse, false,
9507 lockmode);
9508 break;
9510 default:
9511 elog(ERROR, "unrecognized constraint type: %d",
9512 (int) newConstraint->contype);
9515 return address;
9519 * Generate the column-name portion of the constraint name for a new foreign
9520 * key given the list of column names that reference the referenced
9521 * table. This will be passed to ChooseConstraintName along with the parent
9522 * table name and the "fkey" suffix.
9524 * We know that less than NAMEDATALEN characters will actually be used, so we
9525 * can truncate the result once we've generated that many.
9527 * XXX see also ChooseExtendedStatisticNameAddition and
9528 * ChooseIndexNameAddition.
9530 static char *
9531 ChooseForeignKeyConstraintNameAddition(List *colnames)
9533 char buf[NAMEDATALEN * 2];
9534 int buflen = 0;
9535 ListCell *lc;
9537 buf[0] = '\0';
9538 foreach(lc, colnames)
9540 const char *name = strVal(lfirst(lc));
9542 if (buflen > 0)
9543 buf[buflen++] = '_'; /* insert _ between names */
9546 * At this point we have buflen <= NAMEDATALEN. name should be less
9547 * than NAMEDATALEN already, but use strlcpy for paranoia.
9549 strlcpy(buf + buflen, name, NAMEDATALEN);
9550 buflen += strlen(buf + buflen);
9551 if (buflen >= NAMEDATALEN)
9552 break;
9554 return pstrdup(buf);
9558 * Add a check or not-null constraint to a single table and its children.
9559 * Returns the address of the constraint added to the parent relation,
9560 * if one gets added, or InvalidObjectAddress otherwise.
9562 * Subroutine for ATExecAddConstraint.
9564 * We must recurse to child tables during execution, rather than using
9565 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9566 * constraints *must* be given the same name, else they won't be seen as
9567 * related later. If the user didn't explicitly specify a name, then
9568 * AddRelationNewConstraints would normally assign different names to the
9569 * child constraints. To fix that, we must capture the name assigned at
9570 * the parent table and pass that down.
9572 static ObjectAddress
9573 ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9574 Constraint *constr, bool recurse, bool recursing,
9575 bool is_readd, LOCKMODE lockmode)
9577 List *newcons;
9578 ListCell *lcon;
9579 List *children;
9580 ListCell *child;
9581 ObjectAddress address = InvalidObjectAddress;
9583 /* Guard against stack overflow due to overly deep inheritance tree. */
9584 check_stack_depth();
9586 /* At top level, permission check was done in ATPrepCmd, else do it */
9587 if (recursing)
9588 ATSimplePermissions(AT_AddConstraint, rel,
9589 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
9592 * Call AddRelationNewConstraints to do the work, making sure it works on
9593 * a copy of the Constraint so transformExpr can't modify the original. It
9594 * returns a list of cooked constraints.
9596 * If the constraint ends up getting merged with a pre-existing one, it's
9597 * omitted from the returned list, which is what we want: we do not need
9598 * to do any validation work. That can only happen at child tables,
9599 * though, since we disallow merging at the top level.
9601 newcons = AddRelationNewConstraints(rel, NIL,
9602 list_make1(copyObject(constr)),
9603 recursing || is_readd, /* allow_merge */
9604 !recursing, /* is_local */
9605 is_readd, /* is_internal */
9606 NULL); /* queryString not available
9607 * here */
9609 /* we don't expect more than one constraint here */
9610 Assert(list_length(newcons) <= 1);
9612 /* Add each to-be-validated constraint to Phase 3's queue */
9613 foreach(lcon, newcons)
9615 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9617 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9619 NewConstraint *newcon;
9621 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9622 newcon->name = ccon->name;
9623 newcon->contype = ccon->contype;
9624 newcon->qual = ccon->expr;
9626 tab->constraints = lappend(tab->constraints, newcon);
9629 /* Save the actually assigned name if it was defaulted */
9630 if (constr->conname == NULL)
9631 constr->conname = ccon->name;
9634 * If adding a not-null constraint, set the pg_attribute flag and tell
9635 * phase 3 to verify existing rows, if needed.
9637 if (constr->contype == CONSTR_NOTNULL)
9638 set_attnotnull(wqueue, rel, ccon->attnum, lockmode);
9640 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9643 /* At this point we must have a locked-down name to use */
9644 Assert(newcons == NIL || constr->conname != NULL);
9646 /* Advance command counter in case same table is visited multiple times */
9647 CommandCounterIncrement();
9650 * If the constraint got merged with an existing constraint, we're done.
9651 * We mustn't recurse to child tables in this case, because they've
9652 * already got the constraint, and visiting them again would lead to an
9653 * incorrect value for coninhcount.
9655 if (newcons == NIL)
9656 return address;
9659 * If adding a NO INHERIT constraint, no need to find our children.
9661 if (constr->is_no_inherit)
9662 return address;
9665 * Propagate to children as appropriate. Unlike most other ALTER
9666 * routines, we have to do this one level of recursion at a time; we can't
9667 * use find_all_inheritors to do it in one pass.
9669 children =
9670 find_inheritance_children(RelationGetRelid(rel), lockmode);
9673 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9674 * constraint creation only if there are no children currently. Error out
9675 * otherwise.
9677 if (!recurse && children != NIL)
9678 ereport(ERROR,
9679 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9680 errmsg("constraint must be added to child tables too")));
9683 * Recurse to create the constraint on each child.
9685 foreach(child, children)
9687 Oid childrelid = lfirst_oid(child);
9688 Relation childrel;
9689 AlteredTableInfo *childtab;
9691 /* find_inheritance_children already got lock */
9692 childrel = table_open(childrelid, NoLock);
9693 CheckAlterTableIsSafe(childrel);
9695 /* Find or create work queue entry for this table */
9696 childtab = ATGetQueueEntry(wqueue, childrel);
9698 /* Recurse to this child */
9699 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9700 constr, recurse, true, is_readd, lockmode);
9702 table_close(childrel, NoLock);
9705 return address;
9709 * Add a foreign-key constraint to a single table; return the new constraint's
9710 * address.
9712 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9713 * lock on the rel, and have done appropriate validity checks for it.
9714 * We do permissions checks here, however.
9716 * When the referenced or referencing tables (or both) are partitioned,
9717 * multiple pg_constraint rows are required -- one for each partitioned table
9718 * and each partition on each side (fortunately, not one for every combination
9719 * thereof). We also need action triggers on each leaf partition on the
9720 * referenced side, and check triggers on each leaf partition on the
9721 * referencing side.
9723 static ObjectAddress
9724 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9725 Constraint *fkconstraint,
9726 bool recurse, bool recursing, LOCKMODE lockmode)
9728 Relation pkrel;
9729 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9730 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9731 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9732 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9733 Oid pkcolloid[INDEX_MAX_KEYS] = {0};
9734 Oid fkcolloid[INDEX_MAX_KEYS] = {0};
9735 Oid opclasses[INDEX_MAX_KEYS] = {0};
9736 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9737 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9738 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9739 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9740 bool with_period;
9741 bool pk_has_without_overlaps;
9742 int i;
9743 int numfks,
9744 numpks,
9745 numfkdelsetcols;
9746 Oid indexOid;
9747 bool old_check_ok;
9748 ObjectAddress address;
9749 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9752 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9753 * delete rows out from under us.
9755 if (OidIsValid(fkconstraint->old_pktable_oid))
9756 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9757 else
9758 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9761 * Validity checks (permission checks wait till we have the column
9762 * numbers)
9764 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9766 if (!recurse)
9767 ereport(ERROR,
9768 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9769 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9770 RelationGetRelationName(rel),
9771 RelationGetRelationName(pkrel))));
9772 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9773 ereport(ERROR,
9774 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9775 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9776 RelationGetRelationName(rel),
9777 RelationGetRelationName(pkrel)),
9778 errdetail("This feature is not yet supported on partitioned tables.")));
9781 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9782 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9783 ereport(ERROR,
9784 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9785 errmsg("referenced relation \"%s\" is not a table",
9786 RelationGetRelationName(pkrel))));
9788 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9789 ereport(ERROR,
9790 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9791 errmsg("permission denied: \"%s\" is a system catalog",
9792 RelationGetRelationName(pkrel))));
9795 * References from permanent or unlogged tables to temp tables, and from
9796 * permanent tables to unlogged tables, are disallowed because the
9797 * referenced data can vanish out from under us. References from temp
9798 * tables to any other table type are also disallowed, because other
9799 * backends might need to run the RI triggers on the perm table, but they
9800 * can't reliably see tuples in the local buffers of other backends.
9802 switch (rel->rd_rel->relpersistence)
9804 case RELPERSISTENCE_PERMANENT:
9805 if (!RelationIsPermanent(pkrel))
9806 ereport(ERROR,
9807 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9808 errmsg("constraints on permanent tables may reference only permanent tables")));
9809 break;
9810 case RELPERSISTENCE_UNLOGGED:
9811 if (!RelationIsPermanent(pkrel)
9812 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9813 ereport(ERROR,
9814 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9815 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9816 break;
9817 case RELPERSISTENCE_TEMP:
9818 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9819 ereport(ERROR,
9820 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9821 errmsg("constraints on temporary tables may reference only temporary tables")));
9822 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9823 ereport(ERROR,
9824 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9825 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9826 break;
9830 * Look up the referencing attributes to make sure they exist, and record
9831 * their attnums and type and collation OIDs.
9833 numfks = transformColumnNameList(RelationGetRelid(rel),
9834 fkconstraint->fk_attrs,
9835 fkattnum, fktypoid, fkcolloid);
9836 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
9837 if (with_period && !fkconstraint->fk_with_period)
9838 ereport(ERROR,
9839 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9840 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9842 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9843 fkconstraint->fk_del_set_cols,
9844 fkdelsetcols, NULL, NULL);
9845 validateFkOnDeleteSetColumns(numfks, fkattnum,
9846 numfkdelsetcols, fkdelsetcols,
9847 fkconstraint->fk_del_set_cols);
9850 * If the attribute list for the referenced table was omitted, lookup the
9851 * definition of the primary key and use it. Otherwise, validate the
9852 * supplied attribute list. In either case, discover the index OID and
9853 * index opclasses, and the attnums and type and collation OIDs of the
9854 * attributes.
9856 if (fkconstraint->pk_attrs == NIL)
9858 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9859 &fkconstraint->pk_attrs,
9860 pkattnum, pktypoid, pkcolloid,
9861 opclasses, &pk_has_without_overlaps);
9863 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
9864 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
9865 ereport(ERROR,
9866 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9867 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
9869 else
9871 numpks = transformColumnNameList(RelationGetRelid(pkrel),
9872 fkconstraint->pk_attrs,
9873 pkattnum, pktypoid, pkcolloid);
9875 /* Since we got pk_attrs, one should be a period. */
9876 if (with_period && !fkconstraint->pk_with_period)
9877 ereport(ERROR,
9878 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9879 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
9881 /* Look for an index matching the column list */
9882 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9883 with_period, opclasses, &pk_has_without_overlaps);
9887 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
9888 * must use PERIOD.
9890 if (pk_has_without_overlaps && !with_period)
9891 ereport(ERROR,
9892 errcode(ERRCODE_INVALID_FOREIGN_KEY),
9893 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
9896 * Now we can check permissions.
9898 checkFkeyPermissions(pkrel, pkattnum, numpks);
9901 * Check some things for generated columns.
9903 for (i = 0; i < numfks; i++)
9905 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9907 if (attgenerated)
9910 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9912 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9913 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9914 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9915 ereport(ERROR,
9916 (errcode(ERRCODE_SYNTAX_ERROR),
9917 errmsg("invalid %s action for foreign key constraint containing generated column",
9918 "ON UPDATE")));
9919 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9920 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9921 ereport(ERROR,
9922 (errcode(ERRCODE_SYNTAX_ERROR),
9923 errmsg("invalid %s action for foreign key constraint containing generated column",
9924 "ON DELETE")));
9929 * Some actions are currently unsupported for foreign keys using PERIOD.
9931 if (fkconstraint->fk_with_period)
9933 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
9934 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9935 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
9936 ereport(ERROR,
9937 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9938 errmsg("unsupported %s action for foreign key constraint using PERIOD",
9939 "ON UPDATE"));
9941 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
9942 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9943 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9944 ereport(ERROR,
9945 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9946 errmsg("unsupported %s action for foreign key constraint using PERIOD",
9947 "ON DELETE"));
9951 * Look up the equality operators to use in the constraint.
9953 * Note that we have to be careful about the difference between the actual
9954 * PK column type and the opclass' declared input type, which might be
9955 * only binary-compatible with it. The declared opcintype is the right
9956 * thing to probe pg_amop with.
9958 if (numfks != numpks)
9959 ereport(ERROR,
9960 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9961 errmsg("number of referencing and referenced columns for foreign key disagree")));
9964 * On the strength of a previous constraint, we might avoid scanning
9965 * tables to validate this one. See below.
9967 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9968 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9970 for (i = 0; i < numpks; i++)
9972 Oid pktype = pktypoid[i];
9973 Oid fktype = fktypoid[i];
9974 Oid fktyped;
9975 Oid pkcoll = pkcolloid[i];
9976 Oid fkcoll = fkcolloid[i];
9977 HeapTuple cla_ht;
9978 Form_pg_opclass cla_tup;
9979 Oid amid;
9980 Oid opfamily;
9981 Oid opcintype;
9982 Oid pfeqop;
9983 Oid ppeqop;
9984 Oid ffeqop;
9985 int16 eqstrategy;
9986 Oid pfeqop_right;
9988 /* We need several fields out of the pg_opclass entry */
9989 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9990 if (!HeapTupleIsValid(cla_ht))
9991 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9992 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9993 amid = cla_tup->opcmethod;
9994 opfamily = cla_tup->opcfamily;
9995 opcintype = cla_tup->opcintype;
9996 ReleaseSysCache(cla_ht);
9998 if (with_period)
10000 CompareType cmptype;
10001 bool for_overlaps = with_period && i == numpks - 1;
10004 * GiST indexes are required to support temporal foreign keys
10005 * because they combine equals and overlaps.
10007 if (amid != GIST_AM_OID)
10008 elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10010 cmptype = for_overlaps ? COMPARE_OVERLAP : COMPARE_EQ;
10013 * An opclass can use whatever strategy numbers it wants, so we
10014 * ask the opclass what number it actually uses instead of our RT*
10015 * constants.
10017 eqstrategy = GistTranslateStratnum(opclasses[i], cmptype);
10018 if (eqstrategy == InvalidStrategy)
10020 HeapTuple tuple;
10022 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10023 if (!HeapTupleIsValid(tuple))
10024 elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10026 ereport(ERROR,
10027 errcode(ERRCODE_UNDEFINED_OBJECT),
10028 for_overlaps
10029 ? errmsg("could not identify an overlaps operator for foreign key")
10030 : errmsg("could not identify an equality operator for foreign key"),
10031 errdetail("Could not translate compare type %d for operator class \"%s\" for access method \"%s\".",
10032 cmptype, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10035 else
10038 * Check it's a btree; currently this can never fail since no
10039 * other index AMs support unique indexes. If we ever did have
10040 * other types of unique indexes, we'd need a way to determine
10041 * which operator strategy number is equality. (We could use
10042 * something like GistTranslateStratnum.)
10044 if (amid != BTREE_AM_OID)
10045 elog(ERROR, "only b-tree indexes are supported for foreign keys");
10046 eqstrategy = BTEqualStrategyNumber;
10050 * There had better be a primary equality operator for the index.
10051 * We'll use it for PK = PK comparisons.
10053 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10054 eqstrategy);
10056 if (!OidIsValid(ppeqop))
10057 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10058 eqstrategy, opcintype, opcintype, opfamily);
10061 * Are there equality operators that take exactly the FK type? Assume
10062 * we should look through any domain here.
10064 fktyped = getBaseType(fktype);
10066 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10067 eqstrategy);
10068 if (OidIsValid(pfeqop))
10070 pfeqop_right = fktyped;
10071 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10072 eqstrategy);
10074 else
10076 /* keep compiler quiet */
10077 pfeqop_right = InvalidOid;
10078 ffeqop = InvalidOid;
10081 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10084 * Otherwise, look for an implicit cast from the FK type to the
10085 * opcintype, and if found, use the primary equality operator.
10086 * This is a bit tricky because opcintype might be a polymorphic
10087 * type such as ANYARRAY or ANYENUM; so what we have to test is
10088 * whether the two actual column types can be concurrently cast to
10089 * that type. (Otherwise, we'd fail to reject combinations such
10090 * as int[] and point[].)
10092 Oid input_typeids[2];
10093 Oid target_typeids[2];
10095 input_typeids[0] = pktype;
10096 input_typeids[1] = fktype;
10097 target_typeids[0] = opcintype;
10098 target_typeids[1] = opcintype;
10099 if (can_coerce_type(2, input_typeids, target_typeids,
10100 COERCION_IMPLICIT))
10102 pfeqop = ffeqop = ppeqop;
10103 pfeqop_right = opcintype;
10107 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10108 ereport(ERROR,
10109 (errcode(ERRCODE_DATATYPE_MISMATCH),
10110 errmsg("foreign key constraint \"%s\" cannot be implemented",
10111 fkconstraint->conname),
10112 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10113 "are of incompatible types: %s and %s.",
10114 strVal(list_nth(fkconstraint->fk_attrs, i)),
10115 strVal(list_nth(fkconstraint->pk_attrs, i)),
10116 format_type_be(fktype),
10117 format_type_be(pktype))));
10120 * This shouldn't be possible, but better check to make sure we have a
10121 * consistent state for the check below.
10123 if ((OidIsValid(pkcoll) && !OidIsValid(fkcoll)) || (!OidIsValid(pkcoll) && OidIsValid(fkcoll)))
10124 elog(ERROR, "key columns are not both collatable");
10126 if (OidIsValid(pkcoll) && OidIsValid(fkcoll))
10128 bool pkcolldet;
10129 bool fkcolldet;
10131 pkcolldet = get_collation_isdeterministic(pkcoll);
10132 fkcolldet = get_collation_isdeterministic(fkcoll);
10135 * SQL requires that both collations are the same. This is
10136 * because we need a consistent notion of equality on both
10137 * columns. We relax this by allowing different collations if
10138 * they are both deterministic. (This is also for backward
10139 * compatibility, because PostgreSQL has always allowed this.)
10141 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10142 ereport(ERROR,
10143 (errcode(ERRCODE_COLLATION_MISMATCH),
10144 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10145 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10146 "have incompatible collations: \"%s\" and \"%s\". "
10147 "If either collation is nondeterministic, then both collations have to be the same.",
10148 strVal(list_nth(fkconstraint->fk_attrs, i)),
10149 strVal(list_nth(fkconstraint->pk_attrs, i)),
10150 get_collation_name(fkcoll),
10151 get_collation_name(pkcoll))));
10154 if (old_check_ok)
10157 * When a pfeqop changes, revalidate the constraint. We could
10158 * permit intra-opfamily changes, but that adds subtle complexity
10159 * without any concrete benefit for core types. We need not
10160 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10162 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10163 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10164 old_pfeqop_item);
10166 if (old_check_ok)
10168 Oid old_fktype;
10169 Oid new_fktype;
10170 CoercionPathType old_pathtype;
10171 CoercionPathType new_pathtype;
10172 Oid old_castfunc;
10173 Oid new_castfunc;
10174 Oid old_fkcoll;
10175 Oid new_fkcoll;
10176 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10177 fkattnum[i] - 1);
10180 * Identify coercion pathways from each of the old and new FK-side
10181 * column types to the right (foreign) operand type of the pfeqop.
10182 * We may assume that pg_constraint.conkey is not changing.
10184 old_fktype = attr->atttypid;
10185 new_fktype = fktype;
10186 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10187 &old_castfunc);
10188 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10189 &new_castfunc);
10191 old_fkcoll = attr->attcollation;
10192 new_fkcoll = fkcoll;
10195 * Upon a change to the cast from the FK column to its pfeqop
10196 * operand, revalidate the constraint. For this evaluation, a
10197 * binary coercion cast is equivalent to no cast at all. While
10198 * type implementors should design implicit casts with an eye
10199 * toward consistency of operations like equality, we cannot
10200 * assume here that they have done so.
10202 * A function with a polymorphic argument could change behavior
10203 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10204 * when the cast destination is polymorphic, we only avoid
10205 * revalidation if the input type has not changed at all. Given
10206 * just the core data types and operator classes, this requirement
10207 * prevents no would-be optimizations.
10209 * If the cast converts from a base type to a domain thereon, then
10210 * that domain type must be the opcintype of the unique index.
10211 * Necessarily, the primary key column must then be of the domain
10212 * type. Since the constraint was previously valid, all values on
10213 * the foreign side necessarily exist on the primary side and in
10214 * turn conform to the domain. Consequently, we need not treat
10215 * domains specially here.
10217 * If the collation changes, revalidation is required, unless both
10218 * collations are deterministic, because those share the same
10219 * notion of equality (because texteq reduces to bitwise
10220 * equality).
10222 * We need not directly consider the PK type. It's necessarily
10223 * binary coercible to the opcintype of the unique index column,
10224 * and ri_triggers.c will only deal with PK datums in terms of
10225 * that opcintype. Changing the opcintype also changes pfeqop.
10227 old_check_ok = (new_pathtype == old_pathtype &&
10228 new_castfunc == old_castfunc &&
10229 (!IsPolymorphicType(pfeqop_right) ||
10230 new_fktype == old_fktype) &&
10231 (new_fkcoll == old_fkcoll ||
10232 (get_collation_isdeterministic(old_fkcoll) && get_collation_isdeterministic(new_fkcoll))));
10235 pfeqoperators[i] = pfeqop;
10236 ppeqoperators[i] = ppeqop;
10237 ffeqoperators[i] = ffeqop;
10241 * For FKs with PERIOD we need additional operators to check whether the
10242 * referencing row's range is contained by the aggregated ranges of the
10243 * referenced row(s). For rangetypes and multirangetypes this is
10244 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10245 * support for now. FKs will look these up at "runtime", but we should
10246 * make sure the lookup works here, even if we don't use the values.
10248 if (with_period)
10250 Oid periodoperoid;
10251 Oid aggedperiodoperoid;
10253 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10256 /* First, create the constraint catalog entry itself. */
10257 address = addFkConstraint(addFkBothSides,
10258 fkconstraint->conname, fkconstraint, rel, pkrel,
10259 indexOid,
10260 InvalidOid, /* no parent constraint */
10261 numfks,
10262 pkattnum,
10263 fkattnum,
10264 pfeqoperators,
10265 ppeqoperators,
10266 ffeqoperators,
10267 numfkdelsetcols,
10268 fkdelsetcols,
10269 false,
10270 with_period);
10272 /* Next process the action triggers at the referenced side and recurse */
10273 addFkRecurseReferenced(fkconstraint, rel, pkrel,
10274 indexOid,
10275 address.objectId,
10276 numfks,
10277 pkattnum,
10278 fkattnum,
10279 pfeqoperators,
10280 ppeqoperators,
10281 ffeqoperators,
10282 numfkdelsetcols,
10283 fkdelsetcols,
10284 old_check_ok,
10285 InvalidOid, InvalidOid,
10286 with_period);
10288 /* Lastly create the check triggers at the referencing side and recurse */
10289 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10290 indexOid,
10291 address.objectId,
10292 numfks,
10293 pkattnum,
10294 fkattnum,
10295 pfeqoperators,
10296 ppeqoperators,
10297 ffeqoperators,
10298 numfkdelsetcols,
10299 fkdelsetcols,
10300 old_check_ok,
10301 lockmode,
10302 InvalidOid, InvalidOid,
10303 with_period);
10306 * Done. Close pk table, but keep lock until we've committed.
10308 table_close(pkrel, NoLock);
10310 return address;
10314 * validateFkOnDeleteSetColumns
10315 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10316 * column lists are valid.
10318 void
10319 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10320 int numfksetcols, const int16 *fksetcolsattnums,
10321 List *fksetcols)
10323 for (int i = 0; i < numfksetcols; i++)
10325 int16 setcol_attnum = fksetcolsattnums[i];
10326 bool seen = false;
10328 for (int j = 0; j < numfks; j++)
10330 if (fkattnums[j] == setcol_attnum)
10332 seen = true;
10333 break;
10337 if (!seen)
10339 char *col = strVal(list_nth(fksetcols, i));
10341 ereport(ERROR,
10342 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10343 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10349 * addFkConstraint
10350 * Install pg_constraint entries to implement a foreign key constraint.
10351 * Caller must separately invoke addFkRecurseReferenced and
10352 * addFkRecurseReferencing, as appropriate, to install pg_trigger entries
10353 * and (for partitioned tables) recurse to partitions.
10355 * fkside: the side of the FK (or both) to create. Caller should
10356 * call addFkRecurseReferenced if this is addFkReferencedSide,
10357 * addFkRecurseReferencing if it's addFkReferencingSide, or both if it's
10358 * addFkBothSides.
10359 * constraintname: the base name for the constraint being added,
10360 * copied to fkconstraint->conname if the latter is not set
10361 * fkconstraint: the constraint being added
10362 * rel: the root referencing relation
10363 * pkrel: the referenced relation; might be a partition, if recursing
10364 * indexOid: the OID of the index (on pkrel) implementing this constraint
10365 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10366 * top-level constraint
10367 * numfks: the number of columns in the foreign key
10368 * pkattnum: the attnum array of referenced attributes
10369 * fkattnum: the attnum array of referencing attributes
10370 * pf/pp/ffeqoperators: OID array of operators between columns
10371 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10372 * (...) clause
10373 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10374 * NULL/DEFAULT clause
10375 * with_period: true if this is a temporal FK
10377 static ObjectAddress
10378 addFkConstraint(addFkConstraintSides fkside,
10379 char *constraintname, Constraint *fkconstraint,
10380 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
10381 int numfks, int16 *pkattnum,
10382 int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators,
10383 Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols,
10384 bool is_internal, bool with_period)
10386 ObjectAddress address;
10387 Oid constrOid;
10388 char *conname;
10389 bool conislocal;
10390 int16 coninhcount;
10391 bool connoinherit;
10394 * Verify relkind for each referenced partition. At the top level, this
10395 * is redundant with a previous check, but we need it when recursing.
10397 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10398 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10399 ereport(ERROR,
10400 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10401 errmsg("referenced relation \"%s\" is not a table",
10402 RelationGetRelationName(pkrel))));
10405 * Caller supplies us with a constraint name; however, it may be used in
10406 * this partition, so come up with a different one in that case.
10408 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10409 RelationGetRelid(rel),
10410 constraintname))
10411 conname = ChooseConstraintName(RelationGetRelationName(rel),
10412 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10413 "fkey",
10414 RelationGetNamespace(rel), NIL);
10415 else
10416 conname = constraintname;
10418 if (fkconstraint->conname == NULL)
10419 fkconstraint->conname = pstrdup(conname);
10421 if (OidIsValid(parentConstr))
10423 conislocal = false;
10424 coninhcount = 1;
10425 connoinherit = false;
10427 else
10429 conislocal = true;
10430 coninhcount = 0;
10433 * always inherit for partitioned tables, never for legacy inheritance
10435 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10439 * Record the FK constraint in pg_constraint.
10441 constrOid = CreateConstraintEntry(conname,
10442 RelationGetNamespace(rel),
10443 CONSTRAINT_FOREIGN,
10444 fkconstraint->deferrable,
10445 fkconstraint->initdeferred,
10446 true, /* Is Enforced */
10447 fkconstraint->initially_valid,
10448 parentConstr,
10449 RelationGetRelid(rel),
10450 fkattnum,
10451 numfks,
10452 numfks,
10453 InvalidOid, /* not a domain constraint */
10454 indexOid,
10455 RelationGetRelid(pkrel),
10456 pkattnum,
10457 pfeqoperators,
10458 ppeqoperators,
10459 ffeqoperators,
10460 numfks,
10461 fkconstraint->fk_upd_action,
10462 fkconstraint->fk_del_action,
10463 fkdelsetcols,
10464 numfkdelsetcols,
10465 fkconstraint->fk_matchtype,
10466 NULL, /* no exclusion constraint */
10467 NULL, /* no check constraint */
10468 NULL,
10469 conislocal, /* islocal */
10470 coninhcount, /* inhcount */
10471 connoinherit, /* conNoInherit */
10472 with_period, /* conPeriod */
10473 is_internal); /* is_internal */
10475 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10478 * In partitioning cases, create the dependency entries for this
10479 * constraint. (For non-partitioned cases, relevant entries were created
10480 * by CreateConstraintEntry.)
10482 * On the referenced side, we need the constraint to have an internal
10483 * dependency on its parent constraint; this means that this constraint
10484 * cannot be dropped on its own -- only through the parent constraint. It
10485 * also means the containing partition cannot be dropped on its own, but
10486 * it can be detached, at which point this dependency is removed (after
10487 * verifying that no rows are referenced via this FK.)
10489 * When processing the referencing side, we link the constraint via the
10490 * special partitioning dependencies: the parent constraint is the primary
10491 * dependent, and the partition on which the foreign key exists is the
10492 * secondary dependency. That way, this constraint is dropped if either
10493 * of these objects is.
10495 * Note that this is only necessary for the subsidiary pg_constraint rows
10496 * in partitions; the topmost row doesn't need any of this.
10498 if (OidIsValid(parentConstr))
10500 ObjectAddress referenced;
10502 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10504 Assert(fkside != addFkBothSides);
10505 if (fkside == addFkReferencedSide)
10506 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10507 else
10509 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10510 ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel));
10511 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10515 /* make new constraint visible, in case we add more */
10516 CommandCounterIncrement();
10518 return address;
10522 * addFkRecurseReferenced
10523 * Recursive helper for the referenced side of foreign key creation,
10524 * which creates the action triggers and recurses
10526 * If the referenced relation is a plain relation, create the necessary action
10527 * triggers that implement the constraint. If the referenced relation is a
10528 * partitioned table, then we create a pg_constraint row referencing the parent
10529 * of the referencing side for it and recurse on this routine for each
10530 * partition.
10532 * fkconstraint: the constraint being added
10533 * rel: the root referencing relation
10534 * pkrel: the referenced relation; might be a partition, if recursing
10535 * indexOid: the OID of the index (on pkrel) implementing this constraint
10536 * parentConstr: the OID of a parent constraint; InvalidOid if this is a
10537 * top-level constraint
10538 * numfks: the number of columns in the foreign key
10539 * pkattnum: the attnum array of referenced attributes
10540 * fkattnum: the attnum array of referencing attributes
10541 * numfkdelsetcols: the number of columns in the ON DELETE SET
10542 * NULL/DEFAULT (...) clause
10543 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10544 * NULL/DEFAULT clause
10545 * pf/pp/ffeqoperators: OID array of operators between columns
10546 * old_check_ok: true if this constraint replaces an existing one that
10547 * was already validated (thus this one doesn't need validation)
10548 * parentDelTrigger and parentUpdTrigger: when recursively called on a
10549 * partition, the OIDs of the parent action triggers for DELETE and
10550 * UPDATE respectively.
10551 * with_period: true if this is a temporal FK
10553 static void
10554 addFkRecurseReferenced(Constraint *fkconstraint, Relation rel,
10555 Relation pkrel, Oid indexOid, Oid parentConstr,
10556 int numfks,
10557 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10558 Oid *ppeqoperators, Oid *ffeqoperators,
10559 int numfkdelsetcols, int16 *fkdelsetcols,
10560 bool old_check_ok,
10561 Oid parentDelTrigger, Oid parentUpdTrigger,
10562 bool with_period)
10564 Oid deleteTriggerOid,
10565 updateTriggerOid;
10567 Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10568 Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10571 * Create the action triggers that enforce the constraint.
10573 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10574 fkconstraint,
10575 parentConstr, indexOid,
10576 parentDelTrigger, parentUpdTrigger,
10577 &deleteTriggerOid, &updateTriggerOid);
10580 * If the referenced table is partitioned, recurse on ourselves to handle
10581 * each partition. We need one pg_constraint row created for each
10582 * partition in addition to the pg_constraint row for the parent table.
10584 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10586 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10588 for (int i = 0; i < pd->nparts; i++)
10590 Relation partRel;
10591 AttrMap *map;
10592 AttrNumber *mapped_pkattnum;
10593 Oid partIndexId;
10594 ObjectAddress address;
10596 /* XXX would it be better to acquire these locks beforehand? */
10597 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10600 * Map the attribute numbers in the referenced side of the FK
10601 * definition to match the partition's column layout.
10603 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10604 RelationGetDescr(pkrel),
10605 false);
10606 if (map)
10608 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10609 for (int j = 0; j < numfks; j++)
10610 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10612 else
10613 mapped_pkattnum = pkattnum;
10615 /* Determine the index to use at this level */
10616 partIndexId = index_get_partition(partRel, indexOid);
10617 if (!OidIsValid(partIndexId))
10618 elog(ERROR, "index for %u not found in partition %s",
10619 indexOid, RelationGetRelationName(partRel));
10621 /* Create entry at this level ... */
10622 address = addFkConstraint(addFkReferencedSide,
10623 fkconstraint->conname, fkconstraint, rel,
10624 partRel, partIndexId, parentConstr,
10625 numfks, mapped_pkattnum,
10626 fkattnum, pfeqoperators, ppeqoperators,
10627 ffeqoperators, numfkdelsetcols,
10628 fkdelsetcols, true, with_period);
10629 /* ... and recurse to our children */
10630 addFkRecurseReferenced(fkconstraint, rel, partRel,
10631 partIndexId, address.objectId, numfks,
10632 mapped_pkattnum, fkattnum,
10633 pfeqoperators, ppeqoperators, ffeqoperators,
10634 numfkdelsetcols, fkdelsetcols,
10635 old_check_ok,
10636 deleteTriggerOid, updateTriggerOid,
10637 with_period);
10639 /* Done -- clean up (but keep the lock) */
10640 table_close(partRel, NoLock);
10641 if (map)
10643 pfree(mapped_pkattnum);
10644 free_attrmap(map);
10651 * addFkRecurseReferencing
10652 * Recursive helper for the referencing side of foreign key creation,
10653 * which creates the check triggers and recurses
10655 * If the referencing relation is a plain relation, create the necessary check
10656 * triggers that implement the constraint, and set up for Phase 3 constraint
10657 * verification. If the referencing relation is a partitioned table, then
10658 * we create a pg_constraint row for it and recurse on this routine for each
10659 * partition.
10661 * We assume that the referenced relation is locked against concurrent
10662 * deletions. If it's a partitioned relation, every partition must be so
10663 * locked.
10665 * wqueue: the ALTER TABLE work queue; NULL when not running as part
10666 * of an ALTER TABLE sequence.
10667 * fkconstraint: the constraint being added
10668 * rel: the referencing relation; might be a partition, if recursing
10669 * pkrel: the root referenced relation
10670 * indexOid: the OID of the index (on pkrel) implementing this constraint
10671 * parentConstr: the OID of the parent constraint (there is always one)
10672 * numfks: the number of columns in the foreign key
10673 * pkattnum: the attnum array of referenced attributes
10674 * fkattnum: the attnum array of referencing attributes
10675 * pf/pp/ffeqoperators: OID array of operators between columns
10676 * numfkdelsetcols: the number of columns in the ON DELETE SET NULL/DEFAULT
10677 * (...) clause
10678 * fkdelsetcols: the attnum array of the columns in the ON DELETE SET
10679 * NULL/DEFAULT clause
10680 * old_check_ok: true if this constraint replaces an existing one that
10681 * was already validated (thus this one doesn't need validation)
10682 * lockmode: the lockmode to acquire on partitions when recursing
10683 * parentInsTrigger and parentUpdTrigger: when being recursively called on
10684 * a partition, the OIDs of the parent check triggers for INSERT and
10685 * UPDATE respectively.
10686 * with_period: true if this is a temporal FK
10688 static void
10689 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10690 Relation pkrel, Oid indexOid, Oid parentConstr,
10691 int numfks, int16 *pkattnum, int16 *fkattnum,
10692 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10693 int numfkdelsetcols, int16 *fkdelsetcols,
10694 bool old_check_ok, LOCKMODE lockmode,
10695 Oid parentInsTrigger, Oid parentUpdTrigger,
10696 bool with_period)
10698 Oid insertTriggerOid,
10699 updateTriggerOid;
10701 Assert(OidIsValid(parentConstr));
10702 Assert(CheckRelationLockedByMe(rel, ShareRowExclusiveLock, true));
10703 Assert(CheckRelationLockedByMe(pkrel, ShareRowExclusiveLock, true));
10705 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10706 ereport(ERROR,
10707 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10708 errmsg("foreign key constraints are not supported on foreign tables")));
10711 * Add the check triggers to it and, if necessary, schedule it to be
10712 * checked in Phase 3.
10714 * If the relation is partitioned, drill down to do it to its partitions.
10716 createForeignKeyCheckTriggers(RelationGetRelid(rel),
10717 RelationGetRelid(pkrel),
10718 fkconstraint,
10719 parentConstr,
10720 indexOid,
10721 parentInsTrigger, parentUpdTrigger,
10722 &insertTriggerOid, &updateTriggerOid);
10724 if (rel->rd_rel->relkind == RELKIND_RELATION)
10727 * Tell Phase 3 to check that the constraint is satisfied by existing
10728 * rows. We can skip this during table creation, when requested
10729 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10730 * and when we're recreating a constraint following a SET DATA TYPE
10731 * operation that did not impugn its validity.
10733 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10735 NewConstraint *newcon;
10736 AlteredTableInfo *tab;
10738 tab = ATGetQueueEntry(wqueue, rel);
10740 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10741 newcon->name = get_constraint_name(parentConstr);
10742 newcon->contype = CONSTR_FOREIGN;
10743 newcon->refrelid = RelationGetRelid(pkrel);
10744 newcon->refindid = indexOid;
10745 newcon->conid = parentConstr;
10746 newcon->conwithperiod = fkconstraint->fk_with_period;
10747 newcon->qual = (Node *) fkconstraint;
10749 tab->constraints = lappend(tab->constraints, newcon);
10752 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10754 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10755 Relation trigrel;
10758 * Triggers of the foreign keys will be manipulated a bunch of times
10759 * in the loop below. To avoid repeatedly opening/closing the trigger
10760 * catalog relation, we open it here and pass it to the subroutines
10761 * called below.
10763 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10766 * Recurse to take appropriate action on each partition; either we
10767 * find an existing constraint to reparent to ours, or we create a new
10768 * one.
10770 for (int i = 0; i < pd->nparts; i++)
10772 Oid partitionId = pd->oids[i];
10773 Relation partition = table_open(partitionId, lockmode);
10774 List *partFKs;
10775 AttrMap *attmap;
10776 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10777 bool attached;
10778 ObjectAddress address;
10779 ListCell *cell;
10781 CheckAlterTableIsSafe(partition);
10783 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10784 RelationGetDescr(rel),
10785 false);
10786 for (int j = 0; j < numfks; j++)
10787 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10789 /* Check whether an existing constraint can be repurposed */
10790 partFKs = copyObject(RelationGetFKeyList(partition));
10791 attached = false;
10792 foreach(cell, partFKs)
10794 ForeignKeyCacheInfo *fk;
10796 fk = lfirst_node(ForeignKeyCacheInfo, cell);
10797 if (tryAttachPartitionForeignKey(fk,
10798 partitionId,
10799 parentConstr,
10800 numfks,
10801 mapped_fkattnum,
10802 pkattnum,
10803 pfeqoperators,
10804 insertTriggerOid,
10805 updateTriggerOid,
10806 trigrel))
10808 attached = true;
10809 break;
10812 if (attached)
10814 table_close(partition, NoLock);
10815 continue;
10819 * No luck finding a good constraint to reuse; create our own.
10821 address = addFkConstraint(addFkReferencingSide,
10822 fkconstraint->conname, fkconstraint,
10823 partition, pkrel, indexOid, parentConstr,
10824 numfks, pkattnum,
10825 mapped_fkattnum, pfeqoperators,
10826 ppeqoperators, ffeqoperators,
10827 numfkdelsetcols, fkdelsetcols, true,
10828 with_period);
10830 /* call ourselves to finalize the creation and we're done */
10831 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10832 indexOid,
10833 address.objectId,
10834 numfks,
10835 pkattnum,
10836 mapped_fkattnum,
10837 pfeqoperators,
10838 ppeqoperators,
10839 ffeqoperators,
10840 numfkdelsetcols,
10841 fkdelsetcols,
10842 old_check_ok,
10843 lockmode,
10844 insertTriggerOid,
10845 updateTriggerOid,
10846 with_period);
10848 table_close(partition, NoLock);
10851 table_close(trigrel, RowExclusiveLock);
10856 * CloneForeignKeyConstraints
10857 * Clone foreign keys from a partitioned table to a newly acquired
10858 * partition.
10860 * partitionRel is a partition of parentRel, so we can be certain that it has
10861 * the same columns with the same datatypes. The columns may be in different
10862 * order, though.
10864 * wqueue must be passed to set up phase 3 constraint checking, unless the
10865 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10866 * PARTITION OF).
10868 static void
10869 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10870 Relation partitionRel)
10872 /* This only works for declarative partitioning */
10873 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10876 * Clone constraints for which the parent is on the referenced side.
10878 CloneFkReferenced(parentRel, partitionRel);
10881 * Now clone constraints where the parent is on the referencing side.
10883 CloneFkReferencing(wqueue, parentRel, partitionRel);
10887 * CloneFkReferenced
10888 * Subroutine for CloneForeignKeyConstraints
10890 * Find all the FKs that have the parent relation on the referenced side;
10891 * clone those constraints to the given partition. This is to be called
10892 * when the partition is being created or attached.
10894 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10896 * This recurses to partitions, if the relation being attached is partitioned.
10897 * Recursion is done by calling addFkRecurseReferenced.
10899 static void
10900 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10902 Relation pg_constraint;
10903 AttrMap *attmap;
10904 ListCell *cell;
10905 SysScanDesc scan;
10906 ScanKeyData key[2];
10907 HeapTuple tuple;
10908 List *clone = NIL;
10909 Relation trigrel;
10912 * Search for any constraints where this partition's parent is in the
10913 * referenced side. However, we must not clone any constraint whose
10914 * parent constraint is also going to be cloned, to avoid duplicates. So
10915 * do it in two steps: first construct the list of constraints to clone,
10916 * then go over that list cloning those whose parents are not in the list.
10917 * (We must not rely on the parent being seen first, since the catalog
10918 * scan could return children first.)
10920 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10921 ScanKeyInit(&key[0],
10922 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10923 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10924 ScanKeyInit(&key[1],
10925 Anum_pg_constraint_contype, BTEqualStrategyNumber,
10926 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10927 /* This is a seqscan, as we don't have a usable index ... */
10928 scan = systable_beginscan(pg_constraint, InvalidOid, true,
10929 NULL, 2, key);
10930 while ((tuple = systable_getnext(scan)) != NULL)
10932 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10934 clone = lappend_oid(clone, constrForm->oid);
10936 systable_endscan(scan);
10937 table_close(pg_constraint, RowShareLock);
10940 * Triggers of the foreign keys will be manipulated a bunch of times in
10941 * the loop below. To avoid repeatedly opening/closing the trigger
10942 * catalog relation, we open it here and pass it to the subroutines called
10943 * below.
10945 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10947 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10948 RelationGetDescr(parentRel),
10949 false);
10950 foreach(cell, clone)
10952 Oid constrOid = lfirst_oid(cell);
10953 Form_pg_constraint constrForm;
10954 Relation fkRel;
10955 Oid indexOid;
10956 Oid partIndexId;
10957 int numfks;
10958 AttrNumber conkey[INDEX_MAX_KEYS];
10959 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10960 AttrNumber confkey[INDEX_MAX_KEYS];
10961 Oid conpfeqop[INDEX_MAX_KEYS];
10962 Oid conppeqop[INDEX_MAX_KEYS];
10963 Oid conffeqop[INDEX_MAX_KEYS];
10964 int numfkdelsetcols;
10965 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10966 Constraint *fkconstraint;
10967 ObjectAddress address;
10968 Oid deleteTriggerOid,
10969 updateTriggerOid;
10971 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10972 if (!HeapTupleIsValid(tuple))
10973 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10974 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10977 * As explained above: don't try to clone a constraint for which we're
10978 * going to clone the parent.
10980 if (list_member_oid(clone, constrForm->conparentid))
10982 ReleaseSysCache(tuple);
10983 continue;
10987 * Don't clone self-referencing foreign keys, which can be in the
10988 * partitioned table or in the partition-to-be.
10990 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10991 constrForm->conrelid == RelationGetRelid(partitionRel))
10993 ReleaseSysCache(tuple);
10994 continue;
10997 /* We need the same lock level that CreateTrigger will acquire */
10998 fkRel = table_open(constrForm->conrelid, ShareRowExclusiveLock);
11000 indexOid = constrForm->conindid;
11001 DeconstructFkConstraintRow(tuple,
11002 &numfks,
11003 conkey,
11004 confkey,
11005 conpfeqop,
11006 conppeqop,
11007 conffeqop,
11008 &numfkdelsetcols,
11009 confdelsetcols);
11011 for (int i = 0; i < numfks; i++)
11012 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11014 fkconstraint = makeNode(Constraint);
11015 fkconstraint->contype = CONSTRAINT_FOREIGN;
11016 fkconstraint->conname = NameStr(constrForm->conname);
11017 fkconstraint->deferrable = constrForm->condeferrable;
11018 fkconstraint->initdeferred = constrForm->condeferred;
11019 fkconstraint->location = -1;
11020 fkconstraint->pktable = NULL;
11021 /* ->fk_attrs determined below */
11022 fkconstraint->pk_attrs = NIL;
11023 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11024 fkconstraint->fk_upd_action = constrForm->confupdtype;
11025 fkconstraint->fk_del_action = constrForm->confdeltype;
11026 fkconstraint->fk_del_set_cols = NIL;
11027 fkconstraint->old_conpfeqop = NIL;
11028 fkconstraint->old_pktable_oid = InvalidOid;
11029 fkconstraint->skip_validation = false;
11030 fkconstraint->initially_valid = true;
11032 /* set up colnames that are used to generate the constraint name */
11033 for (int i = 0; i < numfks; i++)
11035 Form_pg_attribute att;
11037 att = TupleDescAttr(RelationGetDescr(fkRel),
11038 conkey[i] - 1);
11039 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11040 makeString(NameStr(att->attname)));
11044 * Add the new foreign key constraint pointing to the new partition.
11045 * Because this new partition appears in the referenced side of the
11046 * constraint, we don't need to set up for Phase 3 check.
11048 partIndexId = index_get_partition(partitionRel, indexOid);
11049 if (!OidIsValid(partIndexId))
11050 elog(ERROR, "index for %u not found in partition %s",
11051 indexOid, RelationGetRelationName(partitionRel));
11054 * Get the "action" triggers belonging to the constraint to pass as
11055 * parent OIDs for similar triggers that will be created on the
11056 * partition in addFkRecurseReferenced().
11058 GetForeignKeyActionTriggers(trigrel, constrOid,
11059 constrForm->confrelid, constrForm->conrelid,
11060 &deleteTriggerOid, &updateTriggerOid);
11062 /* Add this constraint ... */
11063 address = addFkConstraint(addFkReferencedSide,
11064 fkconstraint->conname, fkconstraint, fkRel,
11065 partitionRel, partIndexId, constrOid,
11066 numfks, mapped_confkey,
11067 conkey, conpfeqop, conppeqop, conffeqop,
11068 numfkdelsetcols, confdelsetcols, false,
11069 constrForm->conperiod);
11070 /* ... and recurse */
11071 addFkRecurseReferenced(fkconstraint,
11072 fkRel,
11073 partitionRel,
11074 partIndexId,
11075 address.objectId,
11076 numfks,
11077 mapped_confkey,
11078 conkey,
11079 conpfeqop,
11080 conppeqop,
11081 conffeqop,
11082 numfkdelsetcols,
11083 confdelsetcols,
11084 true,
11085 deleteTriggerOid,
11086 updateTriggerOid,
11087 constrForm->conperiod);
11089 table_close(fkRel, NoLock);
11090 ReleaseSysCache(tuple);
11093 table_close(trigrel, RowExclusiveLock);
11097 * CloneFkReferencing
11098 * Subroutine for CloneForeignKeyConstraints
11100 * For each FK constraint of the parent relation in the given list, find an
11101 * equivalent constraint in its partition relation that can be reparented;
11102 * if one cannot be found, create a new constraint in the partition as its
11103 * child.
11105 * If wqueue is given, it is used to set up phase-3 verification for each
11106 * cloned constraint; omit it if such verification is not needed
11107 * (example: the partition is being created anew).
11109 static void
11110 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11112 AttrMap *attmap;
11113 List *partFKs;
11114 List *clone = NIL;
11115 ListCell *cell;
11116 Relation trigrel;
11118 /* obtain a list of constraints that we need to clone */
11119 foreach(cell, RelationGetFKeyList(parentRel))
11121 ForeignKeyCacheInfo *fk = lfirst(cell);
11124 * Refuse to attach a table as partition that this partitioned table
11125 * already has a foreign key to. This isn't useful schema, which is
11126 * proven by the fact that there have been no user complaints that
11127 * it's already impossible to achieve this in the opposite direction,
11128 * i.e., creating a foreign key that references a partition. This
11129 * restriction allows us to dodge some complexities around
11130 * pg_constraint and pg_trigger row creations that would be needed
11131 * during ATTACH/DETACH for this kind of relationship.
11133 if (fk->confrelid == RelationGetRelid(partRel))
11134 ereport(ERROR,
11135 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11136 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11137 RelationGetRelationName(partRel),
11138 get_constraint_name(fk->conoid))));
11140 clone = lappend_oid(clone, fk->conoid);
11144 * Silently do nothing if there's nothing to do. In particular, this
11145 * avoids throwing a spurious error for foreign tables.
11147 if (clone == NIL)
11148 return;
11150 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11151 ereport(ERROR,
11152 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11153 errmsg("foreign key constraints are not supported on foreign tables")));
11156 * Triggers of the foreign keys will be manipulated a bunch of times in
11157 * the loop below. To avoid repeatedly opening/closing the trigger
11158 * catalog relation, we open it here and pass it to the subroutines called
11159 * below.
11161 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11164 * The constraint key may differ, if the columns in the partition are
11165 * different. This map is used to convert them.
11167 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11168 RelationGetDescr(parentRel),
11169 false);
11171 partFKs = copyObject(RelationGetFKeyList(partRel));
11173 foreach(cell, clone)
11175 Oid parentConstrOid = lfirst_oid(cell);
11176 Form_pg_constraint constrForm;
11177 Relation pkrel;
11178 HeapTuple tuple;
11179 int numfks;
11180 AttrNumber conkey[INDEX_MAX_KEYS];
11181 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11182 AttrNumber confkey[INDEX_MAX_KEYS];
11183 Oid conpfeqop[INDEX_MAX_KEYS];
11184 Oid conppeqop[INDEX_MAX_KEYS];
11185 Oid conffeqop[INDEX_MAX_KEYS];
11186 int numfkdelsetcols;
11187 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11188 Constraint *fkconstraint;
11189 bool attached;
11190 Oid indexOid;
11191 ObjectAddress address;
11192 ListCell *lc;
11193 Oid insertTriggerOid,
11194 updateTriggerOid;
11195 bool with_period;
11197 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11198 if (!HeapTupleIsValid(tuple))
11199 elog(ERROR, "cache lookup failed for constraint %u",
11200 parentConstrOid);
11201 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11203 /* Don't clone constraints whose parents are being cloned */
11204 if (list_member_oid(clone, constrForm->conparentid))
11206 ReleaseSysCache(tuple);
11207 continue;
11211 * Need to prevent concurrent deletions. If pkrel is a partitioned
11212 * relation, that means to lock all partitions.
11214 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11215 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11216 (void) find_all_inheritors(RelationGetRelid(pkrel),
11217 ShareRowExclusiveLock, NULL);
11219 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11220 conpfeqop, conppeqop, conffeqop,
11221 &numfkdelsetcols, confdelsetcols);
11222 for (int i = 0; i < numfks; i++)
11223 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11226 * Get the "check" triggers belonging to the constraint to pass as
11227 * parent OIDs for similar triggers that will be created on the
11228 * partition in addFkRecurseReferencing(). They are also passed to
11229 * tryAttachPartitionForeignKey() below to simply assign as parents to
11230 * the partition's existing "check" triggers, that is, if the
11231 * corresponding constraints is deemed attachable to the parent
11232 * constraint.
11234 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11235 constrForm->confrelid, constrForm->conrelid,
11236 &insertTriggerOid, &updateTriggerOid);
11239 * Before creating a new constraint, see whether any existing FKs are
11240 * fit for the purpose. If one is, attach the parent constraint to
11241 * it, and don't clone anything. This way we avoid the expensive
11242 * verification step and don't end up with a duplicate FK, and we
11243 * don't need to recurse to partitions for this constraint.
11245 attached = false;
11246 foreach(lc, partFKs)
11248 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11250 if (tryAttachPartitionForeignKey(fk,
11251 RelationGetRelid(partRel),
11252 parentConstrOid,
11253 numfks,
11254 mapped_conkey,
11255 confkey,
11256 conpfeqop,
11257 insertTriggerOid,
11258 updateTriggerOid,
11259 trigrel))
11261 attached = true;
11262 table_close(pkrel, NoLock);
11263 break;
11266 if (attached)
11268 ReleaseSysCache(tuple);
11269 continue;
11272 /* No dice. Set up to create our own constraint */
11273 fkconstraint = makeNode(Constraint);
11274 fkconstraint->contype = CONSTRAINT_FOREIGN;
11275 /* ->conname determined below */
11276 fkconstraint->deferrable = constrForm->condeferrable;
11277 fkconstraint->initdeferred = constrForm->condeferred;
11278 fkconstraint->location = -1;
11279 fkconstraint->pktable = NULL;
11280 /* ->fk_attrs determined below */
11281 fkconstraint->pk_attrs = NIL;
11282 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11283 fkconstraint->fk_upd_action = constrForm->confupdtype;
11284 fkconstraint->fk_del_action = constrForm->confdeltype;
11285 fkconstraint->fk_del_set_cols = NIL;
11286 fkconstraint->old_conpfeqop = NIL;
11287 fkconstraint->old_pktable_oid = InvalidOid;
11288 fkconstraint->skip_validation = false;
11289 fkconstraint->initially_valid = constrForm->convalidated;
11290 for (int i = 0; i < numfks; i++)
11292 Form_pg_attribute att;
11294 att = TupleDescAttr(RelationGetDescr(partRel),
11295 mapped_conkey[i] - 1);
11296 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11297 makeString(NameStr(att->attname)));
11300 indexOid = constrForm->conindid;
11301 with_period = constrForm->conperiod;
11303 /* Create the pg_constraint entry at this level */
11304 address = addFkConstraint(addFkReferencingSide,
11305 NameStr(constrForm->conname), fkconstraint,
11306 partRel, pkrel, indexOid, parentConstrOid,
11307 numfks, confkey,
11308 mapped_conkey, conpfeqop,
11309 conppeqop, conffeqop,
11310 numfkdelsetcols, confdelsetcols,
11311 false, with_period);
11313 /* Done with the cloned constraint's tuple */
11314 ReleaseSysCache(tuple);
11316 /* Create the check triggers, and recurse to partitions, if any */
11317 addFkRecurseReferencing(wqueue,
11318 fkconstraint,
11319 partRel,
11320 pkrel,
11321 indexOid,
11322 address.objectId,
11323 numfks,
11324 confkey,
11325 mapped_conkey,
11326 conpfeqop,
11327 conppeqop,
11328 conffeqop,
11329 numfkdelsetcols,
11330 confdelsetcols,
11331 false, /* no old check exists */
11332 AccessExclusiveLock,
11333 insertTriggerOid,
11334 updateTriggerOid,
11335 with_period);
11336 table_close(pkrel, NoLock);
11339 table_close(trigrel, RowExclusiveLock);
11343 * When the parent of a partition receives [the referencing side of] a foreign
11344 * key, we must propagate that foreign key to the partition. However, the
11345 * partition might already have an equivalent foreign key; this routine
11346 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11347 * by the other parameters. If they are equivalent, create the link between
11348 * the two constraints and return true.
11350 * If the given FK does not match the one defined by rest of the params,
11351 * return false.
11353 static bool
11354 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11355 Oid partRelid,
11356 Oid parentConstrOid,
11357 int numfks,
11358 AttrNumber *mapped_conkey,
11359 AttrNumber *confkey,
11360 Oid *conpfeqop,
11361 Oid parentInsTrigger,
11362 Oid parentUpdTrigger,
11363 Relation trigrel)
11365 HeapTuple parentConstrTup;
11366 Form_pg_constraint parentConstr;
11367 HeapTuple partcontup;
11368 Form_pg_constraint partConstr;
11369 ScanKeyData key;
11370 SysScanDesc scan;
11371 HeapTuple trigtup;
11372 Oid insertTriggerOid,
11373 updateTriggerOid;
11375 parentConstrTup = SearchSysCache1(CONSTROID,
11376 ObjectIdGetDatum(parentConstrOid));
11377 if (!HeapTupleIsValid(parentConstrTup))
11378 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11379 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11382 * Do some quick & easy initial checks. If any of these fail, we cannot
11383 * use this constraint.
11385 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11387 ReleaseSysCache(parentConstrTup);
11388 return false;
11390 for (int i = 0; i < numfks; i++)
11392 if (fk->conkey[i] != mapped_conkey[i] ||
11393 fk->confkey[i] != confkey[i] ||
11394 fk->conpfeqop[i] != conpfeqop[i])
11396 ReleaseSysCache(parentConstrTup);
11397 return false;
11402 * Looks good so far; do some more extensive checks. Presumably the check
11403 * for 'convalidated' could be dropped, since we don't really care about
11404 * that, but let's be careful for now.
11406 partcontup = SearchSysCache1(CONSTROID,
11407 ObjectIdGetDatum(fk->conoid));
11408 if (!HeapTupleIsValid(partcontup))
11409 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11410 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11411 if (OidIsValid(partConstr->conparentid) ||
11412 !partConstr->convalidated ||
11413 partConstr->condeferrable != parentConstr->condeferrable ||
11414 partConstr->condeferred != parentConstr->condeferred ||
11415 partConstr->confupdtype != parentConstr->confupdtype ||
11416 partConstr->confdeltype != parentConstr->confdeltype ||
11417 partConstr->confmatchtype != parentConstr->confmatchtype)
11419 ReleaseSysCache(parentConstrTup);
11420 ReleaseSysCache(partcontup);
11421 return false;
11424 ReleaseSysCache(partcontup);
11425 ReleaseSysCache(parentConstrTup);
11428 * Looks good! Attach this constraint. The action triggers in the new
11429 * partition become redundant -- the parent table already has equivalent
11430 * ones, and those will be able to reach the partition. Remove the ones
11431 * in the partition. We identify them because they have our constraint
11432 * OID, as well as being on the referenced rel.
11434 ScanKeyInit(&key,
11435 Anum_pg_trigger_tgconstraint,
11436 BTEqualStrategyNumber, F_OIDEQ,
11437 ObjectIdGetDatum(fk->conoid));
11438 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11439 NULL, 1, &key);
11440 while ((trigtup = systable_getnext(scan)) != NULL)
11442 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11443 ObjectAddress trigger;
11445 if (trgform->tgconstrrelid != fk->conrelid)
11446 continue;
11447 if (trgform->tgrelid != fk->confrelid)
11448 continue;
11451 * The constraint is originally set up to contain this trigger as an
11452 * implementation object, so there's a dependency record that links
11453 * the two; however, since the trigger is no longer needed, we remove
11454 * the dependency link in order to be able to drop the trigger while
11455 * keeping the constraint intact.
11457 deleteDependencyRecordsFor(TriggerRelationId,
11458 trgform->oid,
11459 false);
11460 /* make dependency deletion visible to performDeletion */
11461 CommandCounterIncrement();
11462 ObjectAddressSet(trigger, TriggerRelationId,
11463 trgform->oid);
11464 performDeletion(&trigger, DROP_RESTRICT, 0);
11465 /* make trigger drop visible, in case the loop iterates */
11466 CommandCounterIncrement();
11469 systable_endscan(scan);
11471 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11474 * Like the constraint, attach partition's "check" triggers to the
11475 * corresponding parent triggers.
11477 GetForeignKeyCheckTriggers(trigrel,
11478 fk->conoid, fk->confrelid, fk->conrelid,
11479 &insertTriggerOid, &updateTriggerOid);
11480 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11481 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11482 partRelid);
11483 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11484 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11485 partRelid);
11488 * If the referenced table is partitioned, then the partition we're
11489 * attaching now has extra pg_constraint rows and action triggers that are
11490 * no longer needed. Remove those.
11492 if (get_rel_relkind(fk->confrelid) == RELKIND_PARTITIONED_TABLE)
11494 Relation pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11495 ObjectAddresses *objs;
11496 HeapTuple consttup;
11498 ScanKeyInit(&key,
11499 Anum_pg_constraint_conrelid,
11500 BTEqualStrategyNumber, F_OIDEQ,
11501 ObjectIdGetDatum(fk->conrelid));
11503 scan = systable_beginscan(pg_constraint,
11504 ConstraintRelidTypidNameIndexId,
11505 true, NULL, 1, &key);
11506 objs = new_object_addresses();
11507 while ((consttup = systable_getnext(scan)) != NULL)
11509 Form_pg_constraint conform = (Form_pg_constraint) GETSTRUCT(consttup);
11511 if (conform->conparentid != fk->conoid)
11512 continue;
11513 else
11515 ObjectAddress addr;
11516 SysScanDesc scan2;
11517 ScanKeyData key2;
11518 int n PG_USED_FOR_ASSERTS_ONLY;
11520 ObjectAddressSet(addr, ConstraintRelationId, conform->oid);
11521 add_exact_object_address(&addr, objs);
11524 * First we must delete the dependency record that binds the
11525 * constraint records together.
11527 n = deleteDependencyRecordsForSpecific(ConstraintRelationId,
11528 conform->oid,
11529 DEPENDENCY_INTERNAL,
11530 ConstraintRelationId,
11531 fk->conoid);
11532 Assert(n == 1); /* actually only one is expected */
11535 * Now search for the triggers for this constraint and set
11536 * them up for deletion too
11538 ScanKeyInit(&key2,
11539 Anum_pg_trigger_tgconstraint,
11540 BTEqualStrategyNumber, F_OIDEQ,
11541 ObjectIdGetDatum(conform->oid));
11542 scan2 = systable_beginscan(trigrel, TriggerConstraintIndexId,
11543 true, NULL, 1, &key2);
11544 while ((trigtup = systable_getnext(scan2)) != NULL)
11546 ObjectAddressSet(addr, TriggerRelationId,
11547 ((Form_pg_trigger) GETSTRUCT(trigtup))->oid);
11548 add_exact_object_address(&addr, objs);
11550 systable_endscan(scan2);
11553 /* make the dependency deletions visible */
11554 CommandCounterIncrement();
11555 performMultipleDeletions(objs, DROP_RESTRICT,
11556 PERFORM_DELETION_INTERNAL);
11557 systable_endscan(scan);
11559 table_close(pg_constraint, RowShareLock);
11562 CommandCounterIncrement();
11563 return true;
11567 * GetForeignKeyActionTriggers
11568 * Returns delete and update "action" triggers of the given relation
11569 * belonging to the given constraint
11571 static void
11572 GetForeignKeyActionTriggers(Relation trigrel,
11573 Oid conoid, Oid confrelid, Oid conrelid,
11574 Oid *deleteTriggerOid,
11575 Oid *updateTriggerOid)
11577 ScanKeyData key;
11578 SysScanDesc scan;
11579 HeapTuple trigtup;
11581 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11582 ScanKeyInit(&key,
11583 Anum_pg_trigger_tgconstraint,
11584 BTEqualStrategyNumber, F_OIDEQ,
11585 ObjectIdGetDatum(conoid));
11587 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11588 NULL, 1, &key);
11589 while ((trigtup = systable_getnext(scan)) != NULL)
11591 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11593 if (trgform->tgconstrrelid != conrelid)
11594 continue;
11595 if (trgform->tgrelid != confrelid)
11596 continue;
11597 /* Only ever look at "action" triggers on the PK side. */
11598 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11599 continue;
11600 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11602 Assert(*deleteTriggerOid == InvalidOid);
11603 *deleteTriggerOid = trgform->oid;
11605 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11607 Assert(*updateTriggerOid == InvalidOid);
11608 *updateTriggerOid = trgform->oid;
11610 #ifndef USE_ASSERT_CHECKING
11611 /* In an assert-enabled build, continue looking to find duplicates */
11612 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11613 break;
11614 #endif
11617 if (!OidIsValid(*deleteTriggerOid))
11618 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11619 conoid);
11620 if (!OidIsValid(*updateTriggerOid))
11621 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11622 conoid);
11624 systable_endscan(scan);
11628 * GetForeignKeyCheckTriggers
11629 * Returns insert and update "check" triggers of the given relation
11630 * belonging to the given constraint
11632 static void
11633 GetForeignKeyCheckTriggers(Relation trigrel,
11634 Oid conoid, Oid confrelid, Oid conrelid,
11635 Oid *insertTriggerOid,
11636 Oid *updateTriggerOid)
11638 ScanKeyData key;
11639 SysScanDesc scan;
11640 HeapTuple trigtup;
11642 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11643 ScanKeyInit(&key,
11644 Anum_pg_trigger_tgconstraint,
11645 BTEqualStrategyNumber, F_OIDEQ,
11646 ObjectIdGetDatum(conoid));
11648 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11649 NULL, 1, &key);
11650 while ((trigtup = systable_getnext(scan)) != NULL)
11652 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11654 if (trgform->tgconstrrelid != confrelid)
11655 continue;
11656 if (trgform->tgrelid != conrelid)
11657 continue;
11658 /* Only ever look at "check" triggers on the FK side. */
11659 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11660 continue;
11661 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11663 Assert(*insertTriggerOid == InvalidOid);
11664 *insertTriggerOid = trgform->oid;
11666 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11668 Assert(*updateTriggerOid == InvalidOid);
11669 *updateTriggerOid = trgform->oid;
11671 #ifndef USE_ASSERT_CHECKING
11672 /* In an assert-enabled build, continue looking to find duplicates. */
11673 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11674 break;
11675 #endif
11678 if (!OidIsValid(*insertTriggerOid))
11679 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11680 conoid);
11681 if (!OidIsValid(*updateTriggerOid))
11682 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11683 conoid);
11685 systable_endscan(scan);
11689 * ALTER TABLE ALTER CONSTRAINT
11691 * Update the attributes of a constraint.
11693 * Currently only works for Foreign Key constraints.
11695 * If the constraint is modified, returns its address; otherwise, return
11696 * InvalidObjectAddress.
11698 static ObjectAddress
11699 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11700 bool recursing, LOCKMODE lockmode)
11702 Constraint *cmdcon;
11703 Relation conrel;
11704 Relation tgrel;
11705 SysScanDesc scan;
11706 ScanKeyData skey[3];
11707 HeapTuple contuple;
11708 Form_pg_constraint currcon;
11709 ObjectAddress address;
11710 List *otherrelids = NIL;
11711 ListCell *lc;
11713 cmdcon = castNode(Constraint, cmd->def);
11715 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11716 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11719 * Find and check the target constraint
11721 ScanKeyInit(&skey[0],
11722 Anum_pg_constraint_conrelid,
11723 BTEqualStrategyNumber, F_OIDEQ,
11724 ObjectIdGetDatum(RelationGetRelid(rel)));
11725 ScanKeyInit(&skey[1],
11726 Anum_pg_constraint_contypid,
11727 BTEqualStrategyNumber, F_OIDEQ,
11728 ObjectIdGetDatum(InvalidOid));
11729 ScanKeyInit(&skey[2],
11730 Anum_pg_constraint_conname,
11731 BTEqualStrategyNumber, F_NAMEEQ,
11732 CStringGetDatum(cmdcon->conname));
11733 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11734 true, NULL, 3, skey);
11736 /* There can be at most one matching row */
11737 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11738 ereport(ERROR,
11739 (errcode(ERRCODE_UNDEFINED_OBJECT),
11740 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11741 cmdcon->conname, RelationGetRelationName(rel))));
11743 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11744 if (currcon->contype != CONSTRAINT_FOREIGN)
11745 ereport(ERROR,
11746 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11747 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11748 cmdcon->conname, RelationGetRelationName(rel))));
11751 * If it's not the topmost constraint, raise an error.
11753 * Altering a non-topmost constraint leaves some triggers untouched, since
11754 * they are not directly connected to this constraint; also, pg_dump would
11755 * ignore the deferrability status of the individual constraint, since it
11756 * only dumps topmost constraints. Avoid these problems by refusing this
11757 * operation and telling the user to alter the parent constraint instead.
11759 if (OidIsValid(currcon->conparentid))
11761 HeapTuple tp;
11762 Oid parent = currcon->conparentid;
11763 char *ancestorname = NULL;
11764 char *ancestortable = NULL;
11766 /* Loop to find the topmost constraint */
11767 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11769 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11771 /* If no parent, this is the constraint we want */
11772 if (!OidIsValid(contup->conparentid))
11774 ancestorname = pstrdup(NameStr(contup->conname));
11775 ancestortable = get_rel_name(contup->conrelid);
11776 ReleaseSysCache(tp);
11777 break;
11780 parent = contup->conparentid;
11781 ReleaseSysCache(tp);
11784 ereport(ERROR,
11785 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11786 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11787 cmdcon->conname, RelationGetRelationName(rel)),
11788 ancestorname && ancestortable ?
11789 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11790 cmdcon->conname, ancestorname, ancestortable) : 0,
11791 errhint("You may alter the constraint it derives from instead.")));
11795 * Do the actual catalog work. We can skip changing if already in the
11796 * desired state, but not if a partitioned table: partitions need to be
11797 * processed regardless, in case they had the constraint locally changed.
11799 address = InvalidObjectAddress;
11800 if (currcon->condeferrable != cmdcon->deferrable ||
11801 currcon->condeferred != cmdcon->initdeferred ||
11802 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11804 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11805 &otherrelids, lockmode))
11806 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11810 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11811 * having the constraint itself; here we also invalidate for relations
11812 * that have any triggers that are part of the constraint.
11814 foreach(lc, otherrelids)
11815 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11817 systable_endscan(scan);
11819 table_close(tgrel, RowExclusiveLock);
11820 table_close(conrel, RowExclusiveLock);
11822 return address;
11826 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11827 * constraint is altered.
11829 * *otherrelids is appended OIDs of relations containing affected triggers.
11831 * Note that we must recurse even when the values are correct, in case
11832 * indirect descendants have had their constraints altered locally.
11833 * (This could be avoided if we forbade altering constraints in partitions
11834 * but existing releases don't do that.)
11836 static bool
11837 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11838 Relation rel, HeapTuple contuple, List **otherrelids,
11839 LOCKMODE lockmode)
11841 Form_pg_constraint currcon;
11842 Oid conoid;
11843 Oid refrelid;
11844 bool changed = false;
11846 /* since this function recurses, it could be driven to stack overflow */
11847 check_stack_depth();
11849 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11850 conoid = currcon->oid;
11851 refrelid = currcon->confrelid;
11854 * Update pg_constraint with the flags from cmdcon.
11856 * If called to modify a constraint that's already in the desired state,
11857 * silently do nothing.
11859 if (currcon->condeferrable != cmdcon->deferrable ||
11860 currcon->condeferred != cmdcon->initdeferred)
11862 HeapTuple copyTuple;
11863 Form_pg_constraint copy_con;
11864 HeapTuple tgtuple;
11865 ScanKeyData tgkey;
11866 SysScanDesc tgscan;
11868 copyTuple = heap_copytuple(contuple);
11869 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11870 copy_con->condeferrable = cmdcon->deferrable;
11871 copy_con->condeferred = cmdcon->initdeferred;
11872 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11874 InvokeObjectPostAlterHook(ConstraintRelationId,
11875 conoid, 0);
11877 heap_freetuple(copyTuple);
11878 changed = true;
11880 /* Make new constraint flags visible to others */
11881 CacheInvalidateRelcache(rel);
11884 * Now we need to update the multiple entries in pg_trigger that
11885 * implement the constraint.
11887 ScanKeyInit(&tgkey,
11888 Anum_pg_trigger_tgconstraint,
11889 BTEqualStrategyNumber, F_OIDEQ,
11890 ObjectIdGetDatum(conoid));
11891 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11892 NULL, 1, &tgkey);
11893 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11895 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11896 Form_pg_trigger copy_tg;
11897 HeapTuple tgCopyTuple;
11900 * Remember OIDs of other relation(s) involved in FK constraint.
11901 * (Note: it's likely that we could skip forcing a relcache inval
11902 * for other rels that don't have a trigger whose properties
11903 * change, but let's be conservative.)
11905 if (tgform->tgrelid != RelationGetRelid(rel))
11906 *otherrelids = list_append_unique_oid(*otherrelids,
11907 tgform->tgrelid);
11910 * Update deferrability of RI_FKey_noaction_del,
11911 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11912 * triggers, but not others; see createForeignKeyActionTriggers
11913 * and CreateFKCheckTrigger.
11915 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11916 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11917 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11918 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11919 continue;
11921 tgCopyTuple = heap_copytuple(tgtuple);
11922 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11924 copy_tg->tgdeferrable = cmdcon->deferrable;
11925 copy_tg->tginitdeferred = cmdcon->initdeferred;
11926 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11928 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11930 heap_freetuple(tgCopyTuple);
11933 systable_endscan(tgscan);
11937 * If the table at either end of the constraint is partitioned, we need to
11938 * recurse and handle every constraint that is a child of this one.
11940 * (This assumes that the recurse flag is forcibly set for partitioned
11941 * tables, and not set for legacy inheritance, though we don't check for
11942 * that here.)
11944 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11945 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11947 ScanKeyData pkey;
11948 SysScanDesc pscan;
11949 HeapTuple childtup;
11951 ScanKeyInit(&pkey,
11952 Anum_pg_constraint_conparentid,
11953 BTEqualStrategyNumber, F_OIDEQ,
11954 ObjectIdGetDatum(conoid));
11956 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11957 true, NULL, 1, &pkey);
11959 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11961 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11962 Relation childrel;
11964 childrel = table_open(childcon->conrelid, lockmode);
11965 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11966 otherrelids, lockmode);
11967 table_close(childrel, NoLock);
11970 systable_endscan(pscan);
11973 return changed;
11977 * ALTER TABLE VALIDATE CONSTRAINT
11979 * XXX The reason we handle recursion here rather than at Phase 1 is because
11980 * there's no good way to skip recursing when handling foreign keys: there is
11981 * no need to lock children in that case, yet we wouldn't be able to avoid
11982 * doing so at that level.
11984 * Return value is the address of the validated constraint. If the constraint
11985 * was already validated, InvalidObjectAddress is returned.
11987 static ObjectAddress
11988 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11989 bool recurse, bool recursing, LOCKMODE lockmode)
11991 Relation conrel;
11992 SysScanDesc scan;
11993 ScanKeyData skey[3];
11994 HeapTuple tuple;
11995 Form_pg_constraint con;
11996 ObjectAddress address;
11998 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12001 * Find and check the target constraint
12003 ScanKeyInit(&skey[0],
12004 Anum_pg_constraint_conrelid,
12005 BTEqualStrategyNumber, F_OIDEQ,
12006 ObjectIdGetDatum(RelationGetRelid(rel)));
12007 ScanKeyInit(&skey[1],
12008 Anum_pg_constraint_contypid,
12009 BTEqualStrategyNumber, F_OIDEQ,
12010 ObjectIdGetDatum(InvalidOid));
12011 ScanKeyInit(&skey[2],
12012 Anum_pg_constraint_conname,
12013 BTEqualStrategyNumber, F_NAMEEQ,
12014 CStringGetDatum(constrName));
12015 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12016 true, NULL, 3, skey);
12018 /* There can be at most one matching row */
12019 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12020 ereport(ERROR,
12021 (errcode(ERRCODE_UNDEFINED_OBJECT),
12022 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12023 constrName, RelationGetRelationName(rel))));
12025 con = (Form_pg_constraint) GETSTRUCT(tuple);
12026 if (con->contype != CONSTRAINT_FOREIGN &&
12027 con->contype != CONSTRAINT_CHECK)
12028 ereport(ERROR,
12029 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12030 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12031 constrName, RelationGetRelationName(rel))));
12033 if (!con->conenforced)
12034 ereport(ERROR,
12035 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12036 errmsg("cannot validate NOT ENFORCED constraint")));
12038 if (!con->convalidated)
12040 AlteredTableInfo *tab;
12041 HeapTuple copyTuple;
12042 Form_pg_constraint copy_con;
12044 if (con->contype == CONSTRAINT_FOREIGN)
12046 NewConstraint *newcon;
12047 Constraint *fkconstraint;
12049 /* Queue validation for phase 3 */
12050 fkconstraint = makeNode(Constraint);
12051 /* for now this is all we need */
12052 fkconstraint->conname = constrName;
12054 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12055 newcon->name = constrName;
12056 newcon->contype = CONSTR_FOREIGN;
12057 newcon->refrelid = con->confrelid;
12058 newcon->refindid = con->conindid;
12059 newcon->conid = con->oid;
12060 newcon->qual = (Node *) fkconstraint;
12062 /* Find or create work queue entry for this table */
12063 tab = ATGetQueueEntry(wqueue, rel);
12064 tab->constraints = lappend(tab->constraints, newcon);
12067 * We disallow creating invalid foreign keys to or from
12068 * partitioned tables, so ignoring the recursion bit is okay.
12071 else if (con->contype == CONSTRAINT_CHECK)
12073 List *children = NIL;
12074 ListCell *child;
12075 NewConstraint *newcon;
12076 Datum val;
12077 char *conbin;
12080 * If we're recursing, the parent has already done this, so skip
12081 * it. Also, if the constraint is a NO INHERIT constraint, we
12082 * shouldn't try to look for it in the children.
12084 if (!recursing && !con->connoinherit)
12085 children = find_all_inheritors(RelationGetRelid(rel),
12086 lockmode, NULL);
12089 * For CHECK constraints, we must ensure that we only mark the
12090 * constraint as validated on the parent if it's already validated
12091 * on the children.
12093 * We recurse before validating on the parent, to reduce risk of
12094 * deadlocks.
12096 foreach(child, children)
12098 Oid childoid = lfirst_oid(child);
12099 Relation childrel;
12101 if (childoid == RelationGetRelid(rel))
12102 continue;
12105 * If we are told not to recurse, there had better not be any
12106 * child tables, because we can't mark the constraint on the
12107 * parent valid unless it is valid for all child tables.
12109 if (!recurse)
12110 ereport(ERROR,
12111 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12112 errmsg("constraint must be validated on child tables too")));
12114 /* find_all_inheritors already got lock */
12115 childrel = table_open(childoid, NoLock);
12117 ATExecValidateConstraint(wqueue, childrel, constrName, false,
12118 true, lockmode);
12119 table_close(childrel, NoLock);
12122 /* Queue validation for phase 3 */
12123 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12124 newcon->name = constrName;
12125 newcon->contype = CONSTR_CHECK;
12126 newcon->refrelid = InvalidOid;
12127 newcon->refindid = InvalidOid;
12128 newcon->conid = con->oid;
12130 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12131 Anum_pg_constraint_conbin);
12132 conbin = TextDatumGetCString(val);
12133 newcon->qual = (Node *) stringToNode(conbin);
12135 /* Find or create work queue entry for this table */
12136 tab = ATGetQueueEntry(wqueue, rel);
12137 tab->constraints = lappend(tab->constraints, newcon);
12140 * Invalidate relcache so that others see the new validated
12141 * constraint.
12143 CacheInvalidateRelcache(rel);
12147 * Now update the catalog, while we have the door open.
12149 copyTuple = heap_copytuple(tuple);
12150 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12151 copy_con->convalidated = true;
12152 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12154 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12156 heap_freetuple(copyTuple);
12158 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12160 else
12161 address = InvalidObjectAddress; /* already validated */
12163 systable_endscan(scan);
12165 table_close(conrel, RowExclusiveLock);
12167 return address;
12172 * transformColumnNameList - transform list of column names
12174 * Lookup each name and return its attnum and, optionally, type and collation
12175 * OIDs
12177 * Note: the name of this function suggests that it's general-purpose,
12178 * but actually it's only used to look up names appearing in foreign-key
12179 * clauses. The error messages would need work to use it in other cases,
12180 * and perhaps the validity checks as well.
12182 static int
12183 transformColumnNameList(Oid relId, List *colList,
12184 int16 *attnums, Oid *atttypids, Oid *attcollids)
12186 ListCell *l;
12187 int attnum;
12189 attnum = 0;
12190 foreach(l, colList)
12192 char *attname = strVal(lfirst(l));
12193 HeapTuple atttuple;
12194 Form_pg_attribute attform;
12196 atttuple = SearchSysCacheAttName(relId, attname);
12197 if (!HeapTupleIsValid(atttuple))
12198 ereport(ERROR,
12199 (errcode(ERRCODE_UNDEFINED_COLUMN),
12200 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12201 attname)));
12202 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12203 if (attform->attnum < 0)
12204 ereport(ERROR,
12205 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12206 errmsg("system columns cannot be used in foreign keys")));
12207 if (attnum >= INDEX_MAX_KEYS)
12208 ereport(ERROR,
12209 (errcode(ERRCODE_TOO_MANY_COLUMNS),
12210 errmsg("cannot have more than %d keys in a foreign key",
12211 INDEX_MAX_KEYS)));
12212 attnums[attnum] = attform->attnum;
12213 if (atttypids != NULL)
12214 atttypids[attnum] = attform->atttypid;
12215 if (attcollids != NULL)
12216 attcollids[attnum] = attform->attcollation;
12217 ReleaseSysCache(atttuple);
12218 attnum++;
12221 return attnum;
12225 * transformFkeyGetPrimaryKey -
12227 * Look up the names, attnums, types, and collations of the primary key attributes
12228 * for the pkrel. Also return the index OID and index opclasses of the
12229 * index supporting the primary key. Also return whether the index has
12230 * WITHOUT OVERLAPS.
12232 * All parameters except pkrel are output parameters. Also, the function
12233 * return value is the number of attributes in the primary key.
12235 * Used when the column list in the REFERENCES specification is omitted.
12237 static int
12238 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12239 List **attnamelist,
12240 int16 *attnums, Oid *atttypids, Oid *attcollids,
12241 Oid *opclasses, bool *pk_has_without_overlaps)
12243 List *indexoidlist;
12244 ListCell *indexoidscan;
12245 HeapTuple indexTuple = NULL;
12246 Form_pg_index indexStruct = NULL;
12247 Datum indclassDatum;
12248 oidvector *indclass;
12249 int i;
12252 * Get the list of index OIDs for the table from the relcache, and look up
12253 * each one in the pg_index syscache until we find one marked primary key
12254 * (hopefully there isn't more than one such). Insist it's valid, too.
12256 *indexOid = InvalidOid;
12258 indexoidlist = RelationGetIndexList(pkrel);
12260 foreach(indexoidscan, indexoidlist)
12262 Oid indexoid = lfirst_oid(indexoidscan);
12264 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12265 if (!HeapTupleIsValid(indexTuple))
12266 elog(ERROR, "cache lookup failed for index %u", indexoid);
12267 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12268 if (indexStruct->indisprimary && indexStruct->indisvalid)
12271 * Refuse to use a deferrable primary key. This is per SQL spec,
12272 * and there would be a lot of interesting semantic problems if we
12273 * tried to allow it.
12275 if (!indexStruct->indimmediate)
12276 ereport(ERROR,
12277 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12278 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12279 RelationGetRelationName(pkrel))));
12281 *indexOid = indexoid;
12282 break;
12284 ReleaseSysCache(indexTuple);
12287 list_free(indexoidlist);
12290 * Check that we found it
12292 if (!OidIsValid(*indexOid))
12293 ereport(ERROR,
12294 (errcode(ERRCODE_UNDEFINED_OBJECT),
12295 errmsg("there is no primary key for referenced table \"%s\"",
12296 RelationGetRelationName(pkrel))));
12298 /* Must get indclass the hard way */
12299 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12300 Anum_pg_index_indclass);
12301 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12304 * Now build the list of PK attributes from the indkey definition (we
12305 * assume a primary key cannot have expressional elements)
12307 *attnamelist = NIL;
12308 for (i = 0; i < indexStruct->indnkeyatts; i++)
12310 int pkattno = indexStruct->indkey.values[i];
12312 attnums[i] = pkattno;
12313 atttypids[i] = attnumTypeId(pkrel, pkattno);
12314 attcollids[i] = attnumCollationId(pkrel, pkattno);
12315 opclasses[i] = indclass->values[i];
12316 *attnamelist = lappend(*attnamelist,
12317 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12320 *pk_has_without_overlaps = indexStruct->indisexclusion;
12322 ReleaseSysCache(indexTuple);
12324 return i;
12328 * transformFkeyCheckAttrs -
12330 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12331 * reference as part of a foreign key constraint.
12333 * Returns the OID of the unique index supporting the constraint and
12334 * populates the caller-provided 'opclasses' array with the opclasses
12335 * associated with the index columns. Also sets whether the index
12336 * uses WITHOUT OVERLAPS.
12338 * Raises an ERROR on validation failure.
12340 static Oid
12341 transformFkeyCheckAttrs(Relation pkrel,
12342 int numattrs, int16 *attnums,
12343 bool with_period, Oid *opclasses,
12344 bool *pk_has_without_overlaps)
12346 Oid indexoid = InvalidOid;
12347 bool found = false;
12348 bool found_deferrable = false;
12349 List *indexoidlist;
12350 ListCell *indexoidscan;
12351 int i,
12355 * Reject duplicate appearances of columns in the referenced-columns list.
12356 * Such a case is forbidden by the SQL standard, and even if we thought it
12357 * useful to allow it, there would be ambiguity about how to match the
12358 * list to unique indexes (in particular, it'd be unclear which index
12359 * opclass goes with which FK column).
12361 for (i = 0; i < numattrs; i++)
12363 for (j = i + 1; j < numattrs; j++)
12365 if (attnums[i] == attnums[j])
12366 ereport(ERROR,
12367 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12368 errmsg("foreign key referenced-columns list must not contain duplicates")));
12373 * Get the list of index OIDs for the table from the relcache, and look up
12374 * each one in the pg_index syscache, and match unique indexes to the list
12375 * of attnums we are given.
12377 indexoidlist = RelationGetIndexList(pkrel);
12379 foreach(indexoidscan, indexoidlist)
12381 HeapTuple indexTuple;
12382 Form_pg_index indexStruct;
12384 indexoid = lfirst_oid(indexoidscan);
12385 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12386 if (!HeapTupleIsValid(indexTuple))
12387 elog(ERROR, "cache lookup failed for index %u", indexoid);
12388 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12391 * Must have the right number of columns; must be unique (or if
12392 * temporal then exclusion instead) and not a partial index; forget it
12393 * if there are any expressions, too. Invalid indexes are out as well.
12395 if (indexStruct->indnkeyatts == numattrs &&
12396 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12397 indexStruct->indisvalid &&
12398 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12399 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12401 Datum indclassDatum;
12402 oidvector *indclass;
12404 /* Must get indclass the hard way */
12405 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12406 Anum_pg_index_indclass);
12407 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12410 * The given attnum list may match the index columns in any order.
12411 * Check for a match, and extract the appropriate opclasses while
12412 * we're at it.
12414 * We know that attnums[] is duplicate-free per the test at the
12415 * start of this function, and we checked above that the number of
12416 * index columns agrees, so if we find a match for each attnums[]
12417 * entry then we must have a one-to-one match in some order.
12419 for (i = 0; i < numattrs; i++)
12421 found = false;
12422 for (j = 0; j < numattrs; j++)
12424 if (attnums[i] == indexStruct->indkey.values[j])
12426 opclasses[i] = indclass->values[j];
12427 found = true;
12428 break;
12431 if (!found)
12432 break;
12434 /* The last attribute in the index must be the PERIOD FK part */
12435 if (found && with_period)
12437 int16 periodattnum = attnums[numattrs - 1];
12439 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12443 * Refuse to use a deferrable unique/primary key. This is per SQL
12444 * spec, and there would be a lot of interesting semantic problems
12445 * if we tried to allow it.
12447 if (found && !indexStruct->indimmediate)
12450 * Remember that we found an otherwise matching index, so that
12451 * we can generate a more appropriate error message.
12453 found_deferrable = true;
12454 found = false;
12457 /* We need to know whether the index has WITHOUT OVERLAPS */
12458 if (found)
12459 *pk_has_without_overlaps = indexStruct->indisexclusion;
12461 ReleaseSysCache(indexTuple);
12462 if (found)
12463 break;
12466 if (!found)
12468 if (found_deferrable)
12469 ereport(ERROR,
12470 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12471 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12472 RelationGetRelationName(pkrel))));
12473 else
12474 ereport(ERROR,
12475 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12476 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12477 RelationGetRelationName(pkrel))));
12480 list_free(indexoidlist);
12482 return indexoid;
12486 * findFkeyCast -
12488 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12489 * Caller has equal regard for binary coercibility and for an exact match.
12491 static CoercionPathType
12492 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12494 CoercionPathType ret;
12496 if (targetTypeId == sourceTypeId)
12498 ret = COERCION_PATH_RELABELTYPE;
12499 *funcid = InvalidOid;
12501 else
12503 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12504 COERCION_IMPLICIT, funcid);
12505 if (ret == COERCION_PATH_NONE)
12506 /* A previously-relied-upon cast is now gone. */
12507 elog(ERROR, "could not find cast from %u to %u",
12508 sourceTypeId, targetTypeId);
12511 return ret;
12515 * Permissions checks on the referenced table for ADD FOREIGN KEY
12517 * Note: we have already checked that the user owns the referencing table,
12518 * else we'd have failed much earlier; no additional checks are needed for it.
12520 static void
12521 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12523 Oid roleid = GetUserId();
12524 AclResult aclresult;
12525 int i;
12527 /* Okay if we have relation-level REFERENCES permission */
12528 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12529 ACL_REFERENCES);
12530 if (aclresult == ACLCHECK_OK)
12531 return;
12532 /* Else we must have REFERENCES on each column */
12533 for (i = 0; i < natts; i++)
12535 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12536 roleid, ACL_REFERENCES);
12537 if (aclresult != ACLCHECK_OK)
12538 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12539 RelationGetRelationName(rel));
12544 * Scan the existing rows in a table to verify they meet a proposed FK
12545 * constraint.
12547 * Caller must have opened and locked both relations appropriately.
12549 static void
12550 validateForeignKeyConstraint(char *conname,
12551 Relation rel,
12552 Relation pkrel,
12553 Oid pkindOid,
12554 Oid constraintOid,
12555 bool hasperiod)
12557 TupleTableSlot *slot;
12558 TableScanDesc scan;
12559 Trigger trig = {0};
12560 Snapshot snapshot;
12561 MemoryContext oldcxt;
12562 MemoryContext perTupCxt;
12564 ereport(DEBUG1,
12565 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12568 * Build a trigger call structure; we'll need it either way.
12570 trig.tgoid = InvalidOid;
12571 trig.tgname = conname;
12572 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12573 trig.tgisinternal = true;
12574 trig.tgconstrrelid = RelationGetRelid(pkrel);
12575 trig.tgconstrindid = pkindOid;
12576 trig.tgconstraint = constraintOid;
12577 trig.tgdeferrable = false;
12578 trig.tginitdeferred = false;
12579 /* we needn't fill in remaining fields */
12582 * See if we can do it with a single LEFT JOIN query. A false result
12583 * indicates we must proceed with the fire-the-trigger method. We can't do
12584 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12585 * left joins.
12587 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12588 return;
12591 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12592 * if that tuple had just been inserted. If any of those fail, it should
12593 * ereport(ERROR) and that's that.
12595 snapshot = RegisterSnapshot(GetLatestSnapshot());
12596 slot = table_slot_create(rel, NULL);
12597 scan = table_beginscan(rel, snapshot, 0, NULL);
12599 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12600 "validateForeignKeyConstraint",
12601 ALLOCSET_SMALL_SIZES);
12602 oldcxt = MemoryContextSwitchTo(perTupCxt);
12604 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12606 LOCAL_FCINFO(fcinfo, 0);
12607 TriggerData trigdata = {0};
12609 CHECK_FOR_INTERRUPTS();
12612 * Make a call to the trigger function
12614 * No parameters are passed, but we do set a context
12616 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12619 * We assume RI_FKey_check_ins won't look at flinfo...
12621 trigdata.type = T_TriggerData;
12622 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12623 trigdata.tg_relation = rel;
12624 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12625 trigdata.tg_trigslot = slot;
12626 trigdata.tg_trigger = &trig;
12628 fcinfo->context = (Node *) &trigdata;
12630 RI_FKey_check_ins(fcinfo);
12632 MemoryContextReset(perTupCxt);
12635 MemoryContextSwitchTo(oldcxt);
12636 MemoryContextDelete(perTupCxt);
12637 table_endscan(scan);
12638 UnregisterSnapshot(snapshot);
12639 ExecDropSingleTupleTableSlot(slot);
12643 * CreateFKCheckTrigger
12644 * Creates the insert (on_insert=true) or update "check" trigger that
12645 * implements a given foreign key
12647 * Returns the OID of the so created trigger.
12649 static Oid
12650 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12651 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12652 bool on_insert)
12654 ObjectAddress trigAddress;
12655 CreateTrigStmt *fk_trigger;
12658 * Note: for a self-referential FK (referencing and referenced tables are
12659 * the same), it is important that the ON UPDATE action fires before the
12660 * CHECK action, since both triggers will fire on the same row during an
12661 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12662 * state of the row. Triggers fire in name order, so we ensure this by
12663 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12664 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12666 fk_trigger = makeNode(CreateTrigStmt);
12667 fk_trigger->replace = false;
12668 fk_trigger->isconstraint = true;
12669 fk_trigger->trigname = "RI_ConstraintTrigger_c";
12670 fk_trigger->relation = NULL;
12672 /* Either ON INSERT or ON UPDATE */
12673 if (on_insert)
12675 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12676 fk_trigger->events = TRIGGER_TYPE_INSERT;
12678 else
12680 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12681 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12684 fk_trigger->args = NIL;
12685 fk_trigger->row = true;
12686 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12687 fk_trigger->columns = NIL;
12688 fk_trigger->whenClause = NULL;
12689 fk_trigger->transitionRels = NIL;
12690 fk_trigger->deferrable = fkconstraint->deferrable;
12691 fk_trigger->initdeferred = fkconstraint->initdeferred;
12692 fk_trigger->constrrel = NULL;
12694 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12695 constraintOid, indexOid, InvalidOid,
12696 parentTrigOid, NULL, true, false);
12698 /* Make changes-so-far visible */
12699 CommandCounterIncrement();
12701 return trigAddress.objectId;
12705 * createForeignKeyActionTriggers
12706 * Create the referenced-side "action" triggers that implement a foreign
12707 * key.
12709 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12710 * *updateTrigOid.
12712 static void
12713 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12714 Oid constraintOid, Oid indexOid,
12715 Oid parentDelTrigger, Oid parentUpdTrigger,
12716 Oid *deleteTrigOid, Oid *updateTrigOid)
12718 CreateTrigStmt *fk_trigger;
12719 ObjectAddress trigAddress;
12722 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12723 * DELETE action on the referenced table.
12725 fk_trigger = makeNode(CreateTrigStmt);
12726 fk_trigger->replace = false;
12727 fk_trigger->isconstraint = true;
12728 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12729 fk_trigger->relation = NULL;
12730 fk_trigger->args = NIL;
12731 fk_trigger->row = true;
12732 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12733 fk_trigger->events = TRIGGER_TYPE_DELETE;
12734 fk_trigger->columns = NIL;
12735 fk_trigger->whenClause = NULL;
12736 fk_trigger->transitionRels = NIL;
12737 fk_trigger->constrrel = NULL;
12739 switch (fkconstraint->fk_del_action)
12741 case FKCONSTR_ACTION_NOACTION:
12742 fk_trigger->deferrable = fkconstraint->deferrable;
12743 fk_trigger->initdeferred = fkconstraint->initdeferred;
12744 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12745 break;
12746 case FKCONSTR_ACTION_RESTRICT:
12747 fk_trigger->deferrable = false;
12748 fk_trigger->initdeferred = false;
12749 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12750 break;
12751 case FKCONSTR_ACTION_CASCADE:
12752 fk_trigger->deferrable = false;
12753 fk_trigger->initdeferred = false;
12754 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12755 break;
12756 case FKCONSTR_ACTION_SETNULL:
12757 fk_trigger->deferrable = false;
12758 fk_trigger->initdeferred = false;
12759 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12760 break;
12761 case FKCONSTR_ACTION_SETDEFAULT:
12762 fk_trigger->deferrable = false;
12763 fk_trigger->initdeferred = false;
12764 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12765 break;
12766 default:
12767 elog(ERROR, "unrecognized FK action type: %d",
12768 (int) fkconstraint->fk_del_action);
12769 break;
12772 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12773 RelationGetRelid(rel),
12774 constraintOid, indexOid, InvalidOid,
12775 parentDelTrigger, NULL, true, false);
12776 if (deleteTrigOid)
12777 *deleteTrigOid = trigAddress.objectId;
12779 /* Make changes-so-far visible */
12780 CommandCounterIncrement();
12783 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12784 * UPDATE action on the referenced table.
12786 fk_trigger = makeNode(CreateTrigStmt);
12787 fk_trigger->replace = false;
12788 fk_trigger->isconstraint = true;
12789 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12790 fk_trigger->relation = NULL;
12791 fk_trigger->args = NIL;
12792 fk_trigger->row = true;
12793 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12794 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12795 fk_trigger->columns = NIL;
12796 fk_trigger->whenClause = NULL;
12797 fk_trigger->transitionRels = NIL;
12798 fk_trigger->constrrel = NULL;
12800 switch (fkconstraint->fk_upd_action)
12802 case FKCONSTR_ACTION_NOACTION:
12803 fk_trigger->deferrable = fkconstraint->deferrable;
12804 fk_trigger->initdeferred = fkconstraint->initdeferred;
12805 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12806 break;
12807 case FKCONSTR_ACTION_RESTRICT:
12808 fk_trigger->deferrable = false;
12809 fk_trigger->initdeferred = false;
12810 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12811 break;
12812 case FKCONSTR_ACTION_CASCADE:
12813 fk_trigger->deferrable = false;
12814 fk_trigger->initdeferred = false;
12815 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12816 break;
12817 case FKCONSTR_ACTION_SETNULL:
12818 fk_trigger->deferrable = false;
12819 fk_trigger->initdeferred = false;
12820 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12821 break;
12822 case FKCONSTR_ACTION_SETDEFAULT:
12823 fk_trigger->deferrable = false;
12824 fk_trigger->initdeferred = false;
12825 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12826 break;
12827 default:
12828 elog(ERROR, "unrecognized FK action type: %d",
12829 (int) fkconstraint->fk_upd_action);
12830 break;
12833 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12834 RelationGetRelid(rel),
12835 constraintOid, indexOid, InvalidOid,
12836 parentUpdTrigger, NULL, true, false);
12837 if (updateTrigOid)
12838 *updateTrigOid = trigAddress.objectId;
12842 * createForeignKeyCheckTriggers
12843 * Create the referencing-side "check" triggers that implement a foreign
12844 * key.
12846 * Returns the OIDs of the so created triggers in *insertTrigOid and
12847 * *updateTrigOid.
12849 static void
12850 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12851 Constraint *fkconstraint, Oid constraintOid,
12852 Oid indexOid,
12853 Oid parentInsTrigger, Oid parentUpdTrigger,
12854 Oid *insertTrigOid, Oid *updateTrigOid)
12856 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12857 constraintOid, indexOid,
12858 parentInsTrigger, true);
12859 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12860 constraintOid, indexOid,
12861 parentUpdTrigger, false);
12865 * ALTER TABLE DROP CONSTRAINT
12867 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12869 static void
12870 ATExecDropConstraint(Relation rel, const char *constrName,
12871 DropBehavior behavior, bool recurse,
12872 bool missing_ok, LOCKMODE lockmode)
12874 Relation conrel;
12875 SysScanDesc scan;
12876 ScanKeyData skey[3];
12877 HeapTuple tuple;
12878 bool found = false;
12880 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12883 * Find and drop the target constraint
12885 ScanKeyInit(&skey[0],
12886 Anum_pg_constraint_conrelid,
12887 BTEqualStrategyNumber, F_OIDEQ,
12888 ObjectIdGetDatum(RelationGetRelid(rel)));
12889 ScanKeyInit(&skey[1],
12890 Anum_pg_constraint_contypid,
12891 BTEqualStrategyNumber, F_OIDEQ,
12892 ObjectIdGetDatum(InvalidOid));
12893 ScanKeyInit(&skey[2],
12894 Anum_pg_constraint_conname,
12895 BTEqualStrategyNumber, F_NAMEEQ,
12896 CStringGetDatum(constrName));
12897 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12898 true, NULL, 3, skey);
12900 /* There can be at most one matching row */
12901 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12903 dropconstraint_internal(rel, tuple, behavior, recurse, false,
12904 missing_ok, lockmode);
12905 found = true;
12908 systable_endscan(scan);
12910 if (!found)
12912 if (!missing_ok)
12913 ereport(ERROR,
12914 errcode(ERRCODE_UNDEFINED_OBJECT),
12915 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12916 constrName, RelationGetRelationName(rel)));
12917 else
12918 ereport(NOTICE,
12919 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12920 constrName, RelationGetRelationName(rel)));
12923 table_close(conrel, RowExclusiveLock);
12927 * Remove a constraint, using its pg_constraint tuple
12929 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12930 * DROP NOT NULL.
12932 * Returns the address of the constraint being removed.
12934 static ObjectAddress
12935 dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12936 bool recurse, bool recursing, bool missing_ok,
12937 LOCKMODE lockmode)
12939 Relation conrel;
12940 Form_pg_constraint con;
12941 ObjectAddress conobj;
12942 List *children;
12943 bool is_no_inherit_constraint = false;
12944 char *constrName;
12945 char *colname = NULL;
12947 /* Guard against stack overflow due to overly deep inheritance tree. */
12948 check_stack_depth();
12950 /* At top level, permission check was done in ATPrepCmd, else do it */
12951 if (recursing)
12952 ATSimplePermissions(AT_DropConstraint, rel,
12953 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
12955 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12957 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12958 constrName = NameStr(con->conname);
12960 /* Don't allow drop of inherited constraints */
12961 if (con->coninhcount > 0 && !recursing)
12962 ereport(ERROR,
12963 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12964 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12965 constrName, RelationGetRelationName(rel))));
12968 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
12970 * While doing that, we're in a good position to disallow dropping a not-
12971 * null constraint underneath a primary key, a replica identity index, or
12972 * a generated identity column.
12974 if (con->contype == CONSTRAINT_NOTNULL)
12976 Relation attrel = table_open(AttributeRelationId, RowExclusiveLock);
12977 AttrNumber attnum = extractNotNullColumn(constraintTup);
12978 Bitmapset *pkattrs;
12979 Bitmapset *irattrs;
12980 HeapTuple atttup;
12981 Form_pg_attribute attForm;
12983 /* save column name for recursion step */
12984 colname = get_attname(RelationGetRelid(rel), attnum, false);
12987 * Disallow if it's in the primary key. For partitioned tables we
12988 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
12989 * return NULL if the primary key is invalid; but we still need to
12990 * protect not-null constraints under such a constraint, so check the
12991 * slow way.
12993 pkattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
12995 if (pkattrs == NULL &&
12996 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12998 Oid pkindex = RelationGetPrimaryKeyIndex(rel, true);
13000 if (OidIsValid(pkindex))
13002 Relation pk = relation_open(pkindex, AccessShareLock);
13004 pkattrs = NULL;
13005 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
13006 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
13008 relation_close(pk, AccessShareLock);
13012 if (pkattrs &&
13013 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, pkattrs))
13014 ereport(ERROR,
13015 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13016 errmsg("column \"%s\" is in a primary key",
13017 get_attname(RelationGetRelid(rel), attnum, false)));
13019 /* Disallow if it's in the replica identity */
13020 irattrs = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13021 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, irattrs))
13022 ereport(ERROR,
13023 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13024 errmsg("column \"%s\" is in index used as replica identity",
13025 get_attname(RelationGetRelid(rel), attnum, false)));
13027 /* Disallow if it's a GENERATED AS IDENTITY column */
13028 atttup = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
13029 if (!HeapTupleIsValid(atttup))
13030 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13031 attnum, RelationGetRelid(rel));
13032 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13033 if (attForm->attidentity != '\0')
13034 ereport(ERROR,
13035 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13036 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13037 get_attname(RelationGetRelid(rel), attnum,
13038 false),
13039 RelationGetRelationName(rel)));
13041 /* All good -- reset attnotnull if needed */
13042 if (attForm->attnotnull)
13044 attForm->attnotnull = false;
13045 CatalogTupleUpdate(attrel, &atttup->t_self, atttup);
13048 table_close(attrel, RowExclusiveLock);
13051 is_no_inherit_constraint = con->connoinherit;
13054 * If it's a foreign-key constraint, we'd better lock the referenced table
13055 * and check that that's not in use, just as we've already done for the
13056 * constrained table (else we might, eg, be dropping a trigger that has
13057 * unfired events). But we can/must skip that in the self-referential
13058 * case.
13060 if (con->contype == CONSTRAINT_FOREIGN &&
13061 con->confrelid != RelationGetRelid(rel))
13063 Relation frel;
13065 /* Must match lock taken by RemoveTriggerById: */
13066 frel = table_open(con->confrelid, AccessExclusiveLock);
13067 CheckAlterTableIsSafe(frel);
13068 table_close(frel, NoLock);
13072 * Perform the actual constraint deletion
13074 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13075 performDeletion(&conobj, behavior, 0);
13078 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13079 * are dropped via the dependency mechanism, so we're done here.
13081 if (con->contype != CONSTRAINT_CHECK &&
13082 con->contype != CONSTRAINT_NOTNULL &&
13083 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13085 table_close(conrel, RowExclusiveLock);
13086 return conobj;
13090 * Propagate to children as appropriate. Unlike most other ALTER
13091 * routines, we have to do this one level of recursion at a time; we can't
13092 * use find_all_inheritors to do it in one pass.
13094 if (!is_no_inherit_constraint)
13095 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13096 else
13097 children = NIL;
13099 foreach_oid(childrelid, children)
13101 Relation childrel;
13102 HeapTuple tuple;
13103 Form_pg_constraint childcon;
13105 /* find_inheritance_children already got lock */
13106 childrel = table_open(childrelid, NoLock);
13107 CheckAlterTableIsSafe(childrel);
13110 * We search for not-null constraints by column name, and others by
13111 * constraint name.
13113 if (con->contype == CONSTRAINT_NOTNULL)
13115 tuple = findNotNullConstraint(childrelid, colname);
13116 if (!HeapTupleIsValid(tuple))
13117 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13118 colname, RelationGetRelid(childrel));
13120 else
13122 SysScanDesc scan;
13123 ScanKeyData skey[3];
13125 ScanKeyInit(&skey[0],
13126 Anum_pg_constraint_conrelid,
13127 BTEqualStrategyNumber, F_OIDEQ,
13128 ObjectIdGetDatum(childrelid));
13129 ScanKeyInit(&skey[1],
13130 Anum_pg_constraint_contypid,
13131 BTEqualStrategyNumber, F_OIDEQ,
13132 ObjectIdGetDatum(InvalidOid));
13133 ScanKeyInit(&skey[2],
13134 Anum_pg_constraint_conname,
13135 BTEqualStrategyNumber, F_NAMEEQ,
13136 CStringGetDatum(constrName));
13137 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13138 true, NULL, 3, skey);
13139 /* There can only be one, so no need to loop */
13140 tuple = systable_getnext(scan);
13141 if (!HeapTupleIsValid(tuple))
13142 ereport(ERROR,
13143 (errcode(ERRCODE_UNDEFINED_OBJECT),
13144 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13145 constrName,
13146 RelationGetRelationName(childrel))));
13147 tuple = heap_copytuple(tuple);
13148 systable_endscan(scan);
13151 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13153 /* Right now only CHECK and not-null constraints can be inherited */
13154 if (childcon->contype != CONSTRAINT_CHECK &&
13155 childcon->contype != CONSTRAINT_NOTNULL)
13156 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13158 if (childcon->coninhcount <= 0) /* shouldn't happen */
13159 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13160 childrelid, NameStr(childcon->conname));
13162 if (recurse)
13165 * If the child constraint has other definition sources, just
13166 * decrement its inheritance count; if not, recurse to delete it.
13168 if (childcon->coninhcount == 1 && !childcon->conislocal)
13170 /* Time to delete this child constraint, too */
13171 dropconstraint_internal(childrel, tuple, behavior,
13172 recurse, true, missing_ok,
13173 lockmode);
13175 else
13177 /* Child constraint must survive my deletion */
13178 childcon->coninhcount--;
13179 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13181 /* Make update visible */
13182 CommandCounterIncrement();
13185 else
13188 * If we were told to drop ONLY in this table (no recursion) and
13189 * there are no further parents for this constraint, we need to
13190 * mark the inheritors' constraints as locally defined rather than
13191 * inherited.
13193 childcon->coninhcount--;
13194 if (childcon->coninhcount == 0)
13195 childcon->conislocal = true;
13197 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13199 /* Make update visible */
13200 CommandCounterIncrement();
13203 heap_freetuple(tuple);
13205 table_close(childrel, NoLock);
13208 table_close(conrel, RowExclusiveLock);
13210 return conobj;
13214 * ALTER COLUMN TYPE
13216 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13217 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13218 * transformed (and must be, because we rely on some transformed fields).
13220 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13221 * table will be done "in parallel" during phase 3, so all the USING
13222 * expressions should be parsed assuming the original column types. Also,
13223 * this allows a USING expression to refer to a field that will be dropped.
13225 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13226 * the first two execution steps in phase 2; they must not see the effects
13227 * of any other subcommand types, since the USING expressions are parsed
13228 * against the unmodified table's state.
13230 static void
13231 ATPrepAlterColumnType(List **wqueue,
13232 AlteredTableInfo *tab, Relation rel,
13233 bool recurse, bool recursing,
13234 AlterTableCmd *cmd, LOCKMODE lockmode,
13235 AlterTableUtilityContext *context)
13237 char *colName = cmd->name;
13238 ColumnDef *def = (ColumnDef *) cmd->def;
13239 TypeName *typeName = def->typeName;
13240 Node *transform = def->cooked_default;
13241 HeapTuple tuple;
13242 Form_pg_attribute attTup;
13243 AttrNumber attnum;
13244 Oid targettype;
13245 int32 targettypmod;
13246 Oid targetcollid;
13247 NewColumnValue *newval;
13248 ParseState *pstate = make_parsestate(NULL);
13249 AclResult aclresult;
13250 bool is_expr;
13252 if (rel->rd_rel->reloftype && !recursing)
13253 ereport(ERROR,
13254 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13255 errmsg("cannot alter column type of typed table")));
13257 /* lookup the attribute so we can check inheritance status */
13258 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13259 if (!HeapTupleIsValid(tuple))
13260 ereport(ERROR,
13261 (errcode(ERRCODE_UNDEFINED_COLUMN),
13262 errmsg("column \"%s\" of relation \"%s\" does not exist",
13263 colName, RelationGetRelationName(rel))));
13264 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13265 attnum = attTup->attnum;
13267 /* Can't alter a system attribute */
13268 if (attnum <= 0)
13269 ereport(ERROR,
13270 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13271 errmsg("cannot alter system column \"%s\"",
13272 colName)));
13275 * Cannot specify USING when altering type of a generated column, because
13276 * that would violate the generation expression.
13278 if (attTup->attgenerated && def->cooked_default)
13279 ereport(ERROR,
13280 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
13281 errmsg("cannot specify USING when altering type of generated column"),
13282 errdetail("Column \"%s\" is a generated column.", colName)));
13285 * Don't alter inherited columns. At outer level, there had better not be
13286 * any inherited definition; when recursing, we assume this was checked at
13287 * the parent level (see below).
13289 if (attTup->attinhcount > 0 && !recursing)
13290 ereport(ERROR,
13291 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13292 errmsg("cannot alter inherited column \"%s\"",
13293 colName)));
13295 /* Don't alter columns used in the partition key */
13296 if (has_partition_attrs(rel,
13297 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13298 &is_expr))
13299 ereport(ERROR,
13300 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13301 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13302 colName, RelationGetRelationName(rel))));
13304 /* Look up the target type */
13305 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13307 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13308 if (aclresult != ACLCHECK_OK)
13309 aclcheck_error_type(aclresult, targettype);
13311 /* And the collation */
13312 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13314 /* make sure datatype is legal for a column */
13315 CheckAttributeType(colName, targettype, targetcollid,
13316 list_make1_oid(rel->rd_rel->reltype),
13319 if (tab->relkind == RELKIND_RELATION ||
13320 tab->relkind == RELKIND_PARTITIONED_TABLE)
13323 * Set up an expression to transform the old data value to the new
13324 * type. If a USING option was given, use the expression as
13325 * transformed by transformAlterTableStmt, else just take the old
13326 * value and try to coerce it. We do this first so that type
13327 * incompatibility can be detected before we waste effort, and because
13328 * we need the expression to be parsed against the original table row
13329 * type.
13331 if (!transform)
13333 transform = (Node *) makeVar(1, attnum,
13334 attTup->atttypid, attTup->atttypmod,
13335 attTup->attcollation,
13339 transform = coerce_to_target_type(pstate,
13340 transform, exprType(transform),
13341 targettype, targettypmod,
13342 COERCION_ASSIGNMENT,
13343 COERCE_IMPLICIT_CAST,
13344 -1);
13345 if (transform == NULL)
13347 /* error text depends on whether USING was specified or not */
13348 if (def->cooked_default != NULL)
13349 ereport(ERROR,
13350 (errcode(ERRCODE_DATATYPE_MISMATCH),
13351 errmsg("result of USING clause for column \"%s\""
13352 " cannot be cast automatically to type %s",
13353 colName, format_type_be(targettype)),
13354 errhint("You might need to add an explicit cast.")));
13355 else
13356 ereport(ERROR,
13357 (errcode(ERRCODE_DATATYPE_MISMATCH),
13358 errmsg("column \"%s\" cannot be cast automatically to type %s",
13359 colName, format_type_be(targettype)),
13360 !attTup->attgenerated ?
13361 /* translator: USING is SQL, don't translate it */
13362 errhint("You might need to specify \"USING %s::%s\".",
13363 quote_identifier(colName),
13364 format_type_with_typemod(targettype,
13365 targettypmod)) : 0));
13368 /* Fix collations after all else */
13369 assign_expr_collations(pstate, transform);
13371 /* Plan the expr now so we can accurately assess the need to rewrite. */
13372 transform = (Node *) expression_planner((Expr *) transform);
13375 * Add a work queue item to make ATRewriteTable update the column
13376 * contents.
13378 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13379 newval->attnum = attnum;
13380 newval->expr = (Expr *) transform;
13381 newval->is_generated = false;
13383 tab->newvals = lappend(tab->newvals, newval);
13384 if (ATColumnChangeRequiresRewrite(transform, attnum))
13385 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13387 else if (transform)
13388 ereport(ERROR,
13389 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13390 errmsg("\"%s\" is not a table",
13391 RelationGetRelationName(rel))));
13393 if (!RELKIND_HAS_STORAGE(tab->relkind))
13396 * For relations without storage, do this check now. Regular tables
13397 * will check it later when the table is being rewritten.
13399 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13402 ReleaseSysCache(tuple);
13405 * Recurse manually by queueing a new command for each child, if
13406 * necessary. We cannot apply ATSimpleRecursion here because we need to
13407 * remap attribute numbers in the USING expression, if any.
13409 * If we are told not to recurse, there had better not be any child
13410 * tables; else the alter would put them out of step.
13412 if (recurse)
13414 Oid relid = RelationGetRelid(rel);
13415 List *child_oids,
13416 *child_numparents;
13417 ListCell *lo,
13418 *li;
13420 child_oids = find_all_inheritors(relid, lockmode,
13421 &child_numparents);
13424 * find_all_inheritors does the recursive search of the inheritance
13425 * hierarchy, so all we have to do is process all of the relids in the
13426 * list that it returns.
13428 forboth(lo, child_oids, li, child_numparents)
13430 Oid childrelid = lfirst_oid(lo);
13431 int numparents = lfirst_int(li);
13432 Relation childrel;
13433 HeapTuple childtuple;
13434 Form_pg_attribute childattTup;
13436 if (childrelid == relid)
13437 continue;
13439 /* find_all_inheritors already got lock */
13440 childrel = relation_open(childrelid, NoLock);
13441 CheckAlterTableIsSafe(childrel);
13444 * Verify that the child doesn't have any inherited definitions of
13445 * this column that came from outside this inheritance hierarchy.
13446 * (renameatt makes a similar test, though in a different way
13447 * because of its different recursion mechanism.)
13449 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13450 colName);
13451 if (!HeapTupleIsValid(childtuple))
13452 ereport(ERROR,
13453 (errcode(ERRCODE_UNDEFINED_COLUMN),
13454 errmsg("column \"%s\" of relation \"%s\" does not exist",
13455 colName, RelationGetRelationName(childrel))));
13456 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13458 if (childattTup->attinhcount > numparents)
13459 ereport(ERROR,
13460 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13461 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13462 colName, RelationGetRelationName(childrel))));
13464 ReleaseSysCache(childtuple);
13467 * Remap the attribute numbers. If no USING expression was
13468 * specified, there is no need for this step.
13470 if (def->cooked_default)
13472 AttrMap *attmap;
13473 bool found_whole_row;
13475 /* create a copy to scribble on */
13476 cmd = copyObject(cmd);
13478 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13479 RelationGetDescr(rel),
13480 false);
13481 ((ColumnDef *) cmd->def)->cooked_default =
13482 map_variable_attnos(def->cooked_default,
13483 1, 0,
13484 attmap,
13485 InvalidOid, &found_whole_row);
13486 if (found_whole_row)
13487 ereport(ERROR,
13488 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13489 errmsg("cannot convert whole-row table reference"),
13490 errdetail("USING expression contains a whole-row table reference.")));
13491 pfree(attmap);
13493 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13494 relation_close(childrel, NoLock);
13497 else if (!recursing &&
13498 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13499 ereport(ERROR,
13500 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13501 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13502 colName)));
13504 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13505 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13509 * When the data type of a column is changed, a rewrite might not be required
13510 * if the new type is sufficiently identical to the old one, and the USING
13511 * clause isn't trying to insert some other value. It's safe to skip the
13512 * rewrite in these cases:
13514 * - the old type is binary coercible to the new type
13515 * - the new type is an unconstrained domain over the old type
13516 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13518 * In the case of a constrained domain, we could get by with scanning the
13519 * table and checking the constraint rather than actually rewriting it, but we
13520 * don't currently try to do that.
13522 static bool
13523 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13525 Assert(expr != NULL);
13527 for (;;)
13529 /* only one varno, so no need to check that */
13530 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13531 return false;
13532 else if (IsA(expr, RelabelType))
13533 expr = (Node *) ((RelabelType *) expr)->arg;
13534 else if (IsA(expr, CoerceToDomain))
13536 CoerceToDomain *d = (CoerceToDomain *) expr;
13538 if (DomainHasConstraints(d->resulttype))
13539 return true;
13540 expr = (Node *) d->arg;
13542 else if (IsA(expr, FuncExpr))
13544 FuncExpr *f = (FuncExpr *) expr;
13546 switch (f->funcid)
13548 case F_TIMESTAMPTZ_TIMESTAMP:
13549 case F_TIMESTAMP_TIMESTAMPTZ:
13550 if (TimestampTimestampTzRequiresRewrite())
13551 return true;
13552 else
13553 expr = linitial(f->args);
13554 break;
13555 default:
13556 return true;
13559 else
13560 return true;
13565 * ALTER COLUMN .. SET DATA TYPE
13567 * Return the address of the modified column.
13569 static ObjectAddress
13570 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13571 AlterTableCmd *cmd, LOCKMODE lockmode)
13573 char *colName = cmd->name;
13574 ColumnDef *def = (ColumnDef *) cmd->def;
13575 TypeName *typeName = def->typeName;
13576 HeapTuple heapTup;
13577 Form_pg_attribute attTup,
13578 attOldTup;
13579 AttrNumber attnum;
13580 HeapTuple typeTuple;
13581 Form_pg_type tform;
13582 Oid targettype;
13583 int32 targettypmod;
13584 Oid targetcollid;
13585 Node *defaultexpr;
13586 Relation attrelation;
13587 Relation depRel;
13588 ScanKeyData key[3];
13589 SysScanDesc scan;
13590 HeapTuple depTup;
13591 ObjectAddress address;
13594 * Clear all the missing values if we're rewriting the table, since this
13595 * renders them pointless.
13597 if (tab->rewrite)
13599 Relation newrel;
13601 newrel = table_open(RelationGetRelid(rel), NoLock);
13602 RelationClearMissing(newrel);
13603 relation_close(newrel, NoLock);
13604 /* make sure we don't conflict with later attribute modifications */
13605 CommandCounterIncrement();
13608 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13610 /* Look up the target column */
13611 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13612 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13613 ereport(ERROR,
13614 (errcode(ERRCODE_UNDEFINED_COLUMN),
13615 errmsg("column \"%s\" of relation \"%s\" does not exist",
13616 colName, RelationGetRelationName(rel))));
13617 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13618 attnum = attTup->attnum;
13619 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13621 /* Check for multiple ALTER TYPE on same column --- can't cope */
13622 if (attTup->atttypid != attOldTup->atttypid ||
13623 attTup->atttypmod != attOldTup->atttypmod)
13624 ereport(ERROR,
13625 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13626 errmsg("cannot alter type of column \"%s\" twice",
13627 colName)));
13629 /* Look up the target type (should not fail, since prep found it) */
13630 typeTuple = typenameType(NULL, typeName, &targettypmod);
13631 tform = (Form_pg_type) GETSTRUCT(typeTuple);
13632 targettype = tform->oid;
13633 /* And the collation */
13634 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13637 * If there is a default expression for the column, get it and ensure we
13638 * can coerce it to the new datatype. (We must do this before changing
13639 * the column type, because build_column_default itself will try to
13640 * coerce, and will not issue the error message we want if it fails.)
13642 * We remove any implicit coercion steps at the top level of the old
13643 * default expression; this has been agreed to satisfy the principle of
13644 * least surprise. (The conversion to the new column type should act like
13645 * it started from what the user sees as the stored expression, and the
13646 * implicit coercions aren't going to be shown.)
13648 if (attTup->atthasdef)
13650 defaultexpr = build_column_default(rel, attnum);
13651 Assert(defaultexpr);
13652 defaultexpr = strip_implicit_coercions(defaultexpr);
13653 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13654 defaultexpr, exprType(defaultexpr),
13655 targettype, targettypmod,
13656 COERCION_ASSIGNMENT,
13657 COERCE_IMPLICIT_CAST,
13658 -1);
13659 if (defaultexpr == NULL)
13661 if (attTup->attgenerated)
13662 ereport(ERROR,
13663 (errcode(ERRCODE_DATATYPE_MISMATCH),
13664 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13665 colName, format_type_be(targettype))));
13666 else
13667 ereport(ERROR,
13668 (errcode(ERRCODE_DATATYPE_MISMATCH),
13669 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13670 colName, format_type_be(targettype))));
13673 else
13674 defaultexpr = NULL;
13677 * Find everything that depends on the column (constraints, indexes, etc),
13678 * and record enough information to let us recreate the objects.
13680 * The actual recreation does not happen here, but only after we have
13681 * performed all the individual ALTER TYPE operations. We have to save
13682 * the info before executing ALTER TYPE, though, else the deparser will
13683 * get confused.
13685 RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13688 * Now scan for dependencies of this column on other things. The only
13689 * things we should find are the dependency on the column datatype and
13690 * possibly a collation dependency. Those can be removed.
13692 depRel = table_open(DependRelationId, RowExclusiveLock);
13694 ScanKeyInit(&key[0],
13695 Anum_pg_depend_classid,
13696 BTEqualStrategyNumber, F_OIDEQ,
13697 ObjectIdGetDatum(RelationRelationId));
13698 ScanKeyInit(&key[1],
13699 Anum_pg_depend_objid,
13700 BTEqualStrategyNumber, F_OIDEQ,
13701 ObjectIdGetDatum(RelationGetRelid(rel)));
13702 ScanKeyInit(&key[2],
13703 Anum_pg_depend_objsubid,
13704 BTEqualStrategyNumber, F_INT4EQ,
13705 Int32GetDatum((int32) attnum));
13707 scan = systable_beginscan(depRel, DependDependerIndexId, true,
13708 NULL, 3, key);
13710 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13712 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13713 ObjectAddress foundObject;
13715 foundObject.classId = foundDep->refclassid;
13716 foundObject.objectId = foundDep->refobjid;
13717 foundObject.objectSubId = foundDep->refobjsubid;
13719 if (foundDep->deptype != DEPENDENCY_NORMAL)
13720 elog(ERROR, "found unexpected dependency type '%c'",
13721 foundDep->deptype);
13722 if (!(foundDep->refclassid == TypeRelationId &&
13723 foundDep->refobjid == attTup->atttypid) &&
13724 !(foundDep->refclassid == CollationRelationId &&
13725 foundDep->refobjid == attTup->attcollation))
13726 elog(ERROR, "found unexpected dependency for column: %s",
13727 getObjectDescription(&foundObject, false));
13729 CatalogTupleDelete(depRel, &depTup->t_self);
13732 systable_endscan(scan);
13734 table_close(depRel, RowExclusiveLock);
13737 * Here we go --- change the recorded column type and collation. (Note
13738 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13739 * fix up the missing value if any.
13741 if (attTup->atthasmissing)
13743 Datum missingval;
13744 bool missingNull;
13746 /* if rewrite is true the missing value should already be cleared */
13747 Assert(tab->rewrite == 0);
13749 /* Get the missing value datum */
13750 missingval = heap_getattr(heapTup,
13751 Anum_pg_attribute_attmissingval,
13752 attrelation->rd_att,
13753 &missingNull);
13755 /* if it's a null array there is nothing to do */
13757 if (!missingNull)
13760 * Get the datum out of the array and repack it in a new array
13761 * built with the new type data. We assume that since the table
13762 * doesn't need rewriting, the actual Datum doesn't need to be
13763 * changed, only the array metadata.
13766 int one = 1;
13767 bool isNull;
13768 Datum valuesAtt[Natts_pg_attribute] = {0};
13769 bool nullsAtt[Natts_pg_attribute] = {0};
13770 bool replacesAtt[Natts_pg_attribute] = {0};
13771 HeapTuple newTup;
13773 missingval = array_get_element(missingval,
13775 &one,
13777 attTup->attlen,
13778 attTup->attbyval,
13779 attTup->attalign,
13780 &isNull);
13781 missingval = PointerGetDatum(construct_array(&missingval,
13783 targettype,
13784 tform->typlen,
13785 tform->typbyval,
13786 tform->typalign));
13788 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13789 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13790 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13792 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13793 valuesAtt, nullsAtt, replacesAtt);
13794 heap_freetuple(heapTup);
13795 heapTup = newTup;
13796 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13800 attTup->atttypid = targettype;
13801 attTup->atttypmod = targettypmod;
13802 attTup->attcollation = targetcollid;
13803 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13804 ereport(ERROR,
13805 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13806 errmsg("too many array dimensions"));
13807 attTup->attndims = list_length(typeName->arrayBounds);
13808 attTup->attlen = tform->typlen;
13809 attTup->attbyval = tform->typbyval;
13810 attTup->attalign = tform->typalign;
13811 attTup->attstorage = tform->typstorage;
13812 attTup->attcompression = InvalidCompressionMethod;
13814 ReleaseSysCache(typeTuple);
13816 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13818 table_close(attrelation, RowExclusiveLock);
13820 /* Install dependencies on new datatype and collation */
13821 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13822 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13825 * Drop any pg_statistic entry for the column, since it's now wrong type
13827 RemoveStatistics(RelationGetRelid(rel), attnum);
13829 InvokeObjectPostAlterHook(RelationRelationId,
13830 RelationGetRelid(rel), attnum);
13833 * Update the default, if present, by brute force --- remove and re-add
13834 * the default. Probably unsafe to take shortcuts, since the new version
13835 * may well have additional dependencies. (It's okay to do this now,
13836 * rather than after other ALTER TYPE commands, since the default won't
13837 * depend on other column types.)
13839 if (defaultexpr)
13842 * If it's a GENERATED default, drop its dependency records, in
13843 * particular its INTERNAL dependency on the column, which would
13844 * otherwise cause dependency.c to refuse to perform the deletion.
13846 if (attTup->attgenerated)
13848 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13850 if (!OidIsValid(attrdefoid))
13851 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13852 RelationGetRelid(rel), attnum);
13853 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13857 * Make updates-so-far visible, particularly the new pg_attribute row
13858 * which will be updated again.
13860 CommandCounterIncrement();
13863 * We use RESTRICT here for safety, but at present we do not expect
13864 * anything to depend on the default.
13866 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13867 true);
13869 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13872 ObjectAddressSubSet(address, RelationRelationId,
13873 RelationGetRelid(rel), attnum);
13875 /* Cleanup */
13876 heap_freetuple(heapTup);
13878 return address;
13882 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13883 * that depends on the column (constraints, indexes, etc), and record enough
13884 * information to let us recreate the objects.
13886 static void
13887 RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13888 Relation rel, AttrNumber attnum, const char *colName)
13890 Relation depRel;
13891 ScanKeyData key[3];
13892 SysScanDesc scan;
13893 HeapTuple depTup;
13895 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13897 depRel = table_open(DependRelationId, RowExclusiveLock);
13899 ScanKeyInit(&key[0],
13900 Anum_pg_depend_refclassid,
13901 BTEqualStrategyNumber, F_OIDEQ,
13902 ObjectIdGetDatum(RelationRelationId));
13903 ScanKeyInit(&key[1],
13904 Anum_pg_depend_refobjid,
13905 BTEqualStrategyNumber, F_OIDEQ,
13906 ObjectIdGetDatum(RelationGetRelid(rel)));
13907 ScanKeyInit(&key[2],
13908 Anum_pg_depend_refobjsubid,
13909 BTEqualStrategyNumber, F_INT4EQ,
13910 Int32GetDatum((int32) attnum));
13912 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13913 NULL, 3, key);
13915 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13917 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13918 ObjectAddress foundObject;
13920 foundObject.classId = foundDep->classid;
13921 foundObject.objectId = foundDep->objid;
13922 foundObject.objectSubId = foundDep->objsubid;
13924 switch (foundObject.classId)
13926 case RelationRelationId:
13928 char relKind = get_rel_relkind(foundObject.objectId);
13930 if (relKind == RELKIND_INDEX ||
13931 relKind == RELKIND_PARTITIONED_INDEX)
13933 Assert(foundObject.objectSubId == 0);
13934 RememberIndexForRebuilding(foundObject.objectId, tab);
13936 else if (relKind == RELKIND_SEQUENCE)
13939 * This must be a SERIAL column's sequence. We need
13940 * not do anything to it.
13942 Assert(foundObject.objectSubId == 0);
13944 else
13946 /* Not expecting any other direct dependencies... */
13947 elog(ERROR, "unexpected object depending on column: %s",
13948 getObjectDescription(&foundObject, false));
13950 break;
13953 case ConstraintRelationId:
13954 Assert(foundObject.objectSubId == 0);
13955 RememberConstraintForRebuilding(foundObject.objectId, tab);
13956 break;
13958 case ProcedureRelationId:
13961 * A new-style SQL function can depend on a column, if that
13962 * column is referenced in the parsed function body. Ideally
13963 * we'd automatically update the function by deparsing and
13964 * reparsing it, but that's risky and might well fail anyhow.
13965 * FIXME someday.
13967 * This is only a problem for AT_AlterColumnType, not
13968 * AT_SetExpression.
13970 if (subtype == AT_AlterColumnType)
13971 ereport(ERROR,
13972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13973 errmsg("cannot alter type of a column used by a function or procedure"),
13974 errdetail("%s depends on column \"%s\"",
13975 getObjectDescription(&foundObject, false),
13976 colName)));
13977 break;
13979 case RewriteRelationId:
13982 * View/rule bodies have pretty much the same issues as
13983 * function bodies. FIXME someday.
13985 if (subtype == AT_AlterColumnType)
13986 ereport(ERROR,
13987 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13988 errmsg("cannot alter type of a column used by a view or rule"),
13989 errdetail("%s depends on column \"%s\"",
13990 getObjectDescription(&foundObject, false),
13991 colName)));
13992 break;
13994 case TriggerRelationId:
13997 * A trigger can depend on a column because the column is
13998 * specified as an update target, or because the column is
13999 * used in the trigger's WHEN condition. The first case would
14000 * not require any extra work, but the second case would
14001 * require updating the WHEN expression, which has the same
14002 * issues as above. Since we can't easily tell which case
14003 * applies, we punt for both. FIXME someday.
14005 if (subtype == AT_AlterColumnType)
14006 ereport(ERROR,
14007 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14008 errmsg("cannot alter type of a column used in a trigger definition"),
14009 errdetail("%s depends on column \"%s\"",
14010 getObjectDescription(&foundObject, false),
14011 colName)));
14012 break;
14014 case PolicyRelationId:
14017 * A policy can depend on a column because the column is
14018 * specified in the policy's USING or WITH CHECK qual
14019 * expressions. It might be possible to rewrite and recheck
14020 * the policy expression, but punt for now. It's certainly
14021 * easy enough to remove and recreate the policy; still, FIXME
14022 * someday.
14024 if (subtype == AT_AlterColumnType)
14025 ereport(ERROR,
14026 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14027 errmsg("cannot alter type of a column used in a policy definition"),
14028 errdetail("%s depends on column \"%s\"",
14029 getObjectDescription(&foundObject, false),
14030 colName)));
14031 break;
14033 case AttrDefaultRelationId:
14035 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14037 if (col.objectId == RelationGetRelid(rel) &&
14038 col.objectSubId == attnum)
14041 * Ignore the column's own default expression. The
14042 * caller deals with it.
14045 else
14048 * This must be a reference from the expression of a
14049 * generated column elsewhere in the same table.
14050 * Changing the type/generated expression of a column
14051 * that is used by a generated column is not allowed
14052 * by SQL standard, so just punt for now. It might be
14053 * doable with some thinking and effort.
14055 if (subtype == AT_AlterColumnType)
14056 ereport(ERROR,
14057 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14058 errmsg("cannot alter type of a column used by a generated column"),
14059 errdetail("Column \"%s\" is used by generated column \"%s\".",
14060 colName,
14061 get_attname(col.objectId,
14062 col.objectSubId,
14063 false))));
14065 break;
14068 case StatisticExtRelationId:
14071 * Give the extended-stats machinery a chance to fix anything
14072 * that this column type change would break.
14074 RememberStatisticsForRebuilding(foundObject.objectId, tab);
14075 break;
14077 case PublicationRelRelationId:
14080 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14081 * clause. Same issues as above. FIXME someday.
14083 if (subtype == AT_AlterColumnType)
14084 ereport(ERROR,
14085 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14086 errmsg("cannot alter type of a column used by a publication WHERE clause"),
14087 errdetail("%s depends on column \"%s\"",
14088 getObjectDescription(&foundObject, false),
14089 colName)));
14090 break;
14092 default:
14095 * We don't expect any other sorts of objects to depend on a
14096 * column.
14098 elog(ERROR, "unexpected object depending on column: %s",
14099 getObjectDescription(&foundObject, false));
14100 break;
14104 systable_endscan(scan);
14105 table_close(depRel, NoLock);
14109 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14110 * needs to be reset.
14112 static void
14113 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14115 if (!get_index_isreplident(indoid))
14116 return;
14118 if (tab->replicaIdentityIndex)
14119 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14121 tab->replicaIdentityIndex = get_rel_name(indoid);
14125 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14127 static void
14128 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14130 if (!get_index_isclustered(indoid))
14131 return;
14133 if (tab->clusterOnIndex)
14134 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14136 tab->clusterOnIndex = get_rel_name(indoid);
14140 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14141 * to be rebuilt (which we might already know).
14143 static void
14144 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14147 * This de-duplication check is critical for two independent reasons: we
14148 * mustn't try to recreate the same constraint twice, and if a constraint
14149 * depends on more than one column whose type is to be altered, we must
14150 * capture its definition string before applying any of the column type
14151 * changes. ruleutils.c will get confused if we ask again later.
14153 if (!list_member_oid(tab->changedConstraintOids, conoid))
14155 /* OK, capture the constraint's existing definition string */
14156 char *defstring = pg_get_constraintdef_command(conoid);
14157 Oid indoid;
14160 * It is critical to create not-null constraints ahead of primary key
14161 * indexes; otherwise, the not-null constraint would be created by the
14162 * primary key, and the constraint name would be wrong.
14164 if (get_constraint_type(conoid) == CONSTRAINT_NOTNULL)
14166 tab->changedConstraintOids = lcons_oid(conoid,
14167 tab->changedConstraintOids);
14168 tab->changedConstraintDefs = lcons(defstring,
14169 tab->changedConstraintDefs);
14171 else
14174 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14175 conoid);
14176 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14177 defstring);
14181 * For the index of a constraint, if any, remember if it is used for
14182 * the table's replica identity or if it is a clustered index, so that
14183 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14184 * those properties.
14186 indoid = get_constraint_index(conoid);
14187 if (OidIsValid(indoid))
14189 RememberReplicaIdentityForRebuilding(indoid, tab);
14190 RememberClusterOnForRebuilding(indoid, tab);
14196 * Subroutine for ATExecAlterColumnType: remember that an index needs
14197 * to be rebuilt (which we might already know).
14199 static void
14200 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14203 * This de-duplication check is critical for two independent reasons: we
14204 * mustn't try to recreate the same index twice, and if an index depends
14205 * on more than one column whose type is to be altered, we must capture
14206 * its definition string before applying any of the column type changes.
14207 * ruleutils.c will get confused if we ask again later.
14209 if (!list_member_oid(tab->changedIndexOids, indoid))
14212 * Before adding it as an index-to-rebuild, we'd better see if it
14213 * belongs to a constraint, and if so rebuild the constraint instead.
14214 * Typically this check fails, because constraint indexes normally
14215 * have only dependencies on their constraint. But it's possible for
14216 * such an index to also have direct dependencies on table columns,
14217 * for example with a partial exclusion constraint.
14219 Oid conoid = get_index_constraint(indoid);
14221 if (OidIsValid(conoid))
14223 RememberConstraintForRebuilding(conoid, tab);
14225 else
14227 /* OK, capture the index's existing definition string */
14228 char *defstring = pg_get_indexdef_string(indoid);
14230 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14231 indoid);
14232 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14233 defstring);
14236 * Remember if this index is used for the table's replica identity
14237 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14238 * can queue up commands necessary to restore those properties.
14240 RememberReplicaIdentityForRebuilding(indoid, tab);
14241 RememberClusterOnForRebuilding(indoid, tab);
14247 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14248 * needs to be rebuilt (which we might already know).
14250 static void
14251 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14254 * This de-duplication check is critical for two independent reasons: we
14255 * mustn't try to recreate the same statistics object twice, and if the
14256 * statistics object depends on more than one column whose type is to be
14257 * altered, we must capture its definition string before applying any of
14258 * the type changes. ruleutils.c will get confused if we ask again later.
14260 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14262 /* OK, capture the statistics object's existing definition string */
14263 char *defstring = pg_get_statisticsobjdef_string(stxoid);
14265 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14266 stxoid);
14267 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14268 defstring);
14273 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14274 * operations for a particular relation. We have to drop and recreate all the
14275 * indexes and constraints that depend on the altered columns. We do the
14276 * actual dropping here, but re-creation is managed by adding work queue
14277 * entries to do those steps later.
14279 static void
14280 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14282 ObjectAddress obj;
14283 ObjectAddresses *objects;
14284 ListCell *def_item;
14285 ListCell *oid_item;
14288 * Collect all the constraints and indexes to drop so we can process them
14289 * in a single call. That way we don't have to worry about dependencies
14290 * among them.
14292 objects = new_object_addresses();
14295 * Re-parse the index and constraint definitions, and attach them to the
14296 * appropriate work queue entries. We do this before dropping because in
14297 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14298 * lock on the table the constraint is attached to, and we need to get
14299 * that before reparsing/dropping.
14301 * We can't rely on the output of deparsing to tell us which relation to
14302 * operate on, because concurrent activity might have made the name
14303 * resolve differently. Instead, we've got to use the OID of the
14304 * constraint or index we're processing to figure out which relation to
14305 * operate on.
14307 forboth(oid_item, tab->changedConstraintOids,
14308 def_item, tab->changedConstraintDefs)
14310 Oid oldId = lfirst_oid(oid_item);
14311 HeapTuple tup;
14312 Form_pg_constraint con;
14313 Oid relid;
14314 Oid confrelid;
14315 char contype;
14316 bool conislocal;
14318 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14319 if (!HeapTupleIsValid(tup)) /* should not happen */
14320 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14321 con = (Form_pg_constraint) GETSTRUCT(tup);
14322 if (OidIsValid(con->conrelid))
14323 relid = con->conrelid;
14324 else
14326 /* must be a domain constraint */
14327 relid = get_typ_typrelid(getBaseType(con->contypid));
14328 if (!OidIsValid(relid))
14329 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14331 confrelid = con->confrelid;
14332 contype = con->contype;
14333 conislocal = con->conislocal;
14334 ReleaseSysCache(tup);
14336 ObjectAddressSet(obj, ConstraintRelationId, oldId);
14337 add_exact_object_address(&obj, objects);
14340 * If the constraint is inherited (only), we don't want to inject a
14341 * new definition here; it'll get recreated when
14342 * ATAddCheckNNConstraint recurses from adding the parent table's
14343 * constraint. But we had to carry the info this far so that we can
14344 * drop the constraint below.
14346 if (!conislocal)
14347 continue;
14350 * When rebuilding an FK constraint that references the table we're
14351 * modifying, we might not yet have any lock on the FK's table, so get
14352 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14353 * step, so there's no value in asking for anything weaker.
14355 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14356 LockRelationOid(relid, AccessExclusiveLock);
14358 ATPostAlterTypeParse(oldId, relid, confrelid,
14359 (char *) lfirst(def_item),
14360 wqueue, lockmode, tab->rewrite);
14362 forboth(oid_item, tab->changedIndexOids,
14363 def_item, tab->changedIndexDefs)
14365 Oid oldId = lfirst_oid(oid_item);
14366 Oid relid;
14368 relid = IndexGetRelation(oldId, false);
14369 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14370 (char *) lfirst(def_item),
14371 wqueue, lockmode, tab->rewrite);
14373 ObjectAddressSet(obj, RelationRelationId, oldId);
14374 add_exact_object_address(&obj, objects);
14377 /* add dependencies for new statistics */
14378 forboth(oid_item, tab->changedStatisticsOids,
14379 def_item, tab->changedStatisticsDefs)
14381 Oid oldId = lfirst_oid(oid_item);
14382 Oid relid;
14384 relid = StatisticsGetRelation(oldId, false);
14385 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14386 (char *) lfirst(def_item),
14387 wqueue, lockmode, tab->rewrite);
14389 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14390 add_exact_object_address(&obj, objects);
14394 * Queue up command to restore replica identity index marking
14396 if (tab->replicaIdentityIndex)
14398 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14399 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14401 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14402 subcmd->name = tab->replicaIdentityIndex;
14403 cmd->subtype = AT_ReplicaIdentity;
14404 cmd->def = (Node *) subcmd;
14406 /* do it after indexes and constraints */
14407 tab->subcmds[AT_PASS_OLD_CONSTR] =
14408 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14412 * Queue up command to restore marking of index used for cluster.
14414 if (tab->clusterOnIndex)
14416 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14418 cmd->subtype = AT_ClusterOn;
14419 cmd->name = tab->clusterOnIndex;
14421 /* do it after indexes and constraints */
14422 tab->subcmds[AT_PASS_OLD_CONSTR] =
14423 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14427 * It should be okay to use DROP_RESTRICT here, since nothing else should
14428 * be depending on these objects.
14430 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14432 free_object_addresses(objects);
14435 * The objects will get recreated during subsequent passes over the work
14436 * queue.
14441 * Parse the previously-saved definition string for a constraint, index or
14442 * statistics object against the newly-established column data type(s), and
14443 * queue up the resulting command parsetrees for execution.
14445 * This might fail if, for example, you have a WHERE clause that uses an
14446 * operator that's not available for the new column type.
14448 static void
14449 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14450 List **wqueue, LOCKMODE lockmode, bool rewrite)
14452 List *raw_parsetree_list;
14453 List *querytree_list;
14454 ListCell *list_item;
14455 Relation rel;
14458 * We expect that we will get only ALTER TABLE and CREATE INDEX
14459 * statements. Hence, there is no need to pass them through
14460 * parse_analyze_*() or the rewriter, but instead we need to pass them
14461 * through parse_utilcmd.c to make them ready for execution.
14463 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14464 querytree_list = NIL;
14465 foreach(list_item, raw_parsetree_list)
14467 RawStmt *rs = lfirst_node(RawStmt, list_item);
14468 Node *stmt = rs->stmt;
14470 if (IsA(stmt, IndexStmt))
14471 querytree_list = lappend(querytree_list,
14472 transformIndexStmt(oldRelId,
14473 (IndexStmt *) stmt,
14474 cmd));
14475 else if (IsA(stmt, AlterTableStmt))
14477 List *beforeStmts;
14478 List *afterStmts;
14480 stmt = (Node *) transformAlterTableStmt(oldRelId,
14481 (AlterTableStmt *) stmt,
14482 cmd,
14483 &beforeStmts,
14484 &afterStmts);
14485 querytree_list = list_concat(querytree_list, beforeStmts);
14486 querytree_list = lappend(querytree_list, stmt);
14487 querytree_list = list_concat(querytree_list, afterStmts);
14489 else if (IsA(stmt, CreateStatsStmt))
14490 querytree_list = lappend(querytree_list,
14491 transformStatsStmt(oldRelId,
14492 (CreateStatsStmt *) stmt,
14493 cmd));
14494 else
14495 querytree_list = lappend(querytree_list, stmt);
14498 /* Caller should already have acquired whatever lock we need. */
14499 rel = relation_open(oldRelId, NoLock);
14502 * Attach each generated command to the proper place in the work queue.
14503 * Note this could result in creation of entirely new work-queue entries.
14505 * Also note that we have to tweak the command subtypes, because it turns
14506 * out that re-creation of indexes and constraints has to act a bit
14507 * differently from initial creation.
14509 foreach(list_item, querytree_list)
14511 Node *stm = (Node *) lfirst(list_item);
14512 AlteredTableInfo *tab;
14514 tab = ATGetQueueEntry(wqueue, rel);
14516 if (IsA(stm, IndexStmt))
14518 IndexStmt *stmt = (IndexStmt *) stm;
14519 AlterTableCmd *newcmd;
14521 if (!rewrite)
14522 TryReuseIndex(oldId, stmt);
14523 stmt->reset_default_tblspc = true;
14524 /* keep the index's comment */
14525 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14527 newcmd = makeNode(AlterTableCmd);
14528 newcmd->subtype = AT_ReAddIndex;
14529 newcmd->def = (Node *) stmt;
14530 tab->subcmds[AT_PASS_OLD_INDEX] =
14531 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14533 else if (IsA(stm, AlterTableStmt))
14535 AlterTableStmt *stmt = (AlterTableStmt *) stm;
14536 ListCell *lcmd;
14538 foreach(lcmd, stmt->cmds)
14540 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14542 if (cmd->subtype == AT_AddIndex)
14544 IndexStmt *indstmt;
14545 Oid indoid;
14547 indstmt = castNode(IndexStmt, cmd->def);
14548 indoid = get_constraint_index(oldId);
14550 if (!rewrite)
14551 TryReuseIndex(indoid, indstmt);
14552 /* keep any comment on the index */
14553 indstmt->idxcomment = GetComment(indoid,
14554 RelationRelationId, 0);
14555 indstmt->reset_default_tblspc = true;
14557 cmd->subtype = AT_ReAddIndex;
14558 tab->subcmds[AT_PASS_OLD_INDEX] =
14559 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14561 /* recreate any comment on the constraint */
14562 RebuildConstraintComment(tab,
14563 AT_PASS_OLD_INDEX,
14564 oldId,
14565 rel,
14566 NIL,
14567 indstmt->idxname);
14569 else if (cmd->subtype == AT_AddConstraint)
14571 Constraint *con = castNode(Constraint, cmd->def);
14573 con->old_pktable_oid = refRelId;
14574 /* rewriting neither side of a FK */
14575 if (con->contype == CONSTR_FOREIGN &&
14576 !rewrite && tab->rewrite == 0)
14577 TryReuseForeignKey(oldId, con);
14578 con->reset_default_tblspc = true;
14579 cmd->subtype = AT_ReAddConstraint;
14580 tab->subcmds[AT_PASS_OLD_CONSTR] =
14581 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14584 * Recreate any comment on the constraint. If we have
14585 * recreated a primary key, then transformTableConstraint
14586 * has added an unnamed not-null constraint here; skip
14587 * this in that case.
14589 if (con->conname)
14590 RebuildConstraintComment(tab,
14591 AT_PASS_OLD_CONSTR,
14592 oldId,
14593 rel,
14594 NIL,
14595 con->conname);
14596 else
14597 Assert(con->contype == CONSTR_NOTNULL);
14599 else
14600 elog(ERROR, "unexpected statement subtype: %d",
14601 (int) cmd->subtype);
14604 else if (IsA(stm, AlterDomainStmt))
14606 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14608 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14610 Constraint *con = castNode(Constraint, stmt->def);
14611 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14613 cmd->subtype = AT_ReAddDomainConstraint;
14614 cmd->def = (Node *) stmt;
14615 tab->subcmds[AT_PASS_OLD_CONSTR] =
14616 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14618 /* recreate any comment on the constraint */
14619 RebuildConstraintComment(tab,
14620 AT_PASS_OLD_CONSTR,
14621 oldId,
14622 NULL,
14623 stmt->typeName,
14624 con->conname);
14626 else
14627 elog(ERROR, "unexpected statement subtype: %d",
14628 (int) stmt->subtype);
14630 else if (IsA(stm, CreateStatsStmt))
14632 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14633 AlterTableCmd *newcmd;
14635 /* keep the statistics object's comment */
14636 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14638 newcmd = makeNode(AlterTableCmd);
14639 newcmd->subtype = AT_ReAddStatistics;
14640 newcmd->def = (Node *) stmt;
14641 tab->subcmds[AT_PASS_MISC] =
14642 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14644 else
14645 elog(ERROR, "unexpected statement type: %d",
14646 (int) nodeTag(stm));
14649 relation_close(rel, NoLock);
14653 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14654 * for a table or domain constraint that is being rebuilt.
14656 * objid is the OID of the constraint.
14657 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14658 * as a string list) for a domain constraint.
14659 * (We could dig that info, as well as the conname, out of the pg_constraint
14660 * entry; but callers already have them so might as well pass them.)
14662 static void
14663 RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14664 Relation rel, List *domname,
14665 const char *conname)
14667 CommentStmt *cmd;
14668 char *comment_str;
14669 AlterTableCmd *newcmd;
14671 /* Look for comment for object wanted, and leave if none */
14672 comment_str = GetComment(objid, ConstraintRelationId, 0);
14673 if (comment_str == NULL)
14674 return;
14676 /* Build CommentStmt node, copying all input data for safety */
14677 cmd = makeNode(CommentStmt);
14678 if (rel)
14680 cmd->objtype = OBJECT_TABCONSTRAINT;
14681 cmd->object = (Node *)
14682 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14683 makeString(pstrdup(RelationGetRelationName(rel))),
14684 makeString(pstrdup(conname)));
14686 else
14688 cmd->objtype = OBJECT_DOMCONSTRAINT;
14689 cmd->object = (Node *)
14690 list_make2(makeTypeNameFromNameList(copyObject(domname)),
14691 makeString(pstrdup(conname)));
14693 cmd->comment = comment_str;
14695 /* Append it to list of commands */
14696 newcmd = makeNode(AlterTableCmd);
14697 newcmd->subtype = AT_ReAddComment;
14698 newcmd->def = (Node *) cmd;
14699 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14703 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14704 * for the real analysis, then mutates the IndexStmt based on that verdict.
14706 static void
14707 TryReuseIndex(Oid oldId, IndexStmt *stmt)
14709 if (CheckIndexCompatible(oldId,
14710 stmt->accessMethod,
14711 stmt->indexParams,
14712 stmt->excludeOpNames,
14713 stmt->iswithoutoverlaps))
14715 Relation irel = index_open(oldId, NoLock);
14717 /* If it's a partitioned index, there is no storage to share. */
14718 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14720 stmt->oldNumber = irel->rd_locator.relNumber;
14721 stmt->oldCreateSubid = irel->rd_createSubid;
14722 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14724 index_close(irel, NoLock);
14729 * Subroutine for ATPostAlterTypeParse().
14731 * Stash the old P-F equality operator into the Constraint node, for possible
14732 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14733 * this constraint can be skipped.
14735 static void
14736 TryReuseForeignKey(Oid oldId, Constraint *con)
14738 HeapTuple tup;
14739 Datum adatum;
14740 ArrayType *arr;
14741 Oid *rawarr;
14742 int numkeys;
14743 int i;
14745 Assert(con->contype == CONSTR_FOREIGN);
14746 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14748 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14749 if (!HeapTupleIsValid(tup)) /* should not happen */
14750 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14752 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14753 Anum_pg_constraint_conpfeqop);
14754 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14755 numkeys = ARR_DIMS(arr)[0];
14756 /* test follows the one in ri_FetchConstraintInfo() */
14757 if (ARR_NDIM(arr) != 1 ||
14758 ARR_HASNULL(arr) ||
14759 ARR_ELEMTYPE(arr) != OIDOID)
14760 elog(ERROR, "conpfeqop is not a 1-D Oid array");
14761 rawarr = (Oid *) ARR_DATA_PTR(arr);
14763 /* stash a List of the operator Oids in our Constraint node */
14764 for (i = 0; i < numkeys; i++)
14765 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14767 ReleaseSysCache(tup);
14771 * ALTER COLUMN .. OPTIONS ( ... )
14773 * Returns the address of the modified column
14775 static ObjectAddress
14776 ATExecAlterColumnGenericOptions(Relation rel,
14777 const char *colName,
14778 List *options,
14779 LOCKMODE lockmode)
14781 Relation ftrel;
14782 Relation attrel;
14783 ForeignServer *server;
14784 ForeignDataWrapper *fdw;
14785 HeapTuple tuple;
14786 HeapTuple newtuple;
14787 bool isnull;
14788 Datum repl_val[Natts_pg_attribute];
14789 bool repl_null[Natts_pg_attribute];
14790 bool repl_repl[Natts_pg_attribute];
14791 Datum datum;
14792 Form_pg_foreign_table fttableform;
14793 Form_pg_attribute atttableform;
14794 AttrNumber attnum;
14795 ObjectAddress address;
14797 if (options == NIL)
14798 return InvalidObjectAddress;
14800 /* First, determine FDW validator associated to the foreign table. */
14801 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14802 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14803 if (!HeapTupleIsValid(tuple))
14804 ereport(ERROR,
14805 (errcode(ERRCODE_UNDEFINED_OBJECT),
14806 errmsg("foreign table \"%s\" does not exist",
14807 RelationGetRelationName(rel))));
14808 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14809 server = GetForeignServer(fttableform->ftserver);
14810 fdw = GetForeignDataWrapper(server->fdwid);
14812 table_close(ftrel, AccessShareLock);
14813 ReleaseSysCache(tuple);
14815 attrel = table_open(AttributeRelationId, RowExclusiveLock);
14816 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14817 if (!HeapTupleIsValid(tuple))
14818 ereport(ERROR,
14819 (errcode(ERRCODE_UNDEFINED_COLUMN),
14820 errmsg("column \"%s\" of relation \"%s\" does not exist",
14821 colName, RelationGetRelationName(rel))));
14823 /* Prevent them from altering a system attribute */
14824 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14825 attnum = atttableform->attnum;
14826 if (attnum <= 0)
14827 ereport(ERROR,
14828 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14829 errmsg("cannot alter system column \"%s\"", colName)));
14832 /* Initialize buffers for new tuple values */
14833 memset(repl_val, 0, sizeof(repl_val));
14834 memset(repl_null, false, sizeof(repl_null));
14835 memset(repl_repl, false, sizeof(repl_repl));
14837 /* Extract the current options */
14838 datum = SysCacheGetAttr(ATTNAME,
14839 tuple,
14840 Anum_pg_attribute_attfdwoptions,
14841 &isnull);
14842 if (isnull)
14843 datum = PointerGetDatum(NULL);
14845 /* Transform the options */
14846 datum = transformGenericOptions(AttributeRelationId,
14847 datum,
14848 options,
14849 fdw->fdwvalidator);
14851 if (PointerIsValid(DatumGetPointer(datum)))
14852 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14853 else
14854 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14856 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14858 /* Everything looks good - update the tuple */
14860 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14861 repl_val, repl_null, repl_repl);
14863 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14865 InvokeObjectPostAlterHook(RelationRelationId,
14866 RelationGetRelid(rel),
14867 atttableform->attnum);
14868 ObjectAddressSubSet(address, RelationRelationId,
14869 RelationGetRelid(rel), attnum);
14871 ReleaseSysCache(tuple);
14873 table_close(attrel, RowExclusiveLock);
14875 heap_freetuple(newtuple);
14877 return address;
14881 * ALTER TABLE OWNER
14883 * recursing is true if we are recursing from a table to its indexes,
14884 * sequences, or toast table. We don't allow the ownership of those things to
14885 * be changed separately from the parent table. Also, we can skip permission
14886 * checks (this is necessary not just an optimization, else we'd fail to
14887 * handle toast tables properly).
14889 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14890 * free-standing composite type.
14892 void
14893 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14895 Relation target_rel;
14896 Relation class_rel;
14897 HeapTuple tuple;
14898 Form_pg_class tuple_class;
14901 * Get exclusive lock till end of transaction on the target table. Use
14902 * relation_open so that we can work on indexes and sequences.
14904 target_rel = relation_open(relationOid, lockmode);
14906 /* Get its pg_class tuple, too */
14907 class_rel = table_open(RelationRelationId, RowExclusiveLock);
14909 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14910 if (!HeapTupleIsValid(tuple))
14911 elog(ERROR, "cache lookup failed for relation %u", relationOid);
14912 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14914 /* Can we change the ownership of this tuple? */
14915 switch (tuple_class->relkind)
14917 case RELKIND_RELATION:
14918 case RELKIND_VIEW:
14919 case RELKIND_MATVIEW:
14920 case RELKIND_FOREIGN_TABLE:
14921 case RELKIND_PARTITIONED_TABLE:
14922 /* ok to change owner */
14923 break;
14924 case RELKIND_INDEX:
14925 if (!recursing)
14928 * Because ALTER INDEX OWNER used to be allowed, and in fact
14929 * is generated by old versions of pg_dump, we give a warning
14930 * and do nothing rather than erroring out. Also, to avoid
14931 * unnecessary chatter while restoring those old dumps, say
14932 * nothing at all if the command would be a no-op anyway.
14934 if (tuple_class->relowner != newOwnerId)
14935 ereport(WARNING,
14936 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14937 errmsg("cannot change owner of index \"%s\"",
14938 NameStr(tuple_class->relname)),
14939 errhint("Change the ownership of the index's table instead.")));
14940 /* quick hack to exit via the no-op path */
14941 newOwnerId = tuple_class->relowner;
14943 break;
14944 case RELKIND_PARTITIONED_INDEX:
14945 if (recursing)
14946 break;
14947 ereport(ERROR,
14948 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14949 errmsg("cannot change owner of index \"%s\"",
14950 NameStr(tuple_class->relname)),
14951 errhint("Change the ownership of the index's table instead.")));
14952 break;
14953 case RELKIND_SEQUENCE:
14954 if (!recursing &&
14955 tuple_class->relowner != newOwnerId)
14957 /* if it's an owned sequence, disallow changing it by itself */
14958 Oid tableId;
14959 int32 colId;
14961 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14962 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14963 ereport(ERROR,
14964 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14965 errmsg("cannot change owner of sequence \"%s\"",
14966 NameStr(tuple_class->relname)),
14967 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14968 NameStr(tuple_class->relname),
14969 get_rel_name(tableId))));
14971 break;
14972 case RELKIND_COMPOSITE_TYPE:
14973 if (recursing)
14974 break;
14975 ereport(ERROR,
14976 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14977 errmsg("\"%s\" is a composite type",
14978 NameStr(tuple_class->relname)),
14979 /* translator: %s is an SQL ALTER command */
14980 errhint("Use %s instead.",
14981 "ALTER TYPE")));
14982 break;
14983 case RELKIND_TOASTVALUE:
14984 if (recursing)
14985 break;
14986 /* FALL THRU */
14987 default:
14988 ereport(ERROR,
14989 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14990 errmsg("cannot change owner of relation \"%s\"",
14991 NameStr(tuple_class->relname)),
14992 errdetail_relkind_not_supported(tuple_class->relkind)));
14996 * If the new owner is the same as the existing owner, consider the
14997 * command to have succeeded. This is for dump restoration purposes.
14999 if (tuple_class->relowner != newOwnerId)
15001 Datum repl_val[Natts_pg_class];
15002 bool repl_null[Natts_pg_class];
15003 bool repl_repl[Natts_pg_class];
15004 Acl *newAcl;
15005 Datum aclDatum;
15006 bool isNull;
15007 HeapTuple newtuple;
15009 /* skip permission checks when recursing to index or toast table */
15010 if (!recursing)
15012 /* Superusers can always do it */
15013 if (!superuser())
15015 Oid namespaceOid = tuple_class->relnamespace;
15016 AclResult aclresult;
15018 /* Otherwise, must be owner of the existing object */
15019 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15020 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15021 RelationGetRelationName(target_rel));
15023 /* Must be able to become new owner */
15024 check_can_set_role(GetUserId(), newOwnerId);
15026 /* New owner must have CREATE privilege on namespace */
15027 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15028 ACL_CREATE);
15029 if (aclresult != ACLCHECK_OK)
15030 aclcheck_error(aclresult, OBJECT_SCHEMA,
15031 get_namespace_name(namespaceOid));
15035 memset(repl_null, false, sizeof(repl_null));
15036 memset(repl_repl, false, sizeof(repl_repl));
15038 repl_repl[Anum_pg_class_relowner - 1] = true;
15039 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15042 * Determine the modified ACL for the new owner. This is only
15043 * necessary when the ACL is non-null.
15045 aclDatum = SysCacheGetAttr(RELOID, tuple,
15046 Anum_pg_class_relacl,
15047 &isNull);
15048 if (!isNull)
15050 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15051 tuple_class->relowner, newOwnerId);
15052 repl_repl[Anum_pg_class_relacl - 1] = true;
15053 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15056 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15058 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15060 heap_freetuple(newtuple);
15063 * We must similarly update any per-column ACLs to reflect the new
15064 * owner; for neatness reasons that's split out as a subroutine.
15066 change_owner_fix_column_acls(relationOid,
15067 tuple_class->relowner,
15068 newOwnerId);
15071 * Update owner dependency reference, if any. A composite type has
15072 * none, because it's tracked for the pg_type entry instead of here;
15073 * indexes and TOAST tables don't have their own entries either.
15075 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15076 tuple_class->relkind != RELKIND_INDEX &&
15077 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15078 tuple_class->relkind != RELKIND_TOASTVALUE)
15079 changeDependencyOnOwner(RelationRelationId, relationOid,
15080 newOwnerId);
15083 * Also change the ownership of the table's row type, if it has one
15085 if (OidIsValid(tuple_class->reltype))
15086 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15089 * If we are operating on a table or materialized view, also change
15090 * the ownership of any indexes and sequences that belong to the
15091 * relation, as well as its toast table (if it has one).
15093 if (tuple_class->relkind == RELKIND_RELATION ||
15094 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15095 tuple_class->relkind == RELKIND_MATVIEW ||
15096 tuple_class->relkind == RELKIND_TOASTVALUE)
15098 List *index_oid_list;
15099 ListCell *i;
15101 /* Find all the indexes belonging to this relation */
15102 index_oid_list = RelationGetIndexList(target_rel);
15104 /* For each index, recursively change its ownership */
15105 foreach(i, index_oid_list)
15106 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15108 list_free(index_oid_list);
15111 /* If it has a toast table, recurse to change its ownership */
15112 if (tuple_class->reltoastrelid != InvalidOid)
15113 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15114 true, lockmode);
15116 /* If it has dependent sequences, recurse to change them too */
15117 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15120 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15122 ReleaseSysCache(tuple);
15123 table_close(class_rel, RowExclusiveLock);
15124 relation_close(target_rel, NoLock);
15128 * change_owner_fix_column_acls
15130 * Helper function for ATExecChangeOwner. Scan the columns of the table
15131 * and fix any non-null column ACLs to reflect the new owner.
15133 static void
15134 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15136 Relation attRelation;
15137 SysScanDesc scan;
15138 ScanKeyData key[1];
15139 HeapTuple attributeTuple;
15141 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15142 ScanKeyInit(&key[0],
15143 Anum_pg_attribute_attrelid,
15144 BTEqualStrategyNumber, F_OIDEQ,
15145 ObjectIdGetDatum(relationOid));
15146 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15147 true, NULL, 1, key);
15148 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15150 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15151 Datum repl_val[Natts_pg_attribute];
15152 bool repl_null[Natts_pg_attribute];
15153 bool repl_repl[Natts_pg_attribute];
15154 Acl *newAcl;
15155 Datum aclDatum;
15156 bool isNull;
15157 HeapTuple newtuple;
15159 /* Ignore dropped columns */
15160 if (att->attisdropped)
15161 continue;
15163 aclDatum = heap_getattr(attributeTuple,
15164 Anum_pg_attribute_attacl,
15165 RelationGetDescr(attRelation),
15166 &isNull);
15167 /* Null ACLs do not require changes */
15168 if (isNull)
15169 continue;
15171 memset(repl_null, false, sizeof(repl_null));
15172 memset(repl_repl, false, sizeof(repl_repl));
15174 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15175 oldOwnerId, newOwnerId);
15176 repl_repl[Anum_pg_attribute_attacl - 1] = true;
15177 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15179 newtuple = heap_modify_tuple(attributeTuple,
15180 RelationGetDescr(attRelation),
15181 repl_val, repl_null, repl_repl);
15183 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15185 heap_freetuple(newtuple);
15187 systable_endscan(scan);
15188 table_close(attRelation, RowExclusiveLock);
15192 * change_owner_recurse_to_sequences
15194 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15195 * for sequences that are dependent on serial columns, and changes their
15196 * ownership.
15198 static void
15199 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15201 Relation depRel;
15202 SysScanDesc scan;
15203 ScanKeyData key[2];
15204 HeapTuple tup;
15207 * SERIAL sequences are those having an auto dependency on one of the
15208 * table's columns (we don't care *which* column, exactly).
15210 depRel = table_open(DependRelationId, AccessShareLock);
15212 ScanKeyInit(&key[0],
15213 Anum_pg_depend_refclassid,
15214 BTEqualStrategyNumber, F_OIDEQ,
15215 ObjectIdGetDatum(RelationRelationId));
15216 ScanKeyInit(&key[1],
15217 Anum_pg_depend_refobjid,
15218 BTEqualStrategyNumber, F_OIDEQ,
15219 ObjectIdGetDatum(relationOid));
15220 /* we leave refobjsubid unspecified */
15222 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15223 NULL, 2, key);
15225 while (HeapTupleIsValid(tup = systable_getnext(scan)))
15227 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15228 Relation seqRel;
15230 /* skip dependencies other than auto dependencies on columns */
15231 if (depForm->refobjsubid == 0 ||
15232 depForm->classid != RelationRelationId ||
15233 depForm->objsubid != 0 ||
15234 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15235 continue;
15237 /* Use relation_open just in case it's an index */
15238 seqRel = relation_open(depForm->objid, lockmode);
15240 /* skip non-sequence relations */
15241 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15243 /* No need to keep the lock */
15244 relation_close(seqRel, lockmode);
15245 continue;
15248 /* We don't need to close the sequence while we alter it. */
15249 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15251 /* Now we can close it. Keep the lock till end of transaction. */
15252 relation_close(seqRel, NoLock);
15255 systable_endscan(scan);
15257 relation_close(depRel, AccessShareLock);
15261 * ALTER TABLE CLUSTER ON
15263 * The only thing we have to do is to change the indisclustered bits.
15265 * Return the address of the new clustering index.
15267 static ObjectAddress
15268 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15270 Oid indexOid;
15271 ObjectAddress address;
15273 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15275 if (!OidIsValid(indexOid))
15276 ereport(ERROR,
15277 (errcode(ERRCODE_UNDEFINED_OBJECT),
15278 errmsg("index \"%s\" for table \"%s\" does not exist",
15279 indexName, RelationGetRelationName(rel))));
15281 /* Check index is valid to cluster on */
15282 check_index_is_clusterable(rel, indexOid, lockmode);
15284 /* And do the work */
15285 mark_index_clustered(rel, indexOid, false);
15287 ObjectAddressSet(address,
15288 RelationRelationId, indexOid);
15290 return address;
15294 * ALTER TABLE SET WITHOUT CLUSTER
15296 * We have to find any indexes on the table that have indisclustered bit
15297 * set and turn it off.
15299 static void
15300 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15302 mark_index_clustered(rel, InvalidOid, false);
15306 * Preparation phase for SET ACCESS METHOD
15308 * Check that the access method exists and determine whether a change is
15309 * actually needed.
15311 static void
15312 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15314 Oid amoid;
15317 * Look up the access method name and check that it differs from the
15318 * table's current AM. If DEFAULT was specified for a partitioned table
15319 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15321 if (amname != NULL)
15322 amoid = get_table_am_oid(amname, false);
15323 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15324 amoid = InvalidOid;
15325 else
15326 amoid = get_table_am_oid(default_table_access_method, false);
15328 /* if it's a match, phase 3 doesn't need to do anything */
15329 if (rel->rd_rel->relam == amoid)
15330 return;
15332 /* Save info for Phase 3 to do the real work */
15333 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15334 tab->newAccessMethod = amoid;
15335 tab->chgAccessMethod = true;
15339 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15340 * storage that have an interest in preserving AM.
15342 * Since these have no storage, setting the access method is a catalog only
15343 * operation.
15345 static void
15346 ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15348 Relation pg_class;
15349 Oid oldAccessMethodId;
15350 HeapTuple tuple;
15351 Form_pg_class rd_rel;
15352 Oid reloid = RelationGetRelid(rel);
15355 * Shouldn't be called on relations having storage; these are processed in
15356 * phase 3.
15358 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15360 /* Get a modifiable copy of the relation's pg_class row. */
15361 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15363 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15364 if (!HeapTupleIsValid(tuple))
15365 elog(ERROR, "cache lookup failed for relation %u", reloid);
15366 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15368 /* Update the pg_class row. */
15369 oldAccessMethodId = rd_rel->relam;
15370 rd_rel->relam = newAccessMethodId;
15372 /* Leave if no update required */
15373 if (rd_rel->relam == oldAccessMethodId)
15375 heap_freetuple(tuple);
15376 table_close(pg_class, RowExclusiveLock);
15377 return;
15380 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15383 * Update the dependency on the new access method. No dependency is added
15384 * if the new access method is InvalidOid (default case). Be very careful
15385 * that this has to compare the previous value stored in pg_class with the
15386 * new one.
15388 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15390 ObjectAddress relobj,
15391 referenced;
15394 * New access method is defined and there was no dependency
15395 * previously, so record a new one.
15397 ObjectAddressSet(relobj, RelationRelationId, reloid);
15398 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15399 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15401 else if (OidIsValid(oldAccessMethodId) &&
15402 !OidIsValid(rd_rel->relam))
15405 * There was an access method defined, and no new one, so just remove
15406 * the existing dependency.
15408 deleteDependencyRecordsForClass(RelationRelationId, reloid,
15409 AccessMethodRelationId,
15410 DEPENDENCY_NORMAL);
15412 else
15414 Assert(OidIsValid(oldAccessMethodId) &&
15415 OidIsValid(rd_rel->relam));
15417 /* Both are valid, so update the dependency */
15418 changeDependencyFor(RelationRelationId, reloid,
15419 AccessMethodRelationId,
15420 oldAccessMethodId, rd_rel->relam);
15423 /* make the relam and dependency changes visible */
15424 CommandCounterIncrement();
15426 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15428 heap_freetuple(tuple);
15429 table_close(pg_class, RowExclusiveLock);
15433 * ALTER TABLE SET TABLESPACE
15435 static void
15436 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15438 Oid tablespaceId;
15440 /* Check that the tablespace exists */
15441 tablespaceId = get_tablespace_oid(tablespacename, false);
15443 /* Check permissions except when moving to database's default */
15444 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15446 AclResult aclresult;
15448 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15449 if (aclresult != ACLCHECK_OK)
15450 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15453 /* Save info for Phase 3 to do the real work */
15454 if (OidIsValid(tab->newTableSpace))
15455 ereport(ERROR,
15456 (errcode(ERRCODE_SYNTAX_ERROR),
15457 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15459 tab->newTableSpace = tablespaceId;
15463 * Set, reset, or replace reloptions.
15465 static void
15466 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15467 LOCKMODE lockmode)
15469 Oid relid;
15470 Relation pgclass;
15471 HeapTuple tuple;
15472 HeapTuple newtuple;
15473 Datum datum;
15474 bool isnull;
15475 Datum newOptions;
15476 Datum repl_val[Natts_pg_class];
15477 bool repl_null[Natts_pg_class];
15478 bool repl_repl[Natts_pg_class];
15479 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
15481 if (defList == NIL && operation != AT_ReplaceRelOptions)
15482 return; /* nothing to do */
15484 pgclass = table_open(RelationRelationId, RowExclusiveLock);
15486 /* Fetch heap tuple */
15487 relid = RelationGetRelid(rel);
15488 tuple = SearchSysCacheLocked1(RELOID, ObjectIdGetDatum(relid));
15489 if (!HeapTupleIsValid(tuple))
15490 elog(ERROR, "cache lookup failed for relation %u", relid);
15492 if (operation == AT_ReplaceRelOptions)
15495 * If we're supposed to replace the reloptions list, we just pretend
15496 * there were none before.
15498 datum = (Datum) 0;
15499 isnull = true;
15501 else
15503 /* Get the old reloptions */
15504 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15505 &isnull);
15508 /* Generate new proposed reloptions (text array) */
15509 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15510 defList, NULL, validnsps, false,
15511 operation == AT_ResetRelOptions);
15513 /* Validate */
15514 switch (rel->rd_rel->relkind)
15516 case RELKIND_RELATION:
15517 case RELKIND_TOASTVALUE:
15518 case RELKIND_MATVIEW:
15519 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15520 break;
15521 case RELKIND_PARTITIONED_TABLE:
15522 (void) partitioned_table_reloptions(newOptions, true);
15523 break;
15524 case RELKIND_VIEW:
15525 (void) view_reloptions(newOptions, true);
15526 break;
15527 case RELKIND_INDEX:
15528 case RELKIND_PARTITIONED_INDEX:
15529 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15530 break;
15531 default:
15532 ereport(ERROR,
15533 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15534 errmsg("cannot set options for relation \"%s\"",
15535 RelationGetRelationName(rel)),
15536 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15537 break;
15540 /* Special-case validation of view options */
15541 if (rel->rd_rel->relkind == RELKIND_VIEW)
15543 Query *view_query = get_view_query(rel);
15544 List *view_options = untransformRelOptions(newOptions);
15545 ListCell *cell;
15546 bool check_option = false;
15548 foreach(cell, view_options)
15550 DefElem *defel = (DefElem *) lfirst(cell);
15552 if (strcmp(defel->defname, "check_option") == 0)
15553 check_option = true;
15557 * If the check option is specified, look to see if the view is
15558 * actually auto-updatable or not.
15560 if (check_option)
15562 const char *view_updatable_error =
15563 view_query_is_auto_updatable(view_query, true);
15565 if (view_updatable_error)
15566 ereport(ERROR,
15567 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15568 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15569 errhint("%s", _(view_updatable_error))));
15574 * All we need do here is update the pg_class row; the new options will be
15575 * propagated into relcaches during post-commit cache inval.
15577 memset(repl_val, 0, sizeof(repl_val));
15578 memset(repl_null, false, sizeof(repl_null));
15579 memset(repl_repl, false, sizeof(repl_repl));
15581 if (newOptions != (Datum) 0)
15582 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15583 else
15584 repl_null[Anum_pg_class_reloptions - 1] = true;
15586 repl_repl[Anum_pg_class_reloptions - 1] = true;
15588 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15589 repl_val, repl_null, repl_repl);
15591 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15592 UnlockTuple(pgclass, &tuple->t_self, InplaceUpdateTupleLock);
15594 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15596 heap_freetuple(newtuple);
15598 ReleaseSysCache(tuple);
15600 /* repeat the whole exercise for the toast table, if there's one */
15601 if (OidIsValid(rel->rd_rel->reltoastrelid))
15603 Relation toastrel;
15604 Oid toastid = rel->rd_rel->reltoastrelid;
15606 toastrel = table_open(toastid, lockmode);
15608 /* Fetch heap tuple */
15609 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15610 if (!HeapTupleIsValid(tuple))
15611 elog(ERROR, "cache lookup failed for relation %u", toastid);
15613 if (operation == AT_ReplaceRelOptions)
15616 * If we're supposed to replace the reloptions list, we just
15617 * pretend there were none before.
15619 datum = (Datum) 0;
15620 isnull = true;
15622 else
15624 /* Get the old reloptions */
15625 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15626 &isnull);
15629 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15630 defList, "toast", validnsps, false,
15631 operation == AT_ResetRelOptions);
15633 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15635 memset(repl_val, 0, sizeof(repl_val));
15636 memset(repl_null, false, sizeof(repl_null));
15637 memset(repl_repl, false, sizeof(repl_repl));
15639 if (newOptions != (Datum) 0)
15640 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15641 else
15642 repl_null[Anum_pg_class_reloptions - 1] = true;
15644 repl_repl[Anum_pg_class_reloptions - 1] = true;
15646 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15647 repl_val, repl_null, repl_repl);
15649 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15651 InvokeObjectPostAlterHookArg(RelationRelationId,
15652 RelationGetRelid(toastrel), 0,
15653 InvalidOid, true);
15655 heap_freetuple(newtuple);
15657 ReleaseSysCache(tuple);
15659 table_close(toastrel, NoLock);
15662 table_close(pgclass, RowExclusiveLock);
15666 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15667 * rewriting to be done, so we just want to copy the data as fast as possible.
15669 static void
15670 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15672 Relation rel;
15673 Oid reltoastrelid;
15674 RelFileNumber newrelfilenumber;
15675 RelFileLocator newrlocator;
15676 List *reltoastidxids = NIL;
15677 ListCell *lc;
15680 * Need lock here in case we are recursing to toast table or index
15682 rel = relation_open(tableOid, lockmode);
15684 /* Check first if relation can be moved to new tablespace */
15685 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15687 InvokeObjectPostAlterHook(RelationRelationId,
15688 RelationGetRelid(rel), 0);
15689 relation_close(rel, NoLock);
15690 return;
15693 reltoastrelid = rel->rd_rel->reltoastrelid;
15694 /* Fetch the list of indexes on toast relation if necessary */
15695 if (OidIsValid(reltoastrelid))
15697 Relation toastRel = relation_open(reltoastrelid, lockmode);
15699 reltoastidxids = RelationGetIndexList(toastRel);
15700 relation_close(toastRel, lockmode);
15704 * Relfilenumbers are not unique in databases across tablespaces, so we
15705 * need to allocate a new one in the new tablespace.
15707 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15708 rel->rd_rel->relpersistence);
15710 /* Open old and new relation */
15711 newrlocator = rel->rd_locator;
15712 newrlocator.relNumber = newrelfilenumber;
15713 newrlocator.spcOid = newTableSpace;
15715 /* hand off to AM to actually create new rel storage and copy the data */
15716 if (rel->rd_rel->relkind == RELKIND_INDEX)
15718 index_copy_data(rel, newrlocator);
15720 else
15722 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15723 table_relation_copy_data(rel, &newrlocator);
15727 * Update the pg_class row.
15729 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15730 * executed on pg_class or its indexes (the above copy wouldn't contain
15731 * the updated pg_class entry), but that's forbidden with
15732 * CheckRelationTableSpaceMove().
15734 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15736 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15738 RelationAssumeNewRelfilelocator(rel);
15740 relation_close(rel, NoLock);
15742 /* Make sure the reltablespace change is visible */
15743 CommandCounterIncrement();
15745 /* Move associated toast relation and/or indexes, too */
15746 if (OidIsValid(reltoastrelid))
15747 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15748 foreach(lc, reltoastidxids)
15749 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15751 /* Clean up */
15752 list_free(reltoastidxids);
15756 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15757 * storage that have an interest in preserving tablespace.
15759 * Since these have no storage the tablespace can be updated with a simple
15760 * metadata only operation to update the tablespace.
15762 static void
15763 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15766 * Shouldn't be called on relations having storage; these are processed in
15767 * phase 3.
15769 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15771 /* check if relation can be moved to its new tablespace */
15772 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15774 InvokeObjectPostAlterHook(RelationRelationId,
15775 RelationGetRelid(rel),
15777 return;
15780 /* Update can be done, so change reltablespace */
15781 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15783 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15785 /* Make sure the reltablespace change is visible */
15786 CommandCounterIncrement();
15790 * Alter Table ALL ... SET TABLESPACE
15792 * Allows a user to move all objects of some type in a given tablespace in the
15793 * current database to another tablespace. Objects can be chosen based on the
15794 * owner of the object also, to allow users to move only their objects.
15795 * The user must have CREATE rights on the new tablespace, as usual. The main
15796 * permissions handling is done by the lower-level table move function.
15798 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15799 * lock can't be acquired then we ereport(ERROR).
15802 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15804 List *relations = NIL;
15805 ListCell *l;
15806 ScanKeyData key[1];
15807 Relation rel;
15808 TableScanDesc scan;
15809 HeapTuple tuple;
15810 Oid orig_tablespaceoid;
15811 Oid new_tablespaceoid;
15812 List *role_oids = roleSpecsToIds(stmt->roles);
15814 /* Ensure we were not asked to move something we can't */
15815 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15816 stmt->objtype != OBJECT_MATVIEW)
15817 ereport(ERROR,
15818 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15819 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15821 /* Get the orig and new tablespace OIDs */
15822 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15823 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15825 /* Can't move shared relations in to or out of pg_global */
15826 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15827 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15828 new_tablespaceoid == GLOBALTABLESPACE_OID)
15829 ereport(ERROR,
15830 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15831 errmsg("cannot move relations in to or out of pg_global tablespace")));
15834 * Must have CREATE rights on the new tablespace, unless it is the
15835 * database default tablespace (which all users implicitly have CREATE
15836 * rights on).
15838 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15840 AclResult aclresult;
15842 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15843 ACL_CREATE);
15844 if (aclresult != ACLCHECK_OK)
15845 aclcheck_error(aclresult, OBJECT_TABLESPACE,
15846 get_tablespace_name(new_tablespaceoid));
15850 * Now that the checks are done, check if we should set either to
15851 * InvalidOid because it is our database's default tablespace.
15853 if (orig_tablespaceoid == MyDatabaseTableSpace)
15854 orig_tablespaceoid = InvalidOid;
15856 if (new_tablespaceoid == MyDatabaseTableSpace)
15857 new_tablespaceoid = InvalidOid;
15859 /* no-op */
15860 if (orig_tablespaceoid == new_tablespaceoid)
15861 return new_tablespaceoid;
15864 * Walk the list of objects in the tablespace and move them. This will
15865 * only find objects in our database, of course.
15867 ScanKeyInit(&key[0],
15868 Anum_pg_class_reltablespace,
15869 BTEqualStrategyNumber, F_OIDEQ,
15870 ObjectIdGetDatum(orig_tablespaceoid));
15872 rel = table_open(RelationRelationId, AccessShareLock);
15873 scan = table_beginscan_catalog(rel, 1, key);
15874 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15876 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15877 Oid relOid = relForm->oid;
15880 * Do not move objects in pg_catalog as part of this, if an admin
15881 * really wishes to do so, they can issue the individual ALTER
15882 * commands directly.
15884 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15885 * (TOAST will be moved with the main table).
15887 if (IsCatalogNamespace(relForm->relnamespace) ||
15888 relForm->relisshared ||
15889 isAnyTempNamespace(relForm->relnamespace) ||
15890 IsToastNamespace(relForm->relnamespace))
15891 continue;
15893 /* Only move the object type requested */
15894 if ((stmt->objtype == OBJECT_TABLE &&
15895 relForm->relkind != RELKIND_RELATION &&
15896 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15897 (stmt->objtype == OBJECT_INDEX &&
15898 relForm->relkind != RELKIND_INDEX &&
15899 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15900 (stmt->objtype == OBJECT_MATVIEW &&
15901 relForm->relkind != RELKIND_MATVIEW))
15902 continue;
15904 /* Check if we are only moving objects owned by certain roles */
15905 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15906 continue;
15909 * Handle permissions-checking here since we are locking the tables
15910 * and also to avoid doing a bunch of work only to fail part-way. Note
15911 * that permissions will also be checked by AlterTableInternal().
15913 * Caller must be considered an owner on the table to move it.
15915 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15916 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15917 NameStr(relForm->relname));
15919 if (stmt->nowait &&
15920 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15921 ereport(ERROR,
15922 (errcode(ERRCODE_OBJECT_IN_USE),
15923 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15924 get_namespace_name(relForm->relnamespace),
15925 NameStr(relForm->relname))));
15926 else
15927 LockRelationOid(relOid, AccessExclusiveLock);
15929 /* Add to our list of objects to move */
15930 relations = lappend_oid(relations, relOid);
15933 table_endscan(scan);
15934 table_close(rel, AccessShareLock);
15936 if (relations == NIL)
15937 ereport(NOTICE,
15938 (errcode(ERRCODE_NO_DATA_FOUND),
15939 errmsg("no matching relations in tablespace \"%s\" found",
15940 orig_tablespaceoid == InvalidOid ? "(database default)" :
15941 get_tablespace_name(orig_tablespaceoid))));
15943 /* Everything is locked, loop through and move all of the relations. */
15944 foreach(l, relations)
15946 List *cmds = NIL;
15947 AlterTableCmd *cmd = makeNode(AlterTableCmd);
15949 cmd->subtype = AT_SetTableSpace;
15950 cmd->name = stmt->new_tablespacename;
15952 cmds = lappend(cmds, cmd);
15954 EventTriggerAlterTableStart((Node *) stmt);
15955 /* OID is set by AlterTableInternal */
15956 AlterTableInternal(lfirst_oid(l), cmds, false);
15957 EventTriggerAlterTableEnd();
15960 return new_tablespaceoid;
15963 static void
15964 index_copy_data(Relation rel, RelFileLocator newrlocator)
15966 SMgrRelation dstrel;
15969 * Since we copy the file directly without looking at the shared buffers,
15970 * we'd better first flush out any pages of the source relation that are
15971 * in shared buffers. We assume no new changes will be made while we are
15972 * holding exclusive lock on the rel.
15974 FlushRelationBuffers(rel);
15977 * Create and copy all forks of the relation, and schedule unlinking of
15978 * old physical files.
15980 * NOTE: any conflict in relfilenumber value will be caught in
15981 * RelationCreateStorage().
15983 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15985 /* copy main fork */
15986 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
15987 rel->rd_rel->relpersistence);
15989 /* copy those extra forks that exist */
15990 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15991 forkNum <= MAX_FORKNUM; forkNum++)
15993 if (smgrexists(RelationGetSmgr(rel), forkNum))
15995 smgrcreate(dstrel, forkNum, false);
15998 * WAL log creation if the relation is persistent, or this is the
15999 * init fork of an unlogged relation.
16001 if (RelationIsPermanent(rel) ||
16002 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16003 forkNum == INIT_FORKNUM))
16004 log_smgrcreate(&newrlocator, forkNum);
16005 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16006 rel->rd_rel->relpersistence);
16010 /* drop old relation, and close new one */
16011 RelationDropStorage(rel);
16012 smgrclose(dstrel);
16016 * ALTER TABLE ENABLE/DISABLE TRIGGER
16018 * We just pass this off to trigger.c.
16020 static void
16021 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16022 char fires_when, bool skip_system, bool recurse,
16023 LOCKMODE lockmode)
16025 EnableDisableTrigger(rel, trigname, InvalidOid,
16026 fires_when, skip_system, recurse,
16027 lockmode);
16029 InvokeObjectPostAlterHook(RelationRelationId,
16030 RelationGetRelid(rel), 0);
16034 * ALTER TABLE ENABLE/DISABLE RULE
16036 * We just pass this off to rewriteDefine.c.
16038 static void
16039 ATExecEnableDisableRule(Relation rel, const char *rulename,
16040 char fires_when, LOCKMODE lockmode)
16042 EnableDisableRule(rel, rulename, fires_when);
16044 InvokeObjectPostAlterHook(RelationRelationId,
16045 RelationGetRelid(rel), 0);
16049 * ALTER TABLE INHERIT
16051 * Add a parent to the child's parents. This verifies that all the columns and
16052 * check constraints of the parent appear in the child and that they have the
16053 * same data types and expressions.
16055 static void
16056 ATPrepAddInherit(Relation child_rel)
16058 if (child_rel->rd_rel->reloftype)
16059 ereport(ERROR,
16060 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16061 errmsg("cannot change inheritance of typed table")));
16063 if (child_rel->rd_rel->relispartition)
16064 ereport(ERROR,
16065 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16066 errmsg("cannot change inheritance of a partition")));
16068 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16069 ereport(ERROR,
16070 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16071 errmsg("cannot change inheritance of partitioned table")));
16075 * Return the address of the new parent relation.
16077 static ObjectAddress
16078 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16080 Relation parent_rel;
16081 List *children;
16082 ObjectAddress address;
16083 const char *trigger_name;
16086 * A self-exclusive lock is needed here. See the similar case in
16087 * MergeAttributes() for a full explanation.
16089 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16092 * Must be owner of both parent and child -- child was checked by
16093 * ATSimplePermissions call in ATPrepCmd
16095 ATSimplePermissions(AT_AddInherit, parent_rel,
16096 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
16098 /* Permanent rels cannot inherit from temporary ones */
16099 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16100 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16101 ereport(ERROR,
16102 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16103 errmsg("cannot inherit from temporary relation \"%s\"",
16104 RelationGetRelationName(parent_rel))));
16106 /* If parent rel is temp, it must belong to this session */
16107 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16108 !parent_rel->rd_islocaltemp)
16109 ereport(ERROR,
16110 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16111 errmsg("cannot inherit from temporary relation of another session")));
16113 /* Ditto for the child */
16114 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16115 !child_rel->rd_islocaltemp)
16116 ereport(ERROR,
16117 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16118 errmsg("cannot inherit to temporary relation of another session")));
16120 /* Prevent partitioned tables from becoming inheritance parents */
16121 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16122 ereport(ERROR,
16123 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16124 errmsg("cannot inherit from partitioned table \"%s\"",
16125 parent->relname)));
16127 /* Likewise for partitions */
16128 if (parent_rel->rd_rel->relispartition)
16129 ereport(ERROR,
16130 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16131 errmsg("cannot inherit from a partition")));
16134 * Prevent circularity by seeing if proposed parent inherits from child.
16135 * (In particular, this disallows making a rel inherit from itself.)
16137 * This is not completely bulletproof because of race conditions: in
16138 * multi-level inheritance trees, someone else could concurrently be
16139 * making another inheritance link that closes the loop but does not join
16140 * either of the rels we have locked. Preventing that seems to require
16141 * exclusive locks on the entire inheritance tree, which is a cure worse
16142 * than the disease. find_all_inheritors() will cope with circularity
16143 * anyway, so don't sweat it too much.
16145 * We use weakest lock we can on child's children, namely AccessShareLock.
16147 children = find_all_inheritors(RelationGetRelid(child_rel),
16148 AccessShareLock, NULL);
16150 if (list_member_oid(children, RelationGetRelid(parent_rel)))
16151 ereport(ERROR,
16152 (errcode(ERRCODE_DUPLICATE_TABLE),
16153 errmsg("circular inheritance not allowed"),
16154 errdetail("\"%s\" is already a child of \"%s\".",
16155 parent->relname,
16156 RelationGetRelationName(child_rel))));
16159 * If child_rel has row-level triggers with transition tables, we
16160 * currently don't allow it to become an inheritance child. See also
16161 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16163 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16164 if (trigger_name != NULL)
16165 ereport(ERROR,
16166 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16167 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16168 trigger_name, RelationGetRelationName(child_rel)),
16169 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16171 /* OK to create inheritance */
16172 CreateInheritance(child_rel, parent_rel, false);
16174 ObjectAddressSet(address, RelationRelationId,
16175 RelationGetRelid(parent_rel));
16177 /* keep our lock on the parent relation until commit */
16178 table_close(parent_rel, NoLock);
16180 return address;
16184 * CreateInheritance
16185 * Catalog manipulation portion of creating inheritance between a child
16186 * table and a parent table.
16188 * Common to ATExecAddInherit() and ATExecAttachPartition().
16190 static void
16191 CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16193 Relation catalogRelation;
16194 SysScanDesc scan;
16195 ScanKeyData key;
16196 HeapTuple inheritsTuple;
16197 int32 inhseqno;
16199 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16200 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16203 * Check for duplicates in the list of parents, and determine the highest
16204 * inhseqno already present; we'll use the next one for the new parent.
16205 * Also, if proposed child is a partition, it cannot already be
16206 * inheriting.
16208 * Note: we do not reject the case where the child already inherits from
16209 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16211 ScanKeyInit(&key,
16212 Anum_pg_inherits_inhrelid,
16213 BTEqualStrategyNumber, F_OIDEQ,
16214 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16215 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16216 true, NULL, 1, &key);
16218 /* inhseqno sequences start at 1 */
16219 inhseqno = 0;
16220 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16222 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16224 if (inh->inhparent == RelationGetRelid(parent_rel))
16225 ereport(ERROR,
16226 (errcode(ERRCODE_DUPLICATE_TABLE),
16227 errmsg("relation \"%s\" would be inherited from more than once",
16228 RelationGetRelationName(parent_rel))));
16230 if (inh->inhseqno > inhseqno)
16231 inhseqno = inh->inhseqno;
16233 systable_endscan(scan);
16235 /* Match up the columns and bump attinhcount as needed */
16236 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16238 /* Match up the constraints and bump coninhcount as needed */
16239 MergeConstraintsIntoExisting(child_rel, parent_rel);
16242 * OK, it looks valid. Make the catalog entries that show inheritance.
16244 StoreCatalogInheritance1(RelationGetRelid(child_rel),
16245 RelationGetRelid(parent_rel),
16246 inhseqno + 1,
16247 catalogRelation,
16248 parent_rel->rd_rel->relkind ==
16249 RELKIND_PARTITIONED_TABLE);
16251 /* Now we're done with pg_inherits */
16252 table_close(catalogRelation, RowExclusiveLock);
16256 * Obtain the source-text form of the constraint expression for a check
16257 * constraint, given its pg_constraint tuple
16259 static char *
16260 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16262 Form_pg_constraint con;
16263 bool isnull;
16264 Datum attr;
16265 Datum expr;
16267 con = (Form_pg_constraint) GETSTRUCT(contup);
16268 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16269 if (isnull)
16270 elog(ERROR, "null conbin for constraint %u", con->oid);
16272 expr = DirectFunctionCall2(pg_get_expr, attr,
16273 ObjectIdGetDatum(con->conrelid));
16274 return TextDatumGetCString(expr);
16278 * Determine whether two check constraints are functionally equivalent
16280 * The test we apply is to see whether they reverse-compile to the same
16281 * source string. This insulates us from issues like whether attributes
16282 * have the same physical column numbers in parent and child relations.
16284 * Note that we ignore enforceability as there are cases where constraints
16285 * with differing enforceability are allowed.
16287 static bool
16288 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16290 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16291 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16293 if (acon->condeferrable != bcon->condeferrable ||
16294 acon->condeferred != bcon->condeferred ||
16295 strcmp(decompile_conbin(a, tupleDesc),
16296 decompile_conbin(b, tupleDesc)) != 0)
16297 return false;
16298 else
16299 return true;
16303 * Check columns in child table match up with columns in parent, and increment
16304 * their attinhcount.
16306 * Called by CreateInheritance
16308 * Currently all parent columns must be found in child. Missing columns are an
16309 * error. One day we might consider creating new columns like CREATE TABLE
16310 * does. However, that is widely unpopular --- in the common use case of
16311 * partitioned tables it's a foot-gun.
16313 * The data type must match exactly. If the parent column is NOT NULL then
16314 * the child must be as well. Defaults are not compared, however.
16316 static void
16317 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16319 Relation attrrel;
16320 TupleDesc parent_desc;
16322 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16323 parent_desc = RelationGetDescr(parent_rel);
16325 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16327 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16328 char *parent_attname = NameStr(parent_att->attname);
16329 HeapTuple tuple;
16331 /* Ignore dropped columns in the parent. */
16332 if (parent_att->attisdropped)
16333 continue;
16335 /* Find same column in child (matching on column name). */
16336 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16337 if (HeapTupleIsValid(tuple))
16339 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16341 if (parent_att->atttypid != child_att->atttypid ||
16342 parent_att->atttypmod != child_att->atttypmod)
16343 ereport(ERROR,
16344 (errcode(ERRCODE_DATATYPE_MISMATCH),
16345 errmsg("child table \"%s\" has different type for column \"%s\"",
16346 RelationGetRelationName(child_rel), parent_attname)));
16348 if (parent_att->attcollation != child_att->attcollation)
16349 ereport(ERROR,
16350 (errcode(ERRCODE_COLLATION_MISMATCH),
16351 errmsg("child table \"%s\" has different collation for column \"%s\"",
16352 RelationGetRelationName(child_rel), parent_attname)));
16355 * If the parent has a not-null constraint that's not NO INHERIT,
16356 * make sure the child has one too.
16358 * Other constraints are checked elsewhere.
16360 if (parent_att->attnotnull && !child_att->attnotnull)
16362 HeapTuple contup;
16364 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16365 parent_att->attnum);
16366 if (HeapTupleIsValid(contup) &&
16367 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16368 ereport(ERROR,
16369 errcode(ERRCODE_DATATYPE_MISMATCH),
16370 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16371 parent_attname, RelationGetRelationName(child_rel)));
16375 * Child column must be generated if and only if parent column is.
16377 if (parent_att->attgenerated && !child_att->attgenerated)
16378 ereport(ERROR,
16379 (errcode(ERRCODE_DATATYPE_MISMATCH),
16380 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16381 if (child_att->attgenerated && !parent_att->attgenerated)
16382 ereport(ERROR,
16383 (errcode(ERRCODE_DATATYPE_MISMATCH),
16384 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16387 * Regular inheritance children are independent enough not to
16388 * inherit identity columns. But partitions are integral part of
16389 * a partitioned table and inherit identity column.
16391 if (ispartition)
16392 child_att->attidentity = parent_att->attidentity;
16395 * OK, bump the child column's inheritance count. (If we fail
16396 * later on, this change will just roll back.)
16398 if (pg_add_s16_overflow(child_att->attinhcount, 1,
16399 &child_att->attinhcount))
16400 ereport(ERROR,
16401 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16402 errmsg("too many inheritance parents"));
16405 * In case of partitions, we must enforce that value of attislocal
16406 * is same in all partitions. (Note: there are only inherited
16407 * attributes in partitions)
16409 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16411 Assert(child_att->attinhcount == 1);
16412 child_att->attislocal = false;
16415 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16416 heap_freetuple(tuple);
16418 else
16420 ereport(ERROR,
16421 (errcode(ERRCODE_DATATYPE_MISMATCH),
16422 errmsg("child table is missing column \"%s\"", parent_attname)));
16426 table_close(attrrel, RowExclusiveLock);
16430 * Check constraints in child table match up with constraints in parent,
16431 * and increment their coninhcount.
16433 * Constraints that are marked ONLY in the parent are ignored.
16435 * Called by CreateInheritance
16437 * Currently all constraints in parent must be present in the child. One day we
16438 * may consider adding new constraints like CREATE TABLE does.
16440 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16441 * constraints. As long as tables have more like 10 constraints it shouldn't be
16442 * a problem though. Even 100 constraints ought not be the end of the world.
16444 * XXX See MergeWithExistingConstraint too if you change this code.
16446 static void
16447 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16449 Relation constraintrel;
16450 SysScanDesc parent_scan;
16451 ScanKeyData parent_key;
16452 HeapTuple parent_tuple;
16453 Oid parent_relid = RelationGetRelid(parent_rel);
16454 AttrMap *attmap;
16456 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16458 /* Outer loop scans through the parent's constraint definitions */
16459 ScanKeyInit(&parent_key,
16460 Anum_pg_constraint_conrelid,
16461 BTEqualStrategyNumber, F_OIDEQ,
16462 ObjectIdGetDatum(parent_relid));
16463 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16464 true, NULL, 1, &parent_key);
16466 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
16467 RelationGetDescr(child_rel),
16468 true);
16470 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16472 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16473 SysScanDesc child_scan;
16474 ScanKeyData child_key;
16475 HeapTuple child_tuple;
16476 AttrNumber parent_attno;
16477 bool found = false;
16479 if (parent_con->contype != CONSTRAINT_CHECK &&
16480 parent_con->contype != CONSTRAINT_NOTNULL)
16481 continue;
16483 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16484 if (parent_con->connoinherit)
16485 continue;
16487 if (parent_con->contype == CONSTRAINT_NOTNULL)
16488 parent_attno = extractNotNullColumn(parent_tuple);
16489 else
16490 parent_attno = InvalidAttrNumber;
16492 /* Search for a child constraint matching this one */
16493 ScanKeyInit(&child_key,
16494 Anum_pg_constraint_conrelid,
16495 BTEqualStrategyNumber, F_OIDEQ,
16496 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16497 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16498 true, NULL, 1, &child_key);
16500 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16502 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16503 HeapTuple child_copy;
16505 if (child_con->contype != parent_con->contype)
16506 continue;
16509 * CHECK constraint are matched by constraint name, NOT NULL ones
16510 * by attribute number.
16512 if (child_con->contype == CONSTRAINT_CHECK)
16514 if (strcmp(NameStr(parent_con->conname),
16515 NameStr(child_con->conname)) != 0)
16516 continue;
16518 else if (child_con->contype == CONSTRAINT_NOTNULL)
16520 Form_pg_attribute parent_attr;
16521 Form_pg_attribute child_attr;
16522 AttrNumber child_attno;
16524 parent_attr = TupleDescAttr(parent_rel->rd_att, parent_attno - 1);
16525 child_attno = extractNotNullColumn(child_tuple);
16526 if (parent_attno != attmap->attnums[child_attno - 1])
16527 continue;
16529 child_attr = TupleDescAttr(child_rel->rd_att, child_attno - 1);
16530 /* there shouldn't be constraints on dropped columns */
16531 if (parent_attr->attisdropped || child_attr->attisdropped)
16532 elog(ERROR, "found not-null constraint on dropped columns");
16535 if (child_con->contype == CONSTRAINT_CHECK &&
16536 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16537 ereport(ERROR,
16538 (errcode(ERRCODE_DATATYPE_MISMATCH),
16539 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16540 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16543 * If the child constraint is "no inherit" then cannot merge
16545 if (child_con->connoinherit)
16546 ereport(ERROR,
16547 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16548 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16549 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16552 * If the child constraint is "not valid" then cannot merge with a
16553 * valid parent constraint
16555 if (parent_con->convalidated && child_con->conenforced &&
16556 !child_con->convalidated)
16557 ereport(ERROR,
16558 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16559 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16560 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16563 * A non-enforced child constraint cannot be merged with an
16564 * enforced parent constraint. However, the reverse is allowed,
16565 * where the child constraint is enforced.
16567 if (parent_con->conenforced && !child_con->conenforced)
16568 ereport(ERROR,
16569 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16570 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
16571 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16574 * OK, bump the child constraint's inheritance count. (If we fail
16575 * later on, this change will just roll back.)
16577 child_copy = heap_copytuple(child_tuple);
16578 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16580 if (pg_add_s16_overflow(child_con->coninhcount, 1,
16581 &child_con->coninhcount))
16582 ereport(ERROR,
16583 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16584 errmsg("too many inheritance parents"));
16587 * In case of partitions, an inherited constraint must be
16588 * inherited only once since it cannot have multiple parents and
16589 * it is never considered local.
16591 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16593 Assert(child_con->coninhcount == 1);
16594 child_con->conislocal = false;
16597 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16598 heap_freetuple(child_copy);
16600 found = true;
16601 break;
16604 systable_endscan(child_scan);
16606 if (!found)
16608 if (parent_con->contype == CONSTRAINT_NOTNULL)
16609 ereport(ERROR,
16610 errcode(ERRCODE_DATATYPE_MISMATCH),
16611 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
16612 get_attname(parent_relid,
16613 extractNotNullColumn(parent_tuple),
16614 false),
16615 RelationGetRelationName(child_rel)));
16617 ereport(ERROR,
16618 (errcode(ERRCODE_DATATYPE_MISMATCH),
16619 errmsg("child table is missing constraint \"%s\"",
16620 NameStr(parent_con->conname))));
16624 systable_endscan(parent_scan);
16625 table_close(constraintrel, RowExclusiveLock);
16629 * ALTER TABLE NO INHERIT
16631 * Return value is the address of the relation that is no longer parent.
16633 static ObjectAddress
16634 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16636 ObjectAddress address;
16637 Relation parent_rel;
16639 if (rel->rd_rel->relispartition)
16640 ereport(ERROR,
16641 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16642 errmsg("cannot change inheritance of a partition")));
16645 * AccessShareLock on the parent is probably enough, seeing that DROP
16646 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16647 * be inspecting the parent's schema.
16649 parent_rel = table_openrv(parent, AccessShareLock);
16652 * We don't bother to check ownership of the parent table --- ownership of
16653 * the child is presumed enough rights.
16656 /* Off to RemoveInheritance() where most of the work happens */
16657 RemoveInheritance(rel, parent_rel, false);
16659 ObjectAddressSet(address, RelationRelationId,
16660 RelationGetRelid(parent_rel));
16662 /* keep our lock on the parent relation until commit */
16663 table_close(parent_rel, NoLock);
16665 return address;
16669 * MarkInheritDetached
16671 * Set inhdetachpending for a partition, for ATExecDetachPartition
16672 * in concurrent mode. While at it, verify that no other partition is
16673 * already pending detach.
16675 static void
16676 MarkInheritDetached(Relation child_rel, Relation parent_rel)
16678 Relation catalogRelation;
16679 SysScanDesc scan;
16680 ScanKeyData key;
16681 HeapTuple inheritsTuple;
16682 bool found = false;
16684 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16687 * Find pg_inherits entries by inhparent. (We need to scan them all in
16688 * order to verify that no other partition is pending detach.)
16690 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16691 ScanKeyInit(&key,
16692 Anum_pg_inherits_inhparent,
16693 BTEqualStrategyNumber, F_OIDEQ,
16694 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16695 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16696 true, NULL, 1, &key);
16698 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16700 Form_pg_inherits inhForm;
16702 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16703 if (inhForm->inhdetachpending)
16704 ereport(ERROR,
16705 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16706 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16707 get_rel_name(inhForm->inhrelid),
16708 get_namespace_name(parent_rel->rd_rel->relnamespace),
16709 RelationGetRelationName(parent_rel)),
16710 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16712 if (inhForm->inhrelid == RelationGetRelid(child_rel))
16714 HeapTuple newtup;
16716 newtup = heap_copytuple(inheritsTuple);
16717 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16719 CatalogTupleUpdate(catalogRelation,
16720 &inheritsTuple->t_self,
16721 newtup);
16722 found = true;
16723 heap_freetuple(newtup);
16724 /* keep looking, to ensure we catch others pending detach */
16728 /* Done */
16729 systable_endscan(scan);
16730 table_close(catalogRelation, RowExclusiveLock);
16732 if (!found)
16733 ereport(ERROR,
16734 (errcode(ERRCODE_UNDEFINED_TABLE),
16735 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16736 RelationGetRelationName(child_rel),
16737 RelationGetRelationName(parent_rel))));
16741 * RemoveInheritance
16743 * Drop a parent from the child's parents. This just adjusts the attinhcount
16744 * and attislocal of the columns and removes the pg_inherit and pg_depend
16745 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16747 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16748 * up attislocal stays true, which means if a child is ever removed from a
16749 * parent then its columns will never be automatically dropped which may
16750 * surprise. But at least we'll never surprise by dropping columns someone
16751 * isn't expecting to be dropped which would actually mean data loss.
16753 * coninhcount and conislocal for inherited constraints are adjusted in
16754 * exactly the same way.
16756 * Common to ATExecDropInherit() and ATExecDetachPartition().
16758 static void
16759 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16761 Relation catalogRelation;
16762 SysScanDesc scan;
16763 ScanKeyData key[3];
16764 HeapTuple attributeTuple,
16765 constraintTuple;
16766 AttrMap *attmap;
16767 List *connames;
16768 List *nncolumns;
16769 bool found;
16770 bool is_partitioning;
16772 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16774 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16775 RelationGetRelid(parent_rel),
16776 expect_detached,
16777 RelationGetRelationName(child_rel));
16778 if (!found)
16780 if (is_partitioning)
16781 ereport(ERROR,
16782 (errcode(ERRCODE_UNDEFINED_TABLE),
16783 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16784 RelationGetRelationName(child_rel),
16785 RelationGetRelationName(parent_rel))));
16786 else
16787 ereport(ERROR,
16788 (errcode(ERRCODE_UNDEFINED_TABLE),
16789 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16790 RelationGetRelationName(parent_rel),
16791 RelationGetRelationName(child_rel))));
16795 * Search through child columns looking for ones matching parent rel
16797 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16798 ScanKeyInit(&key[0],
16799 Anum_pg_attribute_attrelid,
16800 BTEqualStrategyNumber, F_OIDEQ,
16801 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16802 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16803 true, NULL, 1, key);
16804 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16806 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16808 /* Ignore if dropped or not inherited */
16809 if (att->attisdropped)
16810 continue;
16811 if (att->attinhcount <= 0)
16812 continue;
16814 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16815 NameStr(att->attname)))
16817 /* Decrement inhcount and possibly set islocal to true */
16818 HeapTuple copyTuple = heap_copytuple(attributeTuple);
16819 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16821 copy_att->attinhcount--;
16822 if (copy_att->attinhcount == 0)
16823 copy_att->attislocal = true;
16825 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16826 heap_freetuple(copyTuple);
16829 systable_endscan(scan);
16830 table_close(catalogRelation, RowExclusiveLock);
16833 * Likewise, find inherited check and not-null constraints and disinherit
16834 * them. To do this, we first need a list of the names of the parent's
16835 * check constraints. (We cheat a bit by only checking for name matches,
16836 * assuming that the expressions will match.)
16838 * For NOT NULL columns, we store column numbers to match, mapping them in
16839 * to the child rel's attribute numbers.
16841 attmap = build_attrmap_by_name(RelationGetDescr(child_rel),
16842 RelationGetDescr(parent_rel),
16843 false);
16845 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16846 ScanKeyInit(&key[0],
16847 Anum_pg_constraint_conrelid,
16848 BTEqualStrategyNumber, F_OIDEQ,
16849 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16850 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16851 true, NULL, 1, key);
16853 connames = NIL;
16854 nncolumns = NIL;
16856 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16858 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16860 if (con->connoinherit)
16861 continue;
16863 if (con->contype == CONSTRAINT_CHECK)
16864 connames = lappend(connames, pstrdup(NameStr(con->conname)));
16865 if (con->contype == CONSTRAINT_NOTNULL)
16867 AttrNumber parent_attno = extractNotNullColumn(constraintTuple);
16869 nncolumns = lappend_int(nncolumns, attmap->attnums[parent_attno - 1]);
16873 systable_endscan(scan);
16875 /* Now scan the child's constraints to find matches */
16876 ScanKeyInit(&key[0],
16877 Anum_pg_constraint_conrelid,
16878 BTEqualStrategyNumber, F_OIDEQ,
16879 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16880 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16881 true, NULL, 1, key);
16883 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16885 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16886 bool match = false;
16889 * Match CHECK constraints by name, not-null constraints by column
16890 * number, and ignore all others.
16892 if (con->contype == CONSTRAINT_CHECK)
16894 foreach_ptr(char, chkname, connames)
16896 if (con->contype == CONSTRAINT_CHECK &&
16897 strcmp(NameStr(con->conname), chkname) == 0)
16899 match = true;
16900 connames = foreach_delete_current(connames, chkname);
16901 break;
16905 else if (con->contype == CONSTRAINT_NOTNULL)
16907 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
16909 foreach_int(prevattno, nncolumns)
16911 if (prevattno == child_attno)
16913 match = true;
16914 nncolumns = foreach_delete_current(nncolumns, prevattno);
16915 break;
16919 else
16920 continue;
16922 if (match)
16924 /* Decrement inhcount and possibly set islocal to true */
16925 HeapTuple copyTuple = heap_copytuple(constraintTuple);
16926 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16928 if (copy_con->coninhcount <= 0) /* shouldn't happen */
16929 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16930 RelationGetRelid(child_rel), NameStr(copy_con->conname));
16932 copy_con->coninhcount--;
16933 if (copy_con->coninhcount == 0)
16934 copy_con->conislocal = true;
16936 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16937 heap_freetuple(copyTuple);
16941 /* We should have matched all constraints */
16942 if (connames != NIL || nncolumns != NIL)
16943 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
16944 list_length(connames) + list_length(nncolumns),
16945 RelationGetRelationName(child_rel), RelationGetRelationName(parent_rel));
16947 systable_endscan(scan);
16948 table_close(catalogRelation, RowExclusiveLock);
16950 drop_parent_dependency(RelationGetRelid(child_rel),
16951 RelationRelationId,
16952 RelationGetRelid(parent_rel),
16953 child_dependency_type(is_partitioning));
16956 * Post alter hook of this inherits. Since object_access_hook doesn't take
16957 * multiple object identifiers, we relay oid of parent relation using
16958 * auxiliary_id argument.
16960 InvokeObjectPostAlterHookArg(InheritsRelationId,
16961 RelationGetRelid(child_rel), 0,
16962 RelationGetRelid(parent_rel), false);
16966 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16967 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16968 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16969 * be TypeRelationId). There's no convenient way to do this, so go trawling
16970 * through pg_depend.
16972 static void
16973 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16974 DependencyType deptype)
16976 Relation catalogRelation;
16977 SysScanDesc scan;
16978 ScanKeyData key[3];
16979 HeapTuple depTuple;
16981 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16983 ScanKeyInit(&key[0],
16984 Anum_pg_depend_classid,
16985 BTEqualStrategyNumber, F_OIDEQ,
16986 ObjectIdGetDatum(RelationRelationId));
16987 ScanKeyInit(&key[1],
16988 Anum_pg_depend_objid,
16989 BTEqualStrategyNumber, F_OIDEQ,
16990 ObjectIdGetDatum(relid));
16991 ScanKeyInit(&key[2],
16992 Anum_pg_depend_objsubid,
16993 BTEqualStrategyNumber, F_INT4EQ,
16994 Int32GetDatum(0));
16996 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16997 NULL, 3, key);
16999 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17001 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17003 if (dep->refclassid == refclassid &&
17004 dep->refobjid == refobjid &&
17005 dep->refobjsubid == 0 &&
17006 dep->deptype == deptype)
17007 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17010 systable_endscan(scan);
17011 table_close(catalogRelation, RowExclusiveLock);
17015 * ALTER TABLE OF
17017 * Attach a table to a composite type, as though it had been created with CREATE
17018 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17019 * subject table must not have inheritance parents. These restrictions ensure
17020 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17022 * The address of the type is returned.
17024 static ObjectAddress
17025 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17027 Oid relid = RelationGetRelid(rel);
17028 Type typetuple;
17029 Form_pg_type typeform;
17030 Oid typeid;
17031 Relation inheritsRelation,
17032 relationRelation;
17033 SysScanDesc scan;
17034 ScanKeyData key;
17035 AttrNumber table_attno,
17036 type_attno;
17037 TupleDesc typeTupleDesc,
17038 tableTupleDesc;
17039 ObjectAddress tableobj,
17040 typeobj;
17041 HeapTuple classtuple;
17043 /* Validate the type. */
17044 typetuple = typenameType(NULL, ofTypename, NULL);
17045 check_of_type(typetuple);
17046 typeform = (Form_pg_type) GETSTRUCT(typetuple);
17047 typeid = typeform->oid;
17049 /* Fail if the table has any inheritance parents. */
17050 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17051 ScanKeyInit(&key,
17052 Anum_pg_inherits_inhrelid,
17053 BTEqualStrategyNumber, F_OIDEQ,
17054 ObjectIdGetDatum(relid));
17055 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17056 true, NULL, 1, &key);
17057 if (HeapTupleIsValid(systable_getnext(scan)))
17058 ereport(ERROR,
17059 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17060 errmsg("typed tables cannot inherit")));
17061 systable_endscan(scan);
17062 table_close(inheritsRelation, AccessShareLock);
17065 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17066 * require that the order also match. However, attnotnull need not match.
17068 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17069 tableTupleDesc = RelationGetDescr(rel);
17070 table_attno = 1;
17071 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17073 Form_pg_attribute type_attr,
17074 table_attr;
17075 const char *type_attname,
17076 *table_attname;
17078 /* Get the next non-dropped type attribute. */
17079 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17080 if (type_attr->attisdropped)
17081 continue;
17082 type_attname = NameStr(type_attr->attname);
17084 /* Get the next non-dropped table attribute. */
17087 if (table_attno > tableTupleDesc->natts)
17088 ereport(ERROR,
17089 (errcode(ERRCODE_DATATYPE_MISMATCH),
17090 errmsg("table is missing column \"%s\"",
17091 type_attname)));
17092 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17093 table_attno++;
17094 } while (table_attr->attisdropped);
17095 table_attname = NameStr(table_attr->attname);
17097 /* Compare name. */
17098 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17099 ereport(ERROR,
17100 (errcode(ERRCODE_DATATYPE_MISMATCH),
17101 errmsg("table has column \"%s\" where type requires \"%s\"",
17102 table_attname, type_attname)));
17104 /* Compare type. */
17105 if (table_attr->atttypid != type_attr->atttypid ||
17106 table_attr->atttypmod != type_attr->atttypmod ||
17107 table_attr->attcollation != type_attr->attcollation)
17108 ereport(ERROR,
17109 (errcode(ERRCODE_DATATYPE_MISMATCH),
17110 errmsg("table \"%s\" has different type for column \"%s\"",
17111 RelationGetRelationName(rel), type_attname)));
17113 ReleaseTupleDesc(typeTupleDesc);
17115 /* Any remaining columns at the end of the table had better be dropped. */
17116 for (; table_attno <= tableTupleDesc->natts; table_attno++)
17118 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17119 table_attno - 1);
17121 if (!table_attr->attisdropped)
17122 ereport(ERROR,
17123 (errcode(ERRCODE_DATATYPE_MISMATCH),
17124 errmsg("table has extra column \"%s\"",
17125 NameStr(table_attr->attname))));
17128 /* If the table was already typed, drop the existing dependency. */
17129 if (rel->rd_rel->reloftype)
17130 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17131 DEPENDENCY_NORMAL);
17133 /* Record a dependency on the new type. */
17134 tableobj.classId = RelationRelationId;
17135 tableobj.objectId = relid;
17136 tableobj.objectSubId = 0;
17137 typeobj.classId = TypeRelationId;
17138 typeobj.objectId = typeid;
17139 typeobj.objectSubId = 0;
17140 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17142 /* Update pg_class.reloftype */
17143 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17144 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17145 if (!HeapTupleIsValid(classtuple))
17146 elog(ERROR, "cache lookup failed for relation %u", relid);
17147 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17148 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17150 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17152 heap_freetuple(classtuple);
17153 table_close(relationRelation, RowExclusiveLock);
17155 ReleaseSysCache(typetuple);
17157 return typeobj;
17161 * ALTER TABLE NOT OF
17163 * Detach a typed table from its originating type. Just clear reloftype and
17164 * remove the dependency.
17166 static void
17167 ATExecDropOf(Relation rel, LOCKMODE lockmode)
17169 Oid relid = RelationGetRelid(rel);
17170 Relation relationRelation;
17171 HeapTuple tuple;
17173 if (!OidIsValid(rel->rd_rel->reloftype))
17174 ereport(ERROR,
17175 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17176 errmsg("\"%s\" is not a typed table",
17177 RelationGetRelationName(rel))));
17180 * We don't bother to check ownership of the type --- ownership of the
17181 * table is presumed enough rights. No lock required on the type, either.
17184 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17185 DEPENDENCY_NORMAL);
17187 /* Clear pg_class.reloftype */
17188 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17189 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17190 if (!HeapTupleIsValid(tuple))
17191 elog(ERROR, "cache lookup failed for relation %u", relid);
17192 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17193 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17195 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17197 heap_freetuple(tuple);
17198 table_close(relationRelation, RowExclusiveLock);
17202 * relation_mark_replica_identity: Update a table's replica identity
17204 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17205 * index. Otherwise, it must be InvalidOid.
17207 * Caller had better hold an exclusive lock on the relation, as the results
17208 * of running two of these concurrently wouldn't be pretty.
17210 static void
17211 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17212 bool is_internal)
17214 Relation pg_index;
17215 Relation pg_class;
17216 HeapTuple pg_class_tuple;
17217 HeapTuple pg_index_tuple;
17218 Form_pg_class pg_class_form;
17219 Form_pg_index pg_index_form;
17220 ListCell *index;
17223 * Check whether relreplident has changed, and update it if so.
17225 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17226 pg_class_tuple = SearchSysCacheCopy1(RELOID,
17227 ObjectIdGetDatum(RelationGetRelid(rel)));
17228 if (!HeapTupleIsValid(pg_class_tuple))
17229 elog(ERROR, "cache lookup failed for relation \"%s\"",
17230 RelationGetRelationName(rel));
17231 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17232 if (pg_class_form->relreplident != ri_type)
17234 pg_class_form->relreplident = ri_type;
17235 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17237 table_close(pg_class, RowExclusiveLock);
17238 heap_freetuple(pg_class_tuple);
17241 * Update the per-index indisreplident flags correctly.
17243 pg_index = table_open(IndexRelationId, RowExclusiveLock);
17244 foreach(index, RelationGetIndexList(rel))
17246 Oid thisIndexOid = lfirst_oid(index);
17247 bool dirty = false;
17249 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17250 ObjectIdGetDatum(thisIndexOid));
17251 if (!HeapTupleIsValid(pg_index_tuple))
17252 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17253 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17255 if (thisIndexOid == indexOid)
17257 /* Set the bit if not already set. */
17258 if (!pg_index_form->indisreplident)
17260 dirty = true;
17261 pg_index_form->indisreplident = true;
17264 else
17266 /* Unset the bit if set. */
17267 if (pg_index_form->indisreplident)
17269 dirty = true;
17270 pg_index_form->indisreplident = false;
17274 if (dirty)
17276 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17277 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17278 InvalidOid, is_internal);
17281 * Invalidate the relcache for the table, so that after we commit
17282 * all sessions will refresh the table's replica identity index
17283 * before attempting any UPDATE or DELETE on the table. (If we
17284 * changed the table's pg_class row above, then a relcache inval
17285 * is already queued due to that; but we might not have.)
17287 CacheInvalidateRelcache(rel);
17289 heap_freetuple(pg_index_tuple);
17292 table_close(pg_index, RowExclusiveLock);
17296 * ALTER TABLE <name> REPLICA IDENTITY ...
17298 static void
17299 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17301 Oid indexOid;
17302 Relation indexRel;
17303 int key;
17305 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17307 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17308 return;
17310 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17312 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17313 return;
17315 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17317 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17318 return;
17320 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17322 /* fallthrough */ ;
17324 else
17325 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17327 /* Check that the index exists */
17328 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17329 if (!OidIsValid(indexOid))
17330 ereport(ERROR,
17331 (errcode(ERRCODE_UNDEFINED_OBJECT),
17332 errmsg("index \"%s\" for table \"%s\" does not exist",
17333 stmt->name, RelationGetRelationName(rel))));
17335 indexRel = index_open(indexOid, ShareLock);
17337 /* Check that the index is on the relation we're altering. */
17338 if (indexRel->rd_index == NULL ||
17339 indexRel->rd_index->indrelid != RelationGetRelid(rel))
17340 ereport(ERROR,
17341 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17342 errmsg("\"%s\" is not an index for table \"%s\"",
17343 RelationGetRelationName(indexRel),
17344 RelationGetRelationName(rel))));
17347 * The AM must support uniqueness, and the index must in fact be unique.
17348 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
17349 * exclusion), we can use that too.
17351 if ((!indexRel->rd_indam->amcanunique ||
17352 !indexRel->rd_index->indisunique) &&
17353 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
17354 ereport(ERROR,
17355 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17356 errmsg("cannot use non-unique index \"%s\" as replica identity",
17357 RelationGetRelationName(indexRel))));
17358 /* Deferred indexes are not guaranteed to be always unique. */
17359 if (!indexRel->rd_index->indimmediate)
17360 ereport(ERROR,
17361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17362 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17363 RelationGetRelationName(indexRel))));
17364 /* Expression indexes aren't supported. */
17365 if (RelationGetIndexExpressions(indexRel) != NIL)
17366 ereport(ERROR,
17367 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17368 errmsg("cannot use expression index \"%s\" as replica identity",
17369 RelationGetRelationName(indexRel))));
17370 /* Predicate indexes aren't supported. */
17371 if (RelationGetIndexPredicate(indexRel) != NIL)
17372 ereport(ERROR,
17373 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17374 errmsg("cannot use partial index \"%s\" as replica identity",
17375 RelationGetRelationName(indexRel))));
17377 /* Check index for nullable columns. */
17378 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17380 int16 attno = indexRel->rd_index->indkey.values[key];
17381 Form_pg_attribute attr;
17384 * Reject any other system columns. (Going forward, we'll disallow
17385 * indexes containing such columns in the first place, but they might
17386 * exist in older branches.)
17388 if (attno <= 0)
17389 ereport(ERROR,
17390 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17391 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17392 RelationGetRelationName(indexRel), attno)));
17394 attr = TupleDescAttr(rel->rd_att, attno - 1);
17395 if (!attr->attnotnull)
17396 ereport(ERROR,
17397 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17398 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17399 RelationGetRelationName(indexRel),
17400 NameStr(attr->attname))));
17403 /* This index is suitable for use as a replica identity. Mark it. */
17404 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17406 index_close(indexRel, NoLock);
17410 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17412 static void
17413 ATExecSetRowSecurity(Relation rel, bool rls)
17415 Relation pg_class;
17416 Oid relid;
17417 HeapTuple tuple;
17419 relid = RelationGetRelid(rel);
17421 /* Pull the record for this relation and update it */
17422 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17424 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17426 if (!HeapTupleIsValid(tuple))
17427 elog(ERROR, "cache lookup failed for relation %u", relid);
17429 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17430 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17432 InvokeObjectPostAlterHook(RelationRelationId,
17433 RelationGetRelid(rel), 0);
17435 table_close(pg_class, RowExclusiveLock);
17436 heap_freetuple(tuple);
17440 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17442 static void
17443 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17445 Relation pg_class;
17446 Oid relid;
17447 HeapTuple tuple;
17449 relid = RelationGetRelid(rel);
17451 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17453 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17455 if (!HeapTupleIsValid(tuple))
17456 elog(ERROR, "cache lookup failed for relation %u", relid);
17458 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17459 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17461 InvokeObjectPostAlterHook(RelationRelationId,
17462 RelationGetRelid(rel), 0);
17464 table_close(pg_class, RowExclusiveLock);
17465 heap_freetuple(tuple);
17469 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17471 static void
17472 ATExecGenericOptions(Relation rel, List *options)
17474 Relation ftrel;
17475 ForeignServer *server;
17476 ForeignDataWrapper *fdw;
17477 HeapTuple tuple;
17478 bool isnull;
17479 Datum repl_val[Natts_pg_foreign_table];
17480 bool repl_null[Natts_pg_foreign_table];
17481 bool repl_repl[Natts_pg_foreign_table];
17482 Datum datum;
17483 Form_pg_foreign_table tableform;
17485 if (options == NIL)
17486 return;
17488 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17490 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17491 ObjectIdGetDatum(rel->rd_id));
17492 if (!HeapTupleIsValid(tuple))
17493 ereport(ERROR,
17494 (errcode(ERRCODE_UNDEFINED_OBJECT),
17495 errmsg("foreign table \"%s\" does not exist",
17496 RelationGetRelationName(rel))));
17497 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17498 server = GetForeignServer(tableform->ftserver);
17499 fdw = GetForeignDataWrapper(server->fdwid);
17501 memset(repl_val, 0, sizeof(repl_val));
17502 memset(repl_null, false, sizeof(repl_null));
17503 memset(repl_repl, false, sizeof(repl_repl));
17505 /* Extract the current options */
17506 datum = SysCacheGetAttr(FOREIGNTABLEREL,
17507 tuple,
17508 Anum_pg_foreign_table_ftoptions,
17509 &isnull);
17510 if (isnull)
17511 datum = PointerGetDatum(NULL);
17513 /* Transform the options */
17514 datum = transformGenericOptions(ForeignTableRelationId,
17515 datum,
17516 options,
17517 fdw->fdwvalidator);
17519 if (PointerIsValid(DatumGetPointer(datum)))
17520 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17521 else
17522 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17524 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17526 /* Everything looks good - update the tuple */
17528 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17529 repl_val, repl_null, repl_repl);
17531 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17534 * Invalidate relcache so that all sessions will refresh any cached plans
17535 * that might depend on the old options.
17537 CacheInvalidateRelcache(rel);
17539 InvokeObjectPostAlterHook(ForeignTableRelationId,
17540 RelationGetRelid(rel), 0);
17542 table_close(ftrel, RowExclusiveLock);
17544 heap_freetuple(tuple);
17548 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17550 * Return value is the address of the modified column
17552 static ObjectAddress
17553 ATExecSetCompression(Relation rel,
17554 const char *column,
17555 Node *newValue,
17556 LOCKMODE lockmode)
17558 Relation attrel;
17559 HeapTuple tuple;
17560 Form_pg_attribute atttableform;
17561 AttrNumber attnum;
17562 char *compression;
17563 char cmethod;
17564 ObjectAddress address;
17566 compression = strVal(newValue);
17568 attrel = table_open(AttributeRelationId, RowExclusiveLock);
17570 /* copy the cache entry so we can scribble on it below */
17571 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17572 if (!HeapTupleIsValid(tuple))
17573 ereport(ERROR,
17574 (errcode(ERRCODE_UNDEFINED_COLUMN),
17575 errmsg("column \"%s\" of relation \"%s\" does not exist",
17576 column, RelationGetRelationName(rel))));
17578 /* prevent them from altering a system attribute */
17579 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17580 attnum = atttableform->attnum;
17581 if (attnum <= 0)
17582 ereport(ERROR,
17583 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17584 errmsg("cannot alter system column \"%s\"", column)));
17587 * Check that column type is compressible, then get the attribute
17588 * compression method code
17590 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17592 /* update pg_attribute entry */
17593 atttableform->attcompression = cmethod;
17594 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17596 InvokeObjectPostAlterHook(RelationRelationId,
17597 RelationGetRelid(rel),
17598 attnum);
17601 * Apply the change to indexes as well (only for simple index columns,
17602 * matching behavior of index.c ConstructTupleDescriptor()).
17604 SetIndexStorageProperties(rel, attrel, attnum,
17605 false, 0,
17606 true, cmethod,
17607 lockmode);
17609 heap_freetuple(tuple);
17611 table_close(attrel, RowExclusiveLock);
17613 /* make changes visible */
17614 CommandCounterIncrement();
17616 ObjectAddressSubSet(address, RelationRelationId,
17617 RelationGetRelid(rel), attnum);
17618 return address;
17623 * Preparation phase for SET LOGGED/UNLOGGED
17625 * This verifies that we're not trying to change a temp table. Also,
17626 * existing foreign key constraints are checked to avoid ending up with
17627 * permanent tables referencing unlogged tables.
17629 static void
17630 ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
17632 Relation pg_constraint;
17633 HeapTuple tuple;
17634 SysScanDesc scan;
17635 ScanKeyData skey[1];
17638 * Disallow changing status for a temp table. Also verify whether we can
17639 * get away with doing nothing; in such cases we don't need to run the
17640 * checks below, either.
17642 switch (rel->rd_rel->relpersistence)
17644 case RELPERSISTENCE_TEMP:
17645 ereport(ERROR,
17646 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17647 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17648 RelationGetRelationName(rel)),
17649 errtable(rel)));
17650 break;
17651 case RELPERSISTENCE_PERMANENT:
17652 if (toLogged)
17653 /* nothing to do */
17654 return;
17655 break;
17656 case RELPERSISTENCE_UNLOGGED:
17657 if (!toLogged)
17658 /* nothing to do */
17659 return;
17660 break;
17664 * Check that the table is not part of any publication when changing to
17665 * UNLOGGED, as UNLOGGED tables can't be published.
17667 if (!toLogged &&
17668 GetRelationPublications(RelationGetRelid(rel)) != NIL)
17669 ereport(ERROR,
17670 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17671 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17672 RelationGetRelationName(rel)),
17673 errdetail("Unlogged relations cannot be replicated.")));
17676 * Check existing foreign key constraints to preserve the invariant that
17677 * permanent tables cannot reference unlogged ones. Self-referencing
17678 * foreign keys can safely be ignored.
17680 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17683 * Scan conrelid if changing to permanent, else confrelid. This also
17684 * determines whether a useful index exists.
17686 ScanKeyInit(&skey[0],
17687 toLogged ? Anum_pg_constraint_conrelid :
17688 Anum_pg_constraint_confrelid,
17689 BTEqualStrategyNumber, F_OIDEQ,
17690 ObjectIdGetDatum(RelationGetRelid(rel)));
17691 scan = systable_beginscan(pg_constraint,
17692 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17693 true, NULL, 1, skey);
17695 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17697 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17699 if (con->contype == CONSTRAINT_FOREIGN)
17701 Oid foreignrelid;
17702 Relation foreignrel;
17704 /* the opposite end of what we used as scankey */
17705 foreignrelid = toLogged ? con->confrelid : con->conrelid;
17707 /* ignore if self-referencing */
17708 if (RelationGetRelid(rel) == foreignrelid)
17709 continue;
17711 foreignrel = relation_open(foreignrelid, AccessShareLock);
17713 if (toLogged)
17715 if (!RelationIsPermanent(foreignrel))
17716 ereport(ERROR,
17717 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17718 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17719 RelationGetRelationName(rel),
17720 RelationGetRelationName(foreignrel)),
17721 errtableconstraint(rel, NameStr(con->conname))));
17723 else
17725 if (RelationIsPermanent(foreignrel))
17726 ereport(ERROR,
17727 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17728 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17729 RelationGetRelationName(rel),
17730 RelationGetRelationName(foreignrel)),
17731 errtableconstraint(rel, NameStr(con->conname))));
17734 relation_close(foreignrel, AccessShareLock);
17738 systable_endscan(scan);
17740 table_close(pg_constraint, AccessShareLock);
17742 /* force rewrite if necessary; see comment in ATRewriteTables */
17743 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
17744 if (toLogged)
17745 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
17746 else
17747 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
17748 tab->chgPersistence = true;
17752 * Execute ALTER TABLE SET SCHEMA
17754 ObjectAddress
17755 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17757 Relation rel;
17758 Oid relid;
17759 Oid oldNspOid;
17760 Oid nspOid;
17761 RangeVar *newrv;
17762 ObjectAddresses *objsMoved;
17763 ObjectAddress myself;
17765 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17766 stmt->missing_ok ? RVR_MISSING_OK : 0,
17767 RangeVarCallbackForAlterRelation,
17768 stmt);
17770 if (!OidIsValid(relid))
17772 ereport(NOTICE,
17773 (errmsg("relation \"%s\" does not exist, skipping",
17774 stmt->relation->relname)));
17775 return InvalidObjectAddress;
17778 rel = relation_open(relid, NoLock);
17780 oldNspOid = RelationGetNamespace(rel);
17782 /* If it's an owned sequence, disallow moving it by itself. */
17783 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17785 Oid tableId;
17786 int32 colId;
17788 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17789 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17790 ereport(ERROR,
17791 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17792 errmsg("cannot move an owned sequence into another schema"),
17793 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17794 RelationGetRelationName(rel),
17795 get_rel_name(tableId))));
17798 /* Get and lock schema OID and check its permissions. */
17799 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17800 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17802 /* common checks on switching namespaces */
17803 CheckSetNamespace(oldNspOid, nspOid);
17805 objsMoved = new_object_addresses();
17806 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17807 free_object_addresses(objsMoved);
17809 ObjectAddressSet(myself, RelationRelationId, relid);
17811 if (oldschema)
17812 *oldschema = oldNspOid;
17814 /* close rel, but keep lock until commit */
17815 relation_close(rel, NoLock);
17817 return myself;
17821 * The guts of relocating a table or materialized view to another namespace:
17822 * besides moving the relation itself, its dependent objects are relocated to
17823 * the new schema.
17825 void
17826 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17827 ObjectAddresses *objsMoved)
17829 Relation classRel;
17831 Assert(objsMoved != NULL);
17833 /* OK, modify the pg_class row and pg_depend entry */
17834 classRel = table_open(RelationRelationId, RowExclusiveLock);
17836 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17837 nspOid, true, objsMoved);
17839 /* Fix the table's row type too, if it has one */
17840 if (OidIsValid(rel->rd_rel->reltype))
17841 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17842 false, /* isImplicitArray */
17843 false, /* ignoreDependent */
17844 false, /* errorOnTableType */
17845 objsMoved);
17847 /* Fix other dependent stuff */
17848 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17849 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17850 objsMoved, AccessExclusiveLock);
17851 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17852 false, objsMoved);
17854 table_close(classRel, RowExclusiveLock);
17858 * The guts of relocating a relation to another namespace: fix the pg_class
17859 * entry, and the pg_depend entry if any. Caller must already have
17860 * opened and write-locked pg_class.
17862 void
17863 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17864 Oid oldNspOid, Oid newNspOid,
17865 bool hasDependEntry,
17866 ObjectAddresses *objsMoved)
17868 HeapTuple classTup;
17869 Form_pg_class classForm;
17870 ObjectAddress thisobj;
17871 bool already_done = false;
17873 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
17874 classTup = SearchSysCacheLockedCopy1(RELOID, ObjectIdGetDatum(relOid));
17875 if (!HeapTupleIsValid(classTup))
17876 elog(ERROR, "cache lookup failed for relation %u", relOid);
17877 classForm = (Form_pg_class) GETSTRUCT(classTup);
17879 Assert(classForm->relnamespace == oldNspOid);
17881 thisobj.classId = RelationRelationId;
17882 thisobj.objectId = relOid;
17883 thisobj.objectSubId = 0;
17886 * If the object has already been moved, don't move it again. If it's
17887 * already in the right place, don't move it, but still fire the object
17888 * access hook.
17890 already_done = object_address_present(&thisobj, objsMoved);
17891 if (!already_done && oldNspOid != newNspOid)
17893 ItemPointerData otid = classTup->t_self;
17895 /* check for duplicate name (more friendly than unique-index failure) */
17896 if (get_relname_relid(NameStr(classForm->relname),
17897 newNspOid) != InvalidOid)
17898 ereport(ERROR,
17899 (errcode(ERRCODE_DUPLICATE_TABLE),
17900 errmsg("relation \"%s\" already exists in schema \"%s\"",
17901 NameStr(classForm->relname),
17902 get_namespace_name(newNspOid))));
17904 /* classTup is a copy, so OK to scribble on */
17905 classForm->relnamespace = newNspOid;
17907 CatalogTupleUpdate(classRel, &otid, classTup);
17908 UnlockTuple(classRel, &otid, InplaceUpdateTupleLock);
17911 /* Update dependency on schema if caller said so */
17912 if (hasDependEntry &&
17913 changeDependencyFor(RelationRelationId,
17914 relOid,
17915 NamespaceRelationId,
17916 oldNspOid,
17917 newNspOid) != 1)
17918 elog(ERROR, "could not change schema dependency for relation \"%s\"",
17919 NameStr(classForm->relname));
17921 else
17922 UnlockTuple(classRel, &classTup->t_self, InplaceUpdateTupleLock);
17923 if (!already_done)
17925 add_exact_object_address(&thisobj, objsMoved);
17927 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17930 heap_freetuple(classTup);
17934 * Move all indexes for the specified relation to another namespace.
17936 * Note: we assume adequate permission checking was done by the caller,
17937 * and that the caller has a suitable lock on the owning relation.
17939 static void
17940 AlterIndexNamespaces(Relation classRel, Relation rel,
17941 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17943 List *indexList;
17944 ListCell *l;
17946 indexList = RelationGetIndexList(rel);
17948 foreach(l, indexList)
17950 Oid indexOid = lfirst_oid(l);
17951 ObjectAddress thisobj;
17953 thisobj.classId = RelationRelationId;
17954 thisobj.objectId = indexOid;
17955 thisobj.objectSubId = 0;
17958 * Note: currently, the index will not have its own dependency on the
17959 * namespace, so we don't need to do changeDependencyFor(). There's no
17960 * row type in pg_type, either.
17962 * XXX this objsMoved test may be pointless -- surely we have a single
17963 * dependency link from a relation to each index?
17965 if (!object_address_present(&thisobj, objsMoved))
17967 AlterRelationNamespaceInternal(classRel, indexOid,
17968 oldNspOid, newNspOid,
17969 false, objsMoved);
17970 add_exact_object_address(&thisobj, objsMoved);
17974 list_free(indexList);
17978 * Move all identity and SERIAL-column sequences of the specified relation to another
17979 * namespace.
17981 * Note: we assume adequate permission checking was done by the caller,
17982 * and that the caller has a suitable lock on the owning relation.
17984 static void
17985 AlterSeqNamespaces(Relation classRel, Relation rel,
17986 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17987 LOCKMODE lockmode)
17989 Relation depRel;
17990 SysScanDesc scan;
17991 ScanKeyData key[2];
17992 HeapTuple tup;
17995 * SERIAL sequences are those having an auto dependency on one of the
17996 * table's columns (we don't care *which* column, exactly).
17998 depRel = table_open(DependRelationId, AccessShareLock);
18000 ScanKeyInit(&key[0],
18001 Anum_pg_depend_refclassid,
18002 BTEqualStrategyNumber, F_OIDEQ,
18003 ObjectIdGetDatum(RelationRelationId));
18004 ScanKeyInit(&key[1],
18005 Anum_pg_depend_refobjid,
18006 BTEqualStrategyNumber, F_OIDEQ,
18007 ObjectIdGetDatum(RelationGetRelid(rel)));
18008 /* we leave refobjsubid unspecified */
18010 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18011 NULL, 2, key);
18013 while (HeapTupleIsValid(tup = systable_getnext(scan)))
18015 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18016 Relation seqRel;
18018 /* skip dependencies other than auto dependencies on columns */
18019 if (depForm->refobjsubid == 0 ||
18020 depForm->classid != RelationRelationId ||
18021 depForm->objsubid != 0 ||
18022 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18023 continue;
18025 /* Use relation_open just in case it's an index */
18026 seqRel = relation_open(depForm->objid, lockmode);
18028 /* skip non-sequence relations */
18029 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18031 /* No need to keep the lock */
18032 relation_close(seqRel, lockmode);
18033 continue;
18036 /* Fix the pg_class and pg_depend entries */
18037 AlterRelationNamespaceInternal(classRel, depForm->objid,
18038 oldNspOid, newNspOid,
18039 true, objsMoved);
18042 * Sequences used to have entries in pg_type, but no longer do. If we
18043 * ever re-instate that, we'll need to move the pg_type entry to the
18044 * new namespace, too (using AlterTypeNamespaceInternal).
18046 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18048 /* Now we can close it. Keep the lock till end of transaction. */
18049 relation_close(seqRel, NoLock);
18052 systable_endscan(scan);
18054 relation_close(depRel, AccessShareLock);
18059 * This code supports
18060 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18062 * Because we only support this for TEMP tables, it's sufficient to remember
18063 * the state in a backend-local data structure.
18067 * Register a newly-created relation's ON COMMIT action.
18069 void
18070 register_on_commit_action(Oid relid, OnCommitAction action)
18072 OnCommitItem *oc;
18073 MemoryContext oldcxt;
18076 * We needn't bother registering the relation unless there is an ON COMMIT
18077 * action we need to take.
18079 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18080 return;
18082 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18084 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18085 oc->relid = relid;
18086 oc->oncommit = action;
18087 oc->creating_subid = GetCurrentSubTransactionId();
18088 oc->deleting_subid = InvalidSubTransactionId;
18091 * We use lcons() here so that ON COMMIT actions are processed in reverse
18092 * order of registration. That might not be essential but it seems
18093 * reasonable.
18095 on_commits = lcons(oc, on_commits);
18097 MemoryContextSwitchTo(oldcxt);
18101 * Unregister any ON COMMIT action when a relation is deleted.
18103 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18105 void
18106 remove_on_commit_action(Oid relid)
18108 ListCell *l;
18110 foreach(l, on_commits)
18112 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18114 if (oc->relid == relid)
18116 oc->deleting_subid = GetCurrentSubTransactionId();
18117 break;
18123 * Perform ON COMMIT actions.
18125 * This is invoked just before actually committing, since it's possible
18126 * to encounter errors.
18128 void
18129 PreCommit_on_commit_actions(void)
18131 ListCell *l;
18132 List *oids_to_truncate = NIL;
18133 List *oids_to_drop = NIL;
18135 foreach(l, on_commits)
18137 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18139 /* Ignore entry if already dropped in this xact */
18140 if (oc->deleting_subid != InvalidSubTransactionId)
18141 continue;
18143 switch (oc->oncommit)
18145 case ONCOMMIT_NOOP:
18146 case ONCOMMIT_PRESERVE_ROWS:
18147 /* Do nothing (there shouldn't be such entries, actually) */
18148 break;
18149 case ONCOMMIT_DELETE_ROWS:
18152 * If this transaction hasn't accessed any temporary
18153 * relations, we can skip truncating ON COMMIT DELETE ROWS
18154 * tables, as they must still be empty.
18156 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18157 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18158 break;
18159 case ONCOMMIT_DROP:
18160 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18161 break;
18166 * Truncate relations before dropping so that all dependencies between
18167 * relations are removed after they are worked on. Doing it like this
18168 * might be a waste as it is possible that a relation being truncated will
18169 * be dropped anyway due to its parent being dropped, but this makes the
18170 * code more robust because of not having to re-check that the relation
18171 * exists at truncation time.
18173 if (oids_to_truncate != NIL)
18174 heap_truncate(oids_to_truncate);
18176 if (oids_to_drop != NIL)
18178 ObjectAddresses *targetObjects = new_object_addresses();
18180 foreach(l, oids_to_drop)
18182 ObjectAddress object;
18184 object.classId = RelationRelationId;
18185 object.objectId = lfirst_oid(l);
18186 object.objectSubId = 0;
18188 Assert(!object_address_present(&object, targetObjects));
18190 add_exact_object_address(&object, targetObjects);
18194 * Object deletion might involve toast table access (to clean up
18195 * toasted catalog entries), so ensure we have a valid snapshot.
18197 PushActiveSnapshot(GetTransactionSnapshot());
18200 * Since this is an automatic drop, rather than one directly initiated
18201 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18203 performMultipleDeletions(targetObjects, DROP_CASCADE,
18204 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18206 PopActiveSnapshot();
18208 #ifdef USE_ASSERT_CHECKING
18211 * Note that table deletion will call remove_on_commit_action, so the
18212 * entry should get marked as deleted.
18214 foreach(l, on_commits)
18216 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18218 if (oc->oncommit != ONCOMMIT_DROP)
18219 continue;
18221 Assert(oc->deleting_subid != InvalidSubTransactionId);
18223 #endif
18228 * Post-commit or post-abort cleanup for ON COMMIT management.
18230 * All we do here is remove no-longer-needed OnCommitItem entries.
18232 * During commit, remove entries that were deleted during this transaction;
18233 * during abort, remove those created during this transaction.
18235 void
18236 AtEOXact_on_commit_actions(bool isCommit)
18238 ListCell *cur_item;
18240 foreach(cur_item, on_commits)
18242 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18244 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18245 oc->creating_subid != InvalidSubTransactionId)
18247 /* cur_item must be removed */
18248 on_commits = foreach_delete_current(on_commits, cur_item);
18249 pfree(oc);
18251 else
18253 /* cur_item must be preserved */
18254 oc->creating_subid = InvalidSubTransactionId;
18255 oc->deleting_subid = InvalidSubTransactionId;
18261 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18263 * During subabort, we can immediately remove entries created during this
18264 * subtransaction. During subcommit, just relabel entries marked during
18265 * this subtransaction as being the parent's responsibility.
18267 void
18268 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18269 SubTransactionId parentSubid)
18271 ListCell *cur_item;
18273 foreach(cur_item, on_commits)
18275 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18277 if (!isCommit && oc->creating_subid == mySubid)
18279 /* cur_item must be removed */
18280 on_commits = foreach_delete_current(on_commits, cur_item);
18281 pfree(oc);
18283 else
18285 /* cur_item must be preserved */
18286 if (oc->creating_subid == mySubid)
18287 oc->creating_subid = parentSubid;
18288 if (oc->deleting_subid == mySubid)
18289 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18295 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18296 * the relation to be locked only if (1) it's a plain or partitioned table,
18297 * materialized view, or TOAST table and (2) the current user is the owner (or
18298 * the superuser) or has been granted MAINTAIN. This meets the
18299 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18300 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18302 void
18303 RangeVarCallbackMaintainsTable(const RangeVar *relation,
18304 Oid relId, Oid oldRelId, void *arg)
18306 char relkind;
18307 AclResult aclresult;
18309 /* Nothing to do if the relation was not found. */
18310 if (!OidIsValid(relId))
18311 return;
18314 * If the relation does exist, check whether it's an index. But note that
18315 * the relation might have been dropped between the time we did the name
18316 * lookup and now. In that case, there's nothing to do.
18318 relkind = get_rel_relkind(relId);
18319 if (!relkind)
18320 return;
18321 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18322 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18323 ereport(ERROR,
18324 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18325 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18327 /* Check permissions */
18328 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18329 if (aclresult != ACLCHECK_OK)
18330 aclcheck_error(aclresult,
18331 get_relkind_objtype(get_rel_relkind(relId)),
18332 relation->relname);
18336 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18338 static void
18339 RangeVarCallbackForTruncate(const RangeVar *relation,
18340 Oid relId, Oid oldRelId, void *arg)
18342 HeapTuple tuple;
18344 /* Nothing to do if the relation was not found. */
18345 if (!OidIsValid(relId))
18346 return;
18348 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18349 if (!HeapTupleIsValid(tuple)) /* should not happen */
18350 elog(ERROR, "cache lookup failed for relation %u", relId);
18352 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18353 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18355 ReleaseSysCache(tuple);
18359 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18360 * the owner of the relation, or superuser.
18362 void
18363 RangeVarCallbackOwnsRelation(const RangeVar *relation,
18364 Oid relId, Oid oldRelId, void *arg)
18366 HeapTuple tuple;
18368 /* Nothing to do if the relation was not found. */
18369 if (!OidIsValid(relId))
18370 return;
18372 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18373 if (!HeapTupleIsValid(tuple)) /* should not happen */
18374 elog(ERROR, "cache lookup failed for relation %u", relId);
18376 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18377 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18378 relation->relname);
18380 if (!allowSystemTableMods &&
18381 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18382 ereport(ERROR,
18383 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18384 errmsg("permission denied: \"%s\" is a system catalog",
18385 relation->relname)));
18387 ReleaseSysCache(tuple);
18391 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18392 * processing.
18394 static void
18395 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18396 void *arg)
18398 Node *stmt = (Node *) arg;
18399 ObjectType reltype;
18400 HeapTuple tuple;
18401 Form_pg_class classform;
18402 AclResult aclresult;
18403 char relkind;
18405 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18406 if (!HeapTupleIsValid(tuple))
18407 return; /* concurrently dropped */
18408 classform = (Form_pg_class) GETSTRUCT(tuple);
18409 relkind = classform->relkind;
18411 /* Must own relation. */
18412 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18413 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18415 /* No system table modifications unless explicitly allowed. */
18416 if (!allowSystemTableMods && IsSystemClass(relid, classform))
18417 ereport(ERROR,
18418 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18419 errmsg("permission denied: \"%s\" is a system catalog",
18420 rv->relname)));
18423 * Extract the specified relation type from the statement parse tree.
18425 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18426 * have CREATE rights on the containing namespace.
18428 if (IsA(stmt, RenameStmt))
18430 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18431 GetUserId(), ACL_CREATE);
18432 if (aclresult != ACLCHECK_OK)
18433 aclcheck_error(aclresult, OBJECT_SCHEMA,
18434 get_namespace_name(classform->relnamespace));
18435 reltype = ((RenameStmt *) stmt)->renameType;
18437 else if (IsA(stmt, AlterObjectSchemaStmt))
18438 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18440 else if (IsA(stmt, AlterTableStmt))
18441 reltype = ((AlterTableStmt *) stmt)->objtype;
18442 else
18444 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18445 reltype = OBJECT_TABLE; /* placate compiler */
18449 * For compatibility with prior releases, we allow ALTER TABLE to be used
18450 * with most other types of relations (but not composite types). We allow
18451 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18452 * otherwise. Otherwise, the user must select the correct form of the
18453 * command for the relation at issue.
18455 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18456 ereport(ERROR,
18457 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18458 errmsg("\"%s\" is not a sequence", rv->relname)));
18460 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18461 ereport(ERROR,
18462 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18463 errmsg("\"%s\" is not a view", rv->relname)));
18465 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18466 ereport(ERROR,
18467 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18468 errmsg("\"%s\" is not a materialized view", rv->relname)));
18470 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18471 ereport(ERROR,
18472 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18473 errmsg("\"%s\" is not a foreign table", rv->relname)));
18475 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18476 ereport(ERROR,
18477 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18478 errmsg("\"%s\" is not a composite type", rv->relname)));
18480 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18481 relkind != RELKIND_PARTITIONED_INDEX
18482 && !IsA(stmt, RenameStmt))
18483 ereport(ERROR,
18484 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18485 errmsg("\"%s\" is not an index", rv->relname)));
18488 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18489 * TYPE for that.
18491 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18492 ereport(ERROR,
18493 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18494 errmsg("\"%s\" is a composite type", rv->relname),
18495 /* translator: %s is an SQL ALTER command */
18496 errhint("Use %s instead.",
18497 "ALTER TYPE")));
18500 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18501 * to a different schema, such as indexes and TOAST tables.
18503 if (IsA(stmt, AlterObjectSchemaStmt))
18505 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18506 ereport(ERROR,
18507 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18508 errmsg("cannot change schema of index \"%s\"",
18509 rv->relname),
18510 errhint("Change the schema of the table instead.")));
18511 else if (relkind == RELKIND_COMPOSITE_TYPE)
18512 ereport(ERROR,
18513 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18514 errmsg("cannot change schema of composite type \"%s\"",
18515 rv->relname),
18516 /* translator: %s is an SQL ALTER command */
18517 errhint("Use %s instead.",
18518 "ALTER TYPE")));
18519 else if (relkind == RELKIND_TOASTVALUE)
18520 ereport(ERROR,
18521 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18522 errmsg("cannot change schema of TOAST table \"%s\"",
18523 rv->relname),
18524 errhint("Change the schema of the table instead.")));
18527 ReleaseSysCache(tuple);
18531 * Transform any expressions present in the partition key
18533 * Returns a transformed PartitionSpec.
18535 static PartitionSpec *
18536 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18538 PartitionSpec *newspec;
18539 ParseState *pstate;
18540 ParseNamespaceItem *nsitem;
18541 ListCell *l;
18543 newspec = makeNode(PartitionSpec);
18545 newspec->strategy = partspec->strategy;
18546 newspec->partParams = NIL;
18547 newspec->location = partspec->location;
18549 /* Check valid number of columns for strategy */
18550 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18551 list_length(partspec->partParams) != 1)
18552 ereport(ERROR,
18553 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18554 errmsg("cannot use \"list\" partition strategy with more than one column")));
18557 * Create a dummy ParseState and insert the target relation as its sole
18558 * rangetable entry. We need a ParseState for transformExpr.
18560 pstate = make_parsestate(NULL);
18561 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18562 NULL, false, true);
18563 addNSItemToQuery(pstate, nsitem, true, true, true);
18565 /* take care of any partition expressions */
18566 foreach(l, partspec->partParams)
18568 PartitionElem *pelem = lfirst_node(PartitionElem, l);
18570 if (pelem->expr)
18572 /* Copy, to avoid scribbling on the input */
18573 pelem = copyObject(pelem);
18575 /* Now do parse transformation of the expression */
18576 pelem->expr = transformExpr(pstate, pelem->expr,
18577 EXPR_KIND_PARTITION_EXPRESSION);
18579 /* we have to fix its collations too */
18580 assign_expr_collations(pstate, pelem->expr);
18583 newspec->partParams = lappend(newspec->partParams, pelem);
18586 return newspec;
18590 * Compute per-partition-column information from a list of PartitionElems.
18591 * Expressions in the PartitionElems must be parse-analyzed already.
18593 static void
18594 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18595 List **partexprs, Oid *partopclass, Oid *partcollation,
18596 PartitionStrategy strategy)
18598 int attn;
18599 ListCell *lc;
18600 Oid am_oid;
18602 attn = 0;
18603 foreach(lc, partParams)
18605 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18606 Oid atttype;
18607 Oid attcollation;
18609 if (pelem->name != NULL)
18611 /* Simple attribute reference */
18612 HeapTuple atttuple;
18613 Form_pg_attribute attform;
18615 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18616 pelem->name);
18617 if (!HeapTupleIsValid(atttuple))
18618 ereport(ERROR,
18619 (errcode(ERRCODE_UNDEFINED_COLUMN),
18620 errmsg("column \"%s\" named in partition key does not exist",
18621 pelem->name),
18622 parser_errposition(pstate, pelem->location)));
18623 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18625 if (attform->attnum <= 0)
18626 ereport(ERROR,
18627 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18628 errmsg("cannot use system column \"%s\" in partition key",
18629 pelem->name),
18630 parser_errposition(pstate, pelem->location)));
18633 * Generated columns cannot work: They are computed after BEFORE
18634 * triggers, but partition routing is done before all triggers.
18636 if (attform->attgenerated)
18637 ereport(ERROR,
18638 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18639 errmsg("cannot use generated column in partition key"),
18640 errdetail("Column \"%s\" is a generated column.",
18641 pelem->name),
18642 parser_errposition(pstate, pelem->location)));
18644 partattrs[attn] = attform->attnum;
18645 atttype = attform->atttypid;
18646 attcollation = attform->attcollation;
18647 ReleaseSysCache(atttuple);
18649 else
18651 /* Expression */
18652 Node *expr = pelem->expr;
18653 char partattname[16];
18655 Assert(expr != NULL);
18656 atttype = exprType(expr);
18657 attcollation = exprCollation(expr);
18660 * The expression must be of a storable type (e.g., not RECORD).
18661 * The test is the same as for whether a table column is of a safe
18662 * type (which is why we needn't check for the non-expression
18663 * case).
18665 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18666 CheckAttributeType(partattname,
18667 atttype, attcollation,
18668 NIL, CHKATYPE_IS_PARTKEY);
18671 * Strip any top-level COLLATE clause. This ensures that we treat
18672 * "x COLLATE y" and "(x COLLATE y)" alike.
18674 while (IsA(expr, CollateExpr))
18675 expr = (Node *) ((CollateExpr *) expr)->arg;
18677 if (IsA(expr, Var) &&
18678 ((Var *) expr)->varattno > 0)
18681 * User wrote "(column)" or "(column COLLATE something)".
18682 * Treat it like simple attribute anyway.
18684 partattrs[attn] = ((Var *) expr)->varattno;
18686 else
18688 Bitmapset *expr_attrs = NULL;
18689 int i;
18691 partattrs[attn] = 0; /* marks the column as expression */
18692 *partexprs = lappend(*partexprs, expr);
18695 * transformPartitionSpec() should have already rejected
18696 * subqueries, aggregates, window functions, and SRFs, based
18697 * on the EXPR_KIND_ for partition expressions.
18701 * Cannot allow system column references, since that would
18702 * make partition routing impossible: their values won't be
18703 * known yet when we need to do that.
18705 pull_varattnos(expr, 1, &expr_attrs);
18706 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18708 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18709 expr_attrs))
18710 ereport(ERROR,
18711 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18712 errmsg("partition key expressions cannot contain system column references")));
18716 * Generated columns cannot work: They are computed after
18717 * BEFORE triggers, but partition routing is done before all
18718 * triggers.
18720 i = -1;
18721 while ((i = bms_next_member(expr_attrs, i)) >= 0)
18723 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18725 if (attno > 0 &&
18726 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18727 ereport(ERROR,
18728 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18729 errmsg("cannot use generated column in partition key"),
18730 errdetail("Column \"%s\" is a generated column.",
18731 get_attname(RelationGetRelid(rel), attno, false)),
18732 parser_errposition(pstate, pelem->location)));
18736 * Preprocess the expression before checking for mutability.
18737 * This is essential for the reasons described in
18738 * contain_mutable_functions_after_planning. However, we call
18739 * expression_planner for ourselves rather than using that
18740 * function, because if constant-folding reduces the
18741 * expression to a constant, we'd like to know that so we can
18742 * complain below.
18744 * Like contain_mutable_functions_after_planning, assume that
18745 * expression_planner won't scribble on its input, so this
18746 * won't affect the partexprs entry we saved above.
18748 expr = (Node *) expression_planner((Expr *) expr);
18751 * Partition expressions cannot contain mutable functions,
18752 * because a given row must always map to the same partition
18753 * as long as there is no change in the partition boundary
18754 * structure.
18756 if (contain_mutable_functions(expr))
18757 ereport(ERROR,
18758 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18759 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18762 * While it is not exactly *wrong* for a partition expression
18763 * to be a constant, it seems better to reject such keys.
18765 if (IsA(expr, Const))
18766 ereport(ERROR,
18767 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18768 errmsg("cannot use constant expression as partition key")));
18773 * Apply collation override if any
18775 if (pelem->collation)
18776 attcollation = get_collation_oid(pelem->collation, false);
18779 * Check we have a collation iff it's a collatable type. The only
18780 * expected failures here are (1) COLLATE applied to a noncollatable
18781 * type, or (2) partition expression had an unresolved collation. But
18782 * we might as well code this to be a complete consistency check.
18784 if (type_is_collatable(atttype))
18786 if (!OidIsValid(attcollation))
18787 ereport(ERROR,
18788 (errcode(ERRCODE_INDETERMINATE_COLLATION),
18789 errmsg("could not determine which collation to use for partition expression"),
18790 errhint("Use the COLLATE clause to set the collation explicitly.")));
18792 else
18794 if (OidIsValid(attcollation))
18795 ereport(ERROR,
18796 (errcode(ERRCODE_DATATYPE_MISMATCH),
18797 errmsg("collations are not supported by type %s",
18798 format_type_be(atttype))));
18801 partcollation[attn] = attcollation;
18804 * Identify the appropriate operator class. For list and range
18805 * partitioning, we use a btree operator class; hash partitioning uses
18806 * a hash operator class.
18808 if (strategy == PARTITION_STRATEGY_HASH)
18809 am_oid = HASH_AM_OID;
18810 else
18811 am_oid = BTREE_AM_OID;
18813 if (!pelem->opclass)
18815 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18817 if (!OidIsValid(partopclass[attn]))
18819 if (strategy == PARTITION_STRATEGY_HASH)
18820 ereport(ERROR,
18821 (errcode(ERRCODE_UNDEFINED_OBJECT),
18822 errmsg("data type %s has no default operator class for access method \"%s\"",
18823 format_type_be(atttype), "hash"),
18824 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18825 else
18826 ereport(ERROR,
18827 (errcode(ERRCODE_UNDEFINED_OBJECT),
18828 errmsg("data type %s has no default operator class for access method \"%s\"",
18829 format_type_be(atttype), "btree"),
18830 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18833 else
18834 partopclass[attn] = ResolveOpClass(pelem->opclass,
18835 atttype,
18836 am_oid == HASH_AM_OID ? "hash" : "btree",
18837 am_oid);
18839 attn++;
18844 * PartConstraintImpliedByRelConstraint
18845 * Do scanrel's existing constraints imply the partition constraint?
18847 * "Existing constraints" include its check constraints and column-level
18848 * not-null constraints. partConstraint describes the partition constraint,
18849 * in implicit-AND form.
18851 bool
18852 PartConstraintImpliedByRelConstraint(Relation scanrel,
18853 List *partConstraint)
18855 List *existConstraint = NIL;
18856 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18857 int i;
18859 if (constr && constr->has_not_null)
18861 int natts = scanrel->rd_att->natts;
18863 for (i = 1; i <= natts; i++)
18865 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18867 if (att->attnotnull && !att->attisdropped)
18869 NullTest *ntest = makeNode(NullTest);
18871 ntest->arg = (Expr *) makeVar(1,
18873 att->atttypid,
18874 att->atttypmod,
18875 att->attcollation,
18877 ntest->nulltesttype = IS_NOT_NULL;
18880 * argisrow=false is correct even for a composite column,
18881 * because attnotnull does not represent a SQL-spec IS NOT
18882 * NULL test in such a case, just IS DISTINCT FROM NULL.
18884 ntest->argisrow = false;
18885 ntest->location = -1;
18886 existConstraint = lappend(existConstraint, ntest);
18891 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18895 * ConstraintImpliedByRelConstraint
18896 * Do scanrel's existing constraints imply the given constraint?
18898 * testConstraint is the constraint to validate. provenConstraint is a
18899 * caller-provided list of conditions which this function may assume
18900 * to be true. Both provenConstraint and testConstraint must be in
18901 * implicit-AND form, must only contain immutable clauses, and must
18902 * contain only Vars with varno = 1.
18904 bool
18905 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18907 List *existConstraint = list_copy(provenConstraint);
18908 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18909 int num_check,
18912 num_check = (constr != NULL) ? constr->num_check : 0;
18913 for (i = 0; i < num_check; i++)
18915 Node *cexpr;
18918 * If this constraint hasn't been fully validated yet, we must ignore
18919 * it here.
18921 if (!constr->check[i].ccvalid)
18922 continue;
18925 * NOT ENFORCED constraints are always marked as invalid, which should
18926 * have been ignored.
18928 Assert(constr->check[i].ccenforced);
18930 cexpr = stringToNode(constr->check[i].ccbin);
18933 * Run each expression through const-simplification and
18934 * canonicalization. It is necessary, because we will be comparing it
18935 * to similarly-processed partition constraint expressions, and may
18936 * fail to detect valid matches without this.
18938 cexpr = eval_const_expressions(NULL, cexpr);
18939 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18941 existConstraint = list_concat(existConstraint,
18942 make_ands_implicit((Expr *) cexpr));
18946 * Try to make the proof. Since we are comparing CHECK constraints, we
18947 * need to use weak implication, i.e., we assume existConstraint is
18948 * not-false and try to prove the same for testConstraint.
18950 * Note that predicate_implied_by assumes its first argument is known
18951 * immutable. That should always be true for both NOT NULL and partition
18952 * constraints, so we don't test it here.
18954 return predicate_implied_by(testConstraint, existConstraint, true);
18958 * QueuePartitionConstraintValidation
18960 * Add an entry to wqueue to have the given partition constraint validated by
18961 * Phase 3, for the given relation, and all its children.
18963 * We first verify whether the given constraint is implied by pre-existing
18964 * relation constraints; if it is, there's no need to scan the table to
18965 * validate, so don't queue in that case.
18967 static void
18968 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18969 List *partConstraint,
18970 bool validate_default)
18973 * Based on the table's existing constraints, determine whether or not we
18974 * may skip scanning the table.
18976 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18978 if (!validate_default)
18979 ereport(DEBUG1,
18980 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18981 RelationGetRelationName(scanrel))));
18982 else
18983 ereport(DEBUG1,
18984 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18985 RelationGetRelationName(scanrel))));
18986 return;
18990 * Constraints proved insufficient. For plain relations, queue a
18991 * validation item now; for partitioned tables, recurse to process each
18992 * partition.
18994 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18996 AlteredTableInfo *tab;
18998 /* Grab a work queue entry. */
18999 tab = ATGetQueueEntry(wqueue, scanrel);
19000 Assert(tab->partition_constraint == NULL);
19001 tab->partition_constraint = (Expr *) linitial(partConstraint);
19002 tab->validate_default = validate_default;
19004 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19006 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19007 int i;
19009 for (i = 0; i < partdesc->nparts; i++)
19011 Relation part_rel;
19012 List *thisPartConstraint;
19015 * This is the minimum lock we need to prevent deadlocks.
19017 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19020 * Adjust the constraint for scanrel so that it matches this
19021 * partition's attribute numbers.
19023 thisPartConstraint =
19024 map_partition_varattnos(partConstraint, 1,
19025 part_rel, scanrel);
19027 QueuePartitionConstraintValidation(wqueue, part_rel,
19028 thisPartConstraint,
19029 validate_default);
19030 table_close(part_rel, NoLock); /* keep lock till commit */
19036 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19038 * Return the address of the newly attached partition.
19040 static ObjectAddress
19041 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19042 AlterTableUtilityContext *context)
19044 Relation attachrel,
19045 catalog;
19046 List *attachrel_children;
19047 List *partConstraint;
19048 SysScanDesc scan;
19049 ScanKeyData skey;
19050 AttrNumber attno;
19051 int natts;
19052 TupleDesc tupleDesc;
19053 ObjectAddress address;
19054 const char *trigger_name;
19055 Oid defaultPartOid;
19056 List *partBoundConstraint;
19057 ParseState *pstate = make_parsestate(NULL);
19059 pstate->p_sourcetext = context->queryString;
19062 * We must lock the default partition if one exists, because attaching a
19063 * new partition will change its partition constraint.
19065 defaultPartOid =
19066 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19067 if (OidIsValid(defaultPartOid))
19068 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19070 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19073 * XXX I think it'd be a good idea to grab locks on all tables referenced
19074 * by FKs at this point also.
19078 * Must be owner of both parent and source table -- parent was checked by
19079 * ATSimplePermissions call in ATPrepCmd
19081 ATSimplePermissions(AT_AttachPartition, attachrel,
19082 ATT_TABLE | ATT_PARTITIONED_TABLE | ATT_FOREIGN_TABLE);
19084 /* A partition can only have one parent */
19085 if (attachrel->rd_rel->relispartition)
19086 ereport(ERROR,
19087 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19088 errmsg("\"%s\" is already a partition",
19089 RelationGetRelationName(attachrel))));
19091 if (OidIsValid(attachrel->rd_rel->reloftype))
19092 ereport(ERROR,
19093 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19094 errmsg("cannot attach a typed table as partition")));
19097 * Table being attached should not already be part of inheritance; either
19098 * as a child table...
19100 catalog = table_open(InheritsRelationId, AccessShareLock);
19101 ScanKeyInit(&skey,
19102 Anum_pg_inherits_inhrelid,
19103 BTEqualStrategyNumber, F_OIDEQ,
19104 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19105 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19106 NULL, 1, &skey);
19107 if (HeapTupleIsValid(systable_getnext(scan)))
19108 ereport(ERROR,
19109 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19110 errmsg("cannot attach inheritance child as partition")));
19111 systable_endscan(scan);
19113 /* ...or as a parent table (except the case when it is partitioned) */
19114 ScanKeyInit(&skey,
19115 Anum_pg_inherits_inhparent,
19116 BTEqualStrategyNumber, F_OIDEQ,
19117 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19118 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19119 1, &skey);
19120 if (HeapTupleIsValid(systable_getnext(scan)) &&
19121 attachrel->rd_rel->relkind == RELKIND_RELATION)
19122 ereport(ERROR,
19123 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19124 errmsg("cannot attach inheritance parent as partition")));
19125 systable_endscan(scan);
19126 table_close(catalog, AccessShareLock);
19129 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19130 * particular, this disallows making a rel a partition of itself.)
19132 * We do that by checking if rel is a member of the list of attachrel's
19133 * partitions provided the latter is partitioned at all. We want to avoid
19134 * having to construct this list again, so we request the strongest lock
19135 * on all partitions. We need the strongest lock, because we may decide
19136 * to scan them if we find out that the table being attached (or its leaf
19137 * partitions) may contain rows that violate the partition constraint. If
19138 * the table has a constraint that would prevent such rows, which by
19139 * definition is present in all the partitions, we need not scan the
19140 * table, nor its partitions. But we cannot risk a deadlock by taking a
19141 * weaker lock now and the stronger one only when needed.
19143 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19144 AccessExclusiveLock, NULL);
19145 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19146 ereport(ERROR,
19147 (errcode(ERRCODE_DUPLICATE_TABLE),
19148 errmsg("circular inheritance not allowed"),
19149 errdetail("\"%s\" is already a child of \"%s\".",
19150 RelationGetRelationName(rel),
19151 RelationGetRelationName(attachrel))));
19153 /* If the parent is permanent, so must be all of its partitions. */
19154 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19155 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19156 ereport(ERROR,
19157 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19158 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19159 RelationGetRelationName(rel))));
19161 /* Temp parent cannot have a partition that is itself not a temp */
19162 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19163 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19164 ereport(ERROR,
19165 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19166 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19167 RelationGetRelationName(rel))));
19169 /* If the parent is temp, it must belong to this session */
19170 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19171 !rel->rd_islocaltemp)
19172 ereport(ERROR,
19173 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19174 errmsg("cannot attach as partition of temporary relation of another session")));
19176 /* Ditto for the partition */
19177 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19178 !attachrel->rd_islocaltemp)
19179 ereport(ERROR,
19180 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19181 errmsg("cannot attach temporary relation of another session as partition")));
19184 * Check if attachrel has any identity columns or any columns that aren't
19185 * in the parent.
19187 tupleDesc = RelationGetDescr(attachrel);
19188 natts = tupleDesc->natts;
19189 for (attno = 1; attno <= natts; attno++)
19191 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19192 char *attributeName = NameStr(attribute->attname);
19194 /* Ignore dropped */
19195 if (attribute->attisdropped)
19196 continue;
19198 if (attribute->attidentity)
19199 ereport(ERROR,
19200 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19201 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19202 RelationGetRelationName(attachrel), attributeName),
19203 errdetail("The new partition may not contain an identity column."));
19205 /* Try to find the column in parent (matching on column name) */
19206 if (!SearchSysCacheExists2(ATTNAME,
19207 ObjectIdGetDatum(RelationGetRelid(rel)),
19208 CStringGetDatum(attributeName)))
19209 ereport(ERROR,
19210 (errcode(ERRCODE_DATATYPE_MISMATCH),
19211 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19212 RelationGetRelationName(attachrel), attributeName,
19213 RelationGetRelationName(rel)),
19214 errdetail("The new partition may contain only the columns present in parent.")));
19218 * If child_rel has row-level triggers with transition tables, we
19219 * currently don't allow it to become a partition. See also prohibitions
19220 * in ATExecAddInherit() and CreateTrigger().
19222 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19223 if (trigger_name != NULL)
19224 ereport(ERROR,
19225 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19226 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19227 trigger_name, RelationGetRelationName(attachrel)),
19228 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19231 * Check that the new partition's bound is valid and does not overlap any
19232 * of existing partitions of the parent - note that it does not return on
19233 * error.
19235 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19236 cmd->bound, pstate);
19238 /* OK to create inheritance. Rest of the checks performed there */
19239 CreateInheritance(attachrel, rel, true);
19241 /* Update the pg_class entry. */
19242 StorePartitionBound(attachrel, rel, cmd->bound);
19244 /* Ensure there exists a correct set of indexes in the partition. */
19245 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19247 /* and triggers */
19248 CloneRowTriggersToPartition(rel, attachrel);
19251 * Clone foreign key constraints. Callee is responsible for setting up
19252 * for phase 3 constraint verification.
19254 CloneForeignKeyConstraints(wqueue, rel, attachrel);
19257 * Generate partition constraint from the partition bound specification.
19258 * If the parent itself is a partition, make sure to include its
19259 * constraint as well.
19261 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19264 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
19265 * since it's needed later to construct the constraint expression for
19266 * validating against the default partition, if any.
19268 partConstraint = list_concat_copy(partBoundConstraint,
19269 RelationGetPartitionQual(rel));
19271 /* Skip validation if there are no constraints to validate. */
19272 if (partConstraint)
19275 * Run the partition quals through const-simplification similar to
19276 * check constraints. We skip canonicalize_qual, though, because
19277 * partition quals should be in canonical form already.
19279 partConstraint =
19280 (List *) eval_const_expressions(NULL,
19281 (Node *) partConstraint);
19283 /* XXX this sure looks wrong */
19284 partConstraint = list_make1(make_ands_explicit(partConstraint));
19287 * Adjust the generated constraint to match this partition's attribute
19288 * numbers.
19290 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19291 rel);
19293 /* Validate partition constraints against the table being attached. */
19294 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19295 false);
19299 * If we're attaching a partition other than the default partition and a
19300 * default one exists, then that partition's partition constraint changes,
19301 * so add an entry to the work queue to validate it, too. (We must not do
19302 * this when the partition being attached is the default one; we already
19303 * did it above!)
19305 if (OidIsValid(defaultPartOid))
19307 Relation defaultrel;
19308 List *defPartConstraint;
19310 Assert(!cmd->bound->is_default);
19312 /* we already hold a lock on the default partition */
19313 defaultrel = table_open(defaultPartOid, NoLock);
19314 defPartConstraint =
19315 get_proposed_default_constraint(partBoundConstraint);
19318 * Map the Vars in the constraint expression from rel's attnos to
19319 * defaultrel's.
19321 defPartConstraint =
19322 map_partition_varattnos(defPartConstraint,
19323 1, defaultrel, rel);
19324 QueuePartitionConstraintValidation(wqueue, defaultrel,
19325 defPartConstraint, true);
19327 /* keep our lock until commit. */
19328 table_close(defaultrel, NoLock);
19331 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19334 * If the partition we just attached is partitioned itself, invalidate
19335 * relcache for all descendent partitions too to ensure that their
19336 * rd_partcheck expression trees are rebuilt; partitions already locked at
19337 * the beginning of this function.
19339 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19341 ListCell *l;
19343 foreach(l, attachrel_children)
19345 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19349 /* keep our lock until commit */
19350 table_close(attachrel, NoLock);
19352 return address;
19356 * AttachPartitionEnsureIndexes
19357 * subroutine for ATExecAttachPartition to create/match indexes
19359 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19360 * PARTITION: every partition must have an index attached to each index on the
19361 * partitioned table.
19363 static void
19364 AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19366 List *idxes;
19367 List *attachRelIdxs;
19368 Relation *attachrelIdxRels;
19369 IndexInfo **attachInfos;
19370 ListCell *cell;
19371 MemoryContext cxt;
19372 MemoryContext oldcxt;
19374 cxt = AllocSetContextCreate(CurrentMemoryContext,
19375 "AttachPartitionEnsureIndexes",
19376 ALLOCSET_DEFAULT_SIZES);
19377 oldcxt = MemoryContextSwitchTo(cxt);
19379 idxes = RelationGetIndexList(rel);
19380 attachRelIdxs = RelationGetIndexList(attachrel);
19381 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19382 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19384 /* Build arrays of all existing indexes and their IndexInfos */
19385 foreach_oid(cldIdxId, attachRelIdxs)
19387 int i = foreach_current_index(cldIdxId);
19389 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19390 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19394 * If we're attaching a foreign table, we must fail if any of the indexes
19395 * is a constraint index; otherwise, there's nothing to do here. Do this
19396 * before starting work, to avoid wasting the effort of building a few
19397 * non-unique indexes before coming across a unique one.
19399 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19401 foreach(cell, idxes)
19403 Oid idx = lfirst_oid(cell);
19404 Relation idxRel = index_open(idx, AccessShareLock);
19406 if (idxRel->rd_index->indisunique ||
19407 idxRel->rd_index->indisprimary)
19408 ereport(ERROR,
19409 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19410 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19411 RelationGetRelationName(attachrel),
19412 RelationGetRelationName(rel)),
19413 errdetail("Partitioned table \"%s\" contains unique indexes.",
19414 RelationGetRelationName(rel))));
19415 index_close(idxRel, AccessShareLock);
19418 goto out;
19422 * For each index on the partitioned table, find a matching one in the
19423 * partition-to-be; if one is not found, create one.
19425 foreach(cell, idxes)
19427 Oid idx = lfirst_oid(cell);
19428 Relation idxRel = index_open(idx, AccessShareLock);
19429 IndexInfo *info;
19430 AttrMap *attmap;
19431 bool found = false;
19432 Oid constraintOid;
19435 * Ignore indexes in the partitioned table other than partitioned
19436 * indexes.
19438 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19440 index_close(idxRel, AccessShareLock);
19441 continue;
19444 /* construct an indexinfo to compare existing indexes against */
19445 info = BuildIndexInfo(idxRel);
19446 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19447 RelationGetDescr(rel),
19448 false);
19449 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19452 * Scan the list of existing indexes in the partition-to-be, and mark
19453 * the first matching, valid, unattached one we find, if any, as
19454 * partition of the parent index. If we find one, we're done.
19456 for (int i = 0; i < list_length(attachRelIdxs); i++)
19458 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19459 Oid cldConstrOid = InvalidOid;
19461 /* does this index have a parent? if so, can't use it */
19462 if (attachrelIdxRels[i]->rd_rel->relispartition)
19463 continue;
19465 /* If this index is invalid, can't use it */
19466 if (!attachrelIdxRels[i]->rd_index->indisvalid)
19467 continue;
19469 if (CompareIndexInfo(attachInfos[i], info,
19470 attachrelIdxRels[i]->rd_indcollation,
19471 idxRel->rd_indcollation,
19472 attachrelIdxRels[i]->rd_opfamily,
19473 idxRel->rd_opfamily,
19474 attmap))
19477 * If this index is being created in the parent because of a
19478 * constraint, then the child needs to have a constraint also,
19479 * so look for one. If there is no such constraint, this
19480 * index is no good, so keep looking.
19482 if (OidIsValid(constraintOid))
19484 cldConstrOid =
19485 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19486 cldIdxId);
19487 /* no dice */
19488 if (!OidIsValid(cldConstrOid))
19489 continue;
19491 /* Ensure they're both the same type of constraint */
19492 if (get_constraint_type(constraintOid) !=
19493 get_constraint_type(cldConstrOid))
19494 continue;
19497 /* bingo. */
19498 IndexSetParentIndex(attachrelIdxRels[i], idx);
19499 if (OidIsValid(constraintOid))
19500 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19501 RelationGetRelid(attachrel));
19502 found = true;
19504 CommandCounterIncrement();
19505 break;
19510 * If no suitable index was found in the partition-to-be, create one
19511 * now. Note that if this is a PK, not-null constraints must already
19512 * exist.
19514 if (!found)
19516 IndexStmt *stmt;
19517 Oid conOid;
19519 stmt = generateClonedIndexStmt(NULL,
19520 idxRel, attmap,
19521 &conOid);
19522 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19523 RelationGetRelid(idxRel),
19524 conOid,
19526 true, false, false, false, false);
19529 index_close(idxRel, AccessShareLock);
19532 out:
19533 /* Clean up. */
19534 for (int i = 0; i < list_length(attachRelIdxs); i++)
19535 index_close(attachrelIdxRels[i], AccessShareLock);
19536 MemoryContextSwitchTo(oldcxt);
19537 MemoryContextDelete(cxt);
19541 * CloneRowTriggersToPartition
19542 * subroutine for ATExecAttachPartition/DefineRelation to create row
19543 * triggers on partitions
19545 static void
19546 CloneRowTriggersToPartition(Relation parent, Relation partition)
19548 Relation pg_trigger;
19549 ScanKeyData key;
19550 SysScanDesc scan;
19551 HeapTuple tuple;
19552 MemoryContext perTupCxt;
19554 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19555 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19556 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19557 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19558 true, NULL, 1, &key);
19560 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19561 "clone trig", ALLOCSET_SMALL_SIZES);
19563 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19565 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19566 CreateTrigStmt *trigStmt;
19567 Node *qual = NULL;
19568 Datum value;
19569 bool isnull;
19570 List *cols = NIL;
19571 List *trigargs = NIL;
19572 MemoryContext oldcxt;
19575 * Ignore statement-level triggers; those are not cloned.
19577 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19578 continue;
19581 * Don't clone internal triggers, because the constraint cloning code
19582 * will.
19584 if (trigForm->tgisinternal)
19585 continue;
19588 * Complain if we find an unexpected trigger type.
19590 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19591 !TRIGGER_FOR_AFTER(trigForm->tgtype))
19592 elog(ERROR, "unexpected trigger \"%s\" found",
19593 NameStr(trigForm->tgname));
19595 /* Use short-lived context for CREATE TRIGGER */
19596 oldcxt = MemoryContextSwitchTo(perTupCxt);
19599 * If there is a WHEN clause, generate a 'cooked' version of it that's
19600 * appropriate for the partition.
19602 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19603 RelationGetDescr(pg_trigger), &isnull);
19604 if (!isnull)
19606 qual = stringToNode(TextDatumGetCString(value));
19607 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19608 partition, parent);
19609 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19610 partition, parent);
19614 * If there is a column list, transform it to a list of column names.
19615 * Note we don't need to map this list in any way ...
19617 if (trigForm->tgattr.dim1 > 0)
19619 int i;
19621 for (i = 0; i < trigForm->tgattr.dim1; i++)
19623 Form_pg_attribute col;
19625 col = TupleDescAttr(parent->rd_att,
19626 trigForm->tgattr.values[i] - 1);
19627 cols = lappend(cols,
19628 makeString(pstrdup(NameStr(col->attname))));
19632 /* Reconstruct trigger arguments list. */
19633 if (trigForm->tgnargs > 0)
19635 char *p;
19637 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19638 RelationGetDescr(pg_trigger), &isnull);
19639 if (isnull)
19640 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19641 NameStr(trigForm->tgname), RelationGetRelationName(partition));
19643 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19645 for (int i = 0; i < trigForm->tgnargs; i++)
19647 trigargs = lappend(trigargs, makeString(pstrdup(p)));
19648 p += strlen(p) + 1;
19652 trigStmt = makeNode(CreateTrigStmt);
19653 trigStmt->replace = false;
19654 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19655 trigStmt->trigname = NameStr(trigForm->tgname);
19656 trigStmt->relation = NULL;
19657 trigStmt->funcname = NULL; /* passed separately */
19658 trigStmt->args = trigargs;
19659 trigStmt->row = true;
19660 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19661 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19662 trigStmt->columns = cols;
19663 trigStmt->whenClause = NULL; /* passed separately */
19664 trigStmt->transitionRels = NIL; /* not supported at present */
19665 trigStmt->deferrable = trigForm->tgdeferrable;
19666 trigStmt->initdeferred = trigForm->tginitdeferred;
19667 trigStmt->constrrel = NULL; /* passed separately */
19669 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19670 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19671 trigForm->tgfoid, trigForm->oid, qual,
19672 false, true, trigForm->tgenabled);
19674 MemoryContextSwitchTo(oldcxt);
19675 MemoryContextReset(perTupCxt);
19678 MemoryContextDelete(perTupCxt);
19680 systable_endscan(scan);
19681 table_close(pg_trigger, RowExclusiveLock);
19685 * ALTER TABLE DETACH PARTITION
19687 * Return the address of the relation that is no longer a partition of rel.
19689 * If concurrent mode is requested, we run in two transactions. A side-
19690 * effect is that this command cannot run in a multi-part ALTER TABLE.
19691 * Currently, that's enforced by the grammar.
19693 * The strategy for concurrency is to first modify the partition's
19694 * pg_inherit catalog row to make it visible to everyone that the
19695 * partition is detached, lock the partition against writes, and commit
19696 * the transaction; anyone who requests the partition descriptor from
19697 * that point onwards has to ignore such a partition. In a second
19698 * transaction, we wait until all transactions that could have seen the
19699 * partition as attached are gone, then we remove the rest of partition
19700 * metadata (pg_inherits and pg_class.relpartbounds).
19702 static ObjectAddress
19703 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19704 RangeVar *name, bool concurrent)
19706 Relation partRel;
19707 ObjectAddress address;
19708 Oid defaultPartOid;
19711 * We must lock the default partition, because detaching this partition
19712 * will change its partition constraint.
19714 defaultPartOid =
19715 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19716 if (OidIsValid(defaultPartOid))
19719 * Concurrent detaching when a default partition exists is not
19720 * supported. The main problem is that the default partition
19721 * constraint would change. And there's a definitional problem: what
19722 * should happen to the tuples that are being inserted that belong to
19723 * the partition being detached? Putting them on the partition being
19724 * detached would be wrong, since they'd become "lost" after the
19725 * detaching completes but we cannot put them in the default partition
19726 * either until we alter its partition constraint.
19728 * I think we could solve this problem if we effected the constraint
19729 * change before committing the first transaction. But the lock would
19730 * have to remain AEL and it would cause concurrent query planning to
19731 * be blocked, so changing it that way would be even worse.
19733 if (concurrent)
19734 ereport(ERROR,
19735 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19736 errmsg("cannot detach partitions concurrently when a default partition exists")));
19737 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19741 * In concurrent mode, the partition is locked with share-update-exclusive
19742 * in the first transaction. This allows concurrent transactions to be
19743 * doing DML to the partition.
19745 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19746 AccessExclusiveLock);
19749 * Check inheritance conditions and either delete the pg_inherits row (in
19750 * non-concurrent mode) or just set the inhdetachpending flag.
19752 if (!concurrent)
19753 RemoveInheritance(partRel, rel, false);
19754 else
19755 MarkInheritDetached(partRel, rel);
19758 * Ensure that foreign keys still hold after this detach. This keeps
19759 * locks on the referencing tables, which prevents concurrent transactions
19760 * from adding rows that we wouldn't see. For this to work in concurrent
19761 * mode, it is critical that the partition appears as no longer attached
19762 * for the RI queries as soon as the first transaction commits.
19764 ATDetachCheckNoForeignKeyRefs(partRel);
19767 * Concurrent mode has to work harder; first we add a new constraint to
19768 * the partition that matches the partition constraint. Then we close our
19769 * existing transaction, and in a new one wait for all processes to catch
19770 * up on the catalog updates we've done so far; at that point we can
19771 * complete the operation.
19773 if (concurrent)
19775 Oid partrelid,
19776 parentrelid;
19777 LOCKTAG tag;
19778 char *parentrelname;
19779 char *partrelname;
19782 * Add a new constraint to the partition being detached, which
19783 * supplants the partition constraint (unless there is one already).
19785 DetachAddConstraintIfNeeded(wqueue, partRel);
19788 * We're almost done now; the only traces that remain are the
19789 * pg_inherits tuple and the partition's relpartbounds. Before we can
19790 * remove those, we need to wait until all transactions that know that
19791 * this is a partition are gone.
19795 * Remember relation OIDs to re-acquire them later; and relation names
19796 * too, for error messages if something is dropped in between.
19798 partrelid = RelationGetRelid(partRel);
19799 parentrelid = RelationGetRelid(rel);
19800 parentrelname = MemoryContextStrdup(PortalContext,
19801 RelationGetRelationName(rel));
19802 partrelname = MemoryContextStrdup(PortalContext,
19803 RelationGetRelationName(partRel));
19805 /* Invalidate relcache entries for the parent -- must be before close */
19806 CacheInvalidateRelcache(rel);
19808 table_close(partRel, NoLock);
19809 table_close(rel, NoLock);
19810 tab->rel = NULL;
19812 /* Make updated catalog entry visible */
19813 PopActiveSnapshot();
19814 CommitTransactionCommand();
19816 StartTransactionCommand();
19819 * Now wait. This ensures that all queries that were planned
19820 * including the partition are finished before we remove the rest of
19821 * catalog entries. We don't need or indeed want to acquire this
19822 * lock, though -- that would block later queries.
19824 * We don't need to concern ourselves with waiting for a lock on the
19825 * partition itself, since we will acquire AccessExclusiveLock below.
19827 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19828 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19831 * Now acquire locks in both relations again. Note they may have been
19832 * removed in the meantime, so care is required.
19834 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19835 partRel = try_relation_open(partrelid, AccessExclusiveLock);
19837 /* If the relations aren't there, something bad happened; bail out */
19838 if (rel == NULL)
19840 if (partRel != NULL) /* shouldn't happen */
19841 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19842 partrelname);
19843 ereport(ERROR,
19844 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19845 errmsg("partitioned table \"%s\" was removed concurrently",
19846 parentrelname)));
19848 if (partRel == NULL)
19849 ereport(ERROR,
19850 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19851 errmsg("partition \"%s\" was removed concurrently", partrelname)));
19853 tab->rel = rel;
19856 /* Do the final part of detaching */
19857 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19859 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19861 /* keep our lock until commit */
19862 table_close(partRel, NoLock);
19864 return address;
19868 * Second part of ALTER TABLE .. DETACH.
19870 * This is separate so that it can be run independently when the second
19871 * transaction of the concurrent algorithm fails (crash or abort).
19873 static void
19874 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19875 Oid defaultPartOid)
19877 Relation classRel;
19878 List *fks;
19879 ListCell *cell;
19880 List *indexes;
19881 Datum new_val[Natts_pg_class];
19882 bool new_null[Natts_pg_class],
19883 new_repl[Natts_pg_class];
19884 HeapTuple tuple,
19885 newtuple;
19886 Relation trigrel = NULL;
19888 if (concurrent)
19891 * We can remove the pg_inherits row now. (In the non-concurrent case,
19892 * this was already done).
19894 RemoveInheritance(partRel, rel, true);
19897 /* Drop any triggers that were cloned on creation/attach. */
19898 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19901 * Detach any foreign keys that are inherited. This includes creating
19902 * additional action triggers.
19904 fks = copyObject(RelationGetFKeyList(partRel));
19905 if (fks != NIL)
19906 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19907 foreach(cell, fks)
19909 ForeignKeyCacheInfo *fk = lfirst(cell);
19910 HeapTuple contup;
19911 Form_pg_constraint conform;
19912 Oid insertTriggerOid,
19913 updateTriggerOid;
19915 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19916 if (!HeapTupleIsValid(contup))
19917 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19918 conform = (Form_pg_constraint) GETSTRUCT(contup);
19920 /* consider only the inherited foreign keys */
19921 if (conform->contype != CONSTRAINT_FOREIGN ||
19922 !OidIsValid(conform->conparentid))
19924 ReleaseSysCache(contup);
19925 continue;
19929 * The constraint on this table must be marked no longer a child of
19930 * the parent's constraint, as do its check triggers.
19932 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19935 * Also, look up the partition's "check" triggers corresponding to the
19936 * constraint being detached and detach them from the parent triggers.
19938 GetForeignKeyCheckTriggers(trigrel,
19939 fk->conoid, fk->confrelid, fk->conrelid,
19940 &insertTriggerOid, &updateTriggerOid);
19941 Assert(OidIsValid(insertTriggerOid));
19942 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19943 RelationGetRelid(partRel));
19944 Assert(OidIsValid(updateTriggerOid));
19945 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19946 RelationGetRelid(partRel));
19949 * Lastly, create the action triggers on the referenced table, using
19950 * addFkRecurseReferenced, which requires some elaborate setup (so put
19951 * it in a separate block). While at it, if the table is partitioned,
19952 * that function will recurse to create the pg_constraint rows and
19953 * action triggers for each partition.
19955 * Note there's no need to do addFkConstraint() here, because the
19956 * pg_constraint row already exists.
19959 Constraint *fkconstraint;
19960 int numfks;
19961 AttrNumber conkey[INDEX_MAX_KEYS];
19962 AttrNumber confkey[INDEX_MAX_KEYS];
19963 Oid conpfeqop[INDEX_MAX_KEYS];
19964 Oid conppeqop[INDEX_MAX_KEYS];
19965 Oid conffeqop[INDEX_MAX_KEYS];
19966 int numfkdelsetcols;
19967 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
19968 Relation refdRel;
19970 DeconstructFkConstraintRow(contup,
19971 &numfks,
19972 conkey,
19973 confkey,
19974 conpfeqop,
19975 conppeqop,
19976 conffeqop,
19977 &numfkdelsetcols,
19978 confdelsetcols);
19980 /* Create a synthetic node we'll use throughout */
19981 fkconstraint = makeNode(Constraint);
19982 fkconstraint->contype = CONSTRAINT_FOREIGN;
19983 fkconstraint->conname = pstrdup(NameStr(conform->conname));
19984 fkconstraint->deferrable = conform->condeferrable;
19985 fkconstraint->initdeferred = conform->condeferred;
19986 fkconstraint->skip_validation = true;
19987 fkconstraint->initially_valid = true;
19988 /* a few irrelevant fields omitted here */
19989 fkconstraint->pktable = NULL;
19990 fkconstraint->fk_attrs = NIL;
19991 fkconstraint->pk_attrs = NIL;
19992 fkconstraint->fk_matchtype = conform->confmatchtype;
19993 fkconstraint->fk_upd_action = conform->confupdtype;
19994 fkconstraint->fk_del_action = conform->confdeltype;
19995 fkconstraint->fk_del_set_cols = NIL;
19996 fkconstraint->old_conpfeqop = NIL;
19997 fkconstraint->old_pktable_oid = InvalidOid;
19998 fkconstraint->location = -1;
20000 /* set up colnames, used to generate the constraint name */
20001 for (int i = 0; i < numfks; i++)
20003 Form_pg_attribute att;
20005 att = TupleDescAttr(RelationGetDescr(partRel),
20006 conkey[i] - 1);
20008 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
20009 makeString(NameStr(att->attname)));
20012 refdRel = table_open(fk->confrelid, ShareRowExclusiveLock);
20014 addFkRecurseReferenced(fkconstraint, partRel,
20015 refdRel,
20016 conform->conindid,
20017 fk->conoid,
20018 numfks,
20019 confkey,
20020 conkey,
20021 conpfeqop,
20022 conppeqop,
20023 conffeqop,
20024 numfkdelsetcols,
20025 confdelsetcols,
20026 true,
20027 InvalidOid, InvalidOid,
20028 conform->conperiod);
20029 table_close(refdRel, NoLock); /* keep lock till end of xact */
20032 ReleaseSysCache(contup);
20034 list_free_deep(fks);
20035 if (trigrel)
20036 table_close(trigrel, RowExclusiveLock);
20039 * Any sub-constraints that are in the referenced-side of a larger
20040 * constraint have to be removed. This partition is no longer part of the
20041 * key space of the constraint.
20043 foreach(cell, GetParentedForeignKeyRefs(partRel))
20045 Oid constrOid = lfirst_oid(cell);
20046 ObjectAddress constraint;
20048 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20049 deleteDependencyRecordsForClass(ConstraintRelationId,
20050 constrOid,
20051 ConstraintRelationId,
20052 DEPENDENCY_INTERNAL);
20053 CommandCounterIncrement();
20055 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20056 performDeletion(&constraint, DROP_RESTRICT, 0);
20059 /* Now we can detach indexes */
20060 indexes = RelationGetIndexList(partRel);
20061 foreach(cell, indexes)
20063 Oid idxid = lfirst_oid(cell);
20064 Oid parentidx;
20065 Relation idx;
20066 Oid constrOid;
20067 Oid parentConstrOid;
20069 if (!has_superclass(idxid))
20070 continue;
20072 parentidx = get_partition_parent(idxid, false);
20073 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
20075 idx = index_open(idxid, AccessExclusiveLock);
20076 IndexSetParentIndex(idx, InvalidOid);
20079 * If there's a constraint associated with the index, detach it too.
20080 * Careful: it is possible for a constraint index in a partition to be
20081 * the child of a non-constraint index, so verify whether the parent
20082 * index does actually have a constraint.
20084 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20085 idxid);
20086 parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
20087 parentidx);
20088 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
20089 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20091 index_close(idx, NoLock);
20094 /* Update pg_class tuple */
20095 classRel = table_open(RelationRelationId, RowExclusiveLock);
20096 tuple = SearchSysCacheCopy1(RELOID,
20097 ObjectIdGetDatum(RelationGetRelid(partRel)));
20098 if (!HeapTupleIsValid(tuple))
20099 elog(ERROR, "cache lookup failed for relation %u",
20100 RelationGetRelid(partRel));
20101 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20103 /* Clear relpartbound and reset relispartition */
20104 memset(new_val, 0, sizeof(new_val));
20105 memset(new_null, false, sizeof(new_null));
20106 memset(new_repl, false, sizeof(new_repl));
20107 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20108 new_null[Anum_pg_class_relpartbound - 1] = true;
20109 new_repl[Anum_pg_class_relpartbound - 1] = true;
20110 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20111 new_val, new_null, new_repl);
20113 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20114 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20115 heap_freetuple(newtuple);
20116 table_close(classRel, RowExclusiveLock);
20119 * Drop identity property from all identity columns of partition.
20121 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20123 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20125 if (!attr->attisdropped && attr->attidentity)
20126 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20127 AccessExclusiveLock, true, true);
20130 if (OidIsValid(defaultPartOid))
20133 * If the relation being detached is the default partition itself,
20134 * remove it from the parent's pg_partitioned_table entry.
20136 * If not, we must invalidate default partition's relcache entry, as
20137 * in StorePartitionBound: its partition constraint depends on every
20138 * other partition's partition constraint.
20140 if (RelationGetRelid(partRel) == defaultPartOid)
20141 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20142 else
20143 CacheInvalidateRelcacheByRelid(defaultPartOid);
20147 * Invalidate the parent's relcache so that the partition is no longer
20148 * included in its partition descriptor.
20150 CacheInvalidateRelcache(rel);
20153 * If the partition we just detached is partitioned itself, invalidate
20154 * relcache for all descendent partitions too to ensure that their
20155 * rd_partcheck expression trees are rebuilt; must lock partitions before
20156 * doing so, using the same lockmode as what partRel has been locked with
20157 * by the caller.
20159 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20161 List *children;
20163 children = find_all_inheritors(RelationGetRelid(partRel),
20164 AccessExclusiveLock, NULL);
20165 foreach(cell, children)
20167 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20173 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20175 * To use when a DETACH PARTITION command previously did not run to
20176 * completion; this completes the detaching process.
20178 static ObjectAddress
20179 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20181 Relation partRel;
20182 ObjectAddress address;
20183 Snapshot snap = GetActiveSnapshot();
20185 partRel = table_openrv(name, AccessExclusiveLock);
20188 * Wait until existing snapshots are gone. This is important if the
20189 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20190 * user could immediately run DETACH FINALIZE without actually waiting for
20191 * existing transactions. We must not complete the detach action until
20192 * all such queries are complete (otherwise we would present them with an
20193 * inconsistent view of catalogs).
20195 WaitForOlderSnapshots(snap->xmin, false);
20197 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20199 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20201 table_close(partRel, NoLock);
20203 return address;
20207 * DetachAddConstraintIfNeeded
20208 * Subroutine for ATExecDetachPartition. Create a constraint that
20209 * takes the place of the partition constraint, but avoid creating
20210 * a dupe if a constraint already exists which implies the needed
20211 * constraint.
20213 static void
20214 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20216 List *constraintExpr;
20218 constraintExpr = RelationGetPartitionQual(partRel);
20219 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20222 * Avoid adding a new constraint if the needed constraint is implied by an
20223 * existing constraint
20225 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20227 AlteredTableInfo *tab;
20228 Constraint *n;
20230 tab = ATGetQueueEntry(wqueue, partRel);
20232 /* Add constraint on partition, equivalent to the partition constraint */
20233 n = makeNode(Constraint);
20234 n->contype = CONSTR_CHECK;
20235 n->conname = NULL;
20236 n->location = -1;
20237 n->is_no_inherit = false;
20238 n->raw_expr = NULL;
20239 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20240 n->is_enforced = true;
20241 n->initially_valid = true;
20242 n->skip_validation = true;
20243 /* It's a re-add, since it nominally already exists */
20244 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20245 true, false, true, ShareUpdateExclusiveLock);
20250 * DropClonedTriggersFromPartition
20251 * subroutine for ATExecDetachPartition to remove any triggers that were
20252 * cloned to the partition when it was created-as-partition or attached.
20253 * This undoes what CloneRowTriggersToPartition did.
20255 static void
20256 DropClonedTriggersFromPartition(Oid partitionId)
20258 ScanKeyData skey;
20259 SysScanDesc scan;
20260 HeapTuple trigtup;
20261 Relation tgrel;
20262 ObjectAddresses *objects;
20264 objects = new_object_addresses();
20267 * Scan pg_trigger to search for all triggers on this rel.
20269 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20270 F_OIDEQ, ObjectIdGetDatum(partitionId));
20271 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20272 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20273 true, NULL, 1, &skey);
20274 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20276 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20277 ObjectAddress trig;
20279 /* Ignore triggers that weren't cloned */
20280 if (!OidIsValid(pg_trigger->tgparentid))
20281 continue;
20284 * Ignore internal triggers that are implementation objects of foreign
20285 * keys, because these will be detached when the foreign keys
20286 * themselves are.
20288 if (OidIsValid(pg_trigger->tgconstrrelid))
20289 continue;
20292 * This is ugly, but necessary: remove the dependency markings on the
20293 * trigger so that it can be removed.
20295 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20296 TriggerRelationId,
20297 DEPENDENCY_PARTITION_PRI);
20298 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20299 RelationRelationId,
20300 DEPENDENCY_PARTITION_SEC);
20302 /* remember this trigger to remove it below */
20303 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20304 add_exact_object_address(&trig, objects);
20307 /* make the dependency removal visible to the deletion below */
20308 CommandCounterIncrement();
20309 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20311 /* done */
20312 free_object_addresses(objects);
20313 systable_endscan(scan);
20314 table_close(tgrel, RowExclusiveLock);
20318 * Before acquiring lock on an index, acquire the same lock on the owning
20319 * table.
20321 struct AttachIndexCallbackState
20323 Oid partitionOid;
20324 Oid parentTblOid;
20325 bool lockedParentTbl;
20328 static void
20329 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20330 void *arg)
20332 struct AttachIndexCallbackState *state;
20333 Form_pg_class classform;
20334 HeapTuple tuple;
20336 state = (struct AttachIndexCallbackState *) arg;
20338 if (!state->lockedParentTbl)
20340 LockRelationOid(state->parentTblOid, AccessShareLock);
20341 state->lockedParentTbl = true;
20345 * If we previously locked some other heap, and the name we're looking up
20346 * no longer refers to an index on that relation, release the now-useless
20347 * lock. XXX maybe we should do *after* we verify whether the index does
20348 * not actually belong to the same relation ...
20350 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20352 UnlockRelationOid(state->partitionOid, AccessShareLock);
20353 state->partitionOid = InvalidOid;
20356 /* Didn't find a relation, so no need for locking or permission checks. */
20357 if (!OidIsValid(relOid))
20358 return;
20360 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20361 if (!HeapTupleIsValid(tuple))
20362 return; /* concurrently dropped, so nothing to do */
20363 classform = (Form_pg_class) GETSTRUCT(tuple);
20364 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20365 classform->relkind != RELKIND_INDEX)
20366 ereport(ERROR,
20367 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20368 errmsg("\"%s\" is not an index", rv->relname)));
20369 ReleaseSysCache(tuple);
20372 * Since we need only examine the heap's tupledesc, an access share lock
20373 * on it (preventing any DDL) is sufficient.
20375 state->partitionOid = IndexGetRelation(relOid, false);
20376 LockRelationOid(state->partitionOid, AccessShareLock);
20380 * ALTER INDEX i1 ATTACH PARTITION i2
20382 static ObjectAddress
20383 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20385 Relation partIdx;
20386 Relation partTbl;
20387 Relation parentTbl;
20388 ObjectAddress address;
20389 Oid partIdxId;
20390 Oid currParent;
20391 struct AttachIndexCallbackState state;
20394 * We need to obtain lock on the index 'name' to modify it, but we also
20395 * need to read its owning table's tuple descriptor -- so we need to lock
20396 * both. To avoid deadlocks, obtain lock on the table before doing so on
20397 * the index. Furthermore, we need to examine the parent table of the
20398 * partition, so lock that one too.
20400 state.partitionOid = InvalidOid;
20401 state.parentTblOid = parentIdx->rd_index->indrelid;
20402 state.lockedParentTbl = false;
20403 partIdxId =
20404 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20405 RangeVarCallbackForAttachIndex,
20406 &state);
20407 /* Not there? */
20408 if (!OidIsValid(partIdxId))
20409 ereport(ERROR,
20410 (errcode(ERRCODE_UNDEFINED_OBJECT),
20411 errmsg("index \"%s\" does not exist", name->relname)));
20413 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20414 partIdx = relation_open(partIdxId, AccessExclusiveLock);
20416 /* we already hold locks on both tables, so this is safe: */
20417 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20418 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20420 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20422 /* Silently do nothing if already in the right state */
20423 currParent = partIdx->rd_rel->relispartition ?
20424 get_partition_parent(partIdxId, false) : InvalidOid;
20425 if (currParent != RelationGetRelid(parentIdx))
20427 IndexInfo *childInfo;
20428 IndexInfo *parentInfo;
20429 AttrMap *attmap;
20430 bool found;
20431 int i;
20432 PartitionDesc partDesc;
20433 Oid constraintOid,
20434 cldConstrId = InvalidOid;
20437 * If this partition already has an index attached, refuse the
20438 * operation.
20440 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20442 if (OidIsValid(currParent))
20443 ereport(ERROR,
20444 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20445 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20446 RelationGetRelationName(partIdx),
20447 RelationGetRelationName(parentIdx)),
20448 errdetail("Index \"%s\" is already attached to another index.",
20449 RelationGetRelationName(partIdx))));
20451 /* Make sure it indexes a partition of the other index's table */
20452 partDesc = RelationGetPartitionDesc(parentTbl, true);
20453 found = false;
20454 for (i = 0; i < partDesc->nparts; i++)
20456 if (partDesc->oids[i] == state.partitionOid)
20458 found = true;
20459 break;
20462 if (!found)
20463 ereport(ERROR,
20464 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20465 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20466 RelationGetRelationName(partIdx),
20467 RelationGetRelationName(parentIdx)),
20468 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20469 RelationGetRelationName(partIdx),
20470 RelationGetRelationName(parentTbl))));
20472 /* Ensure the indexes are compatible */
20473 childInfo = BuildIndexInfo(partIdx);
20474 parentInfo = BuildIndexInfo(parentIdx);
20475 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20476 RelationGetDescr(parentTbl),
20477 false);
20478 if (!CompareIndexInfo(childInfo, parentInfo,
20479 partIdx->rd_indcollation,
20480 parentIdx->rd_indcollation,
20481 partIdx->rd_opfamily,
20482 parentIdx->rd_opfamily,
20483 attmap))
20484 ereport(ERROR,
20485 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20486 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20487 RelationGetRelationName(partIdx),
20488 RelationGetRelationName(parentIdx)),
20489 errdetail("The index definitions do not match.")));
20492 * If there is a constraint in the parent, make sure there is one in
20493 * the child too.
20495 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20496 RelationGetRelid(parentIdx));
20498 if (OidIsValid(constraintOid))
20500 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20501 partIdxId);
20502 if (!OidIsValid(cldConstrId))
20503 ereport(ERROR,
20504 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20505 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20506 RelationGetRelationName(partIdx),
20507 RelationGetRelationName(parentIdx)),
20508 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20509 RelationGetRelationName(parentIdx),
20510 RelationGetRelationName(parentTbl),
20511 RelationGetRelationName(partIdx))));
20515 * If it's a primary key, make sure the columns in the partition are
20516 * NOT NULL.
20518 if (parentIdx->rd_index->indisprimary)
20519 verifyPartitionIndexNotNull(childInfo, partTbl);
20521 /* All good -- do it */
20522 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20523 if (OidIsValid(constraintOid))
20524 ConstraintSetParentConstraint(cldConstrId, constraintOid,
20525 RelationGetRelid(partTbl));
20527 free_attrmap(attmap);
20529 validatePartitionedIndex(parentIdx, parentTbl);
20532 relation_close(parentTbl, AccessShareLock);
20533 /* keep these locks till commit */
20534 relation_close(partTbl, NoLock);
20535 relation_close(partIdx, NoLock);
20537 return address;
20541 * Verify whether the given partition already contains an index attached
20542 * to the given partitioned index. If so, raise an error.
20544 static void
20545 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20547 Oid existingIdx;
20549 existingIdx = index_get_partition(partitionTbl,
20550 RelationGetRelid(parentIdx));
20551 if (OidIsValid(existingIdx))
20552 ereport(ERROR,
20553 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20554 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20555 RelationGetRelationName(partIdx),
20556 RelationGetRelationName(parentIdx)),
20557 errdetail("Another index is already attached for partition \"%s\".",
20558 RelationGetRelationName(partitionTbl))));
20562 * Verify whether the set of attached partition indexes to a parent index on
20563 * a partitioned table is complete. If it is, mark the parent index valid.
20565 * This should be called each time a partition index is attached.
20567 static void
20568 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20570 Relation inheritsRel;
20571 SysScanDesc scan;
20572 ScanKeyData key;
20573 int tuples = 0;
20574 HeapTuple inhTup;
20575 bool updated = false;
20577 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20580 * Scan pg_inherits for this parent index. Count each valid index we find
20581 * (verifying the pg_index entry for each), and if we reach the total
20582 * amount we expect, we can mark this parent index as valid.
20584 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20585 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20586 BTEqualStrategyNumber, F_OIDEQ,
20587 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20588 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20589 NULL, 1, &key);
20590 while ((inhTup = systable_getnext(scan)) != NULL)
20592 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20593 HeapTuple indTup;
20594 Form_pg_index indexForm;
20596 indTup = SearchSysCache1(INDEXRELID,
20597 ObjectIdGetDatum(inhForm->inhrelid));
20598 if (!HeapTupleIsValid(indTup))
20599 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20600 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20601 if (indexForm->indisvalid)
20602 tuples += 1;
20603 ReleaseSysCache(indTup);
20606 /* Done with pg_inherits */
20607 systable_endscan(scan);
20608 table_close(inheritsRel, AccessShareLock);
20611 * If we found as many inherited indexes as the partitioned table has
20612 * partitions, we're good; update pg_index to set indisvalid.
20614 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20616 Relation idxRel;
20617 HeapTuple indTup;
20618 Form_pg_index indexForm;
20620 idxRel = table_open(IndexRelationId, RowExclusiveLock);
20621 indTup = SearchSysCacheCopy1(INDEXRELID,
20622 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20623 if (!HeapTupleIsValid(indTup))
20624 elog(ERROR, "cache lookup failed for index %u",
20625 RelationGetRelid(partedIdx));
20626 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20628 indexForm->indisvalid = true;
20629 updated = true;
20631 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20633 table_close(idxRel, RowExclusiveLock);
20634 heap_freetuple(indTup);
20638 * If this index is in turn a partition of a larger index, validating it
20639 * might cause the parent to become valid also. Try that.
20641 if (updated && partedIdx->rd_rel->relispartition)
20643 Oid parentIdxId,
20644 parentTblId;
20645 Relation parentIdx,
20646 parentTbl;
20648 /* make sure we see the validation we just did */
20649 CommandCounterIncrement();
20651 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20652 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20653 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20654 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20655 Assert(!parentIdx->rd_index->indisvalid);
20657 validatePartitionedIndex(parentIdx, parentTbl);
20659 relation_close(parentIdx, AccessExclusiveLock);
20660 relation_close(parentTbl, AccessExclusiveLock);
20665 * When attaching an index as a partition of a partitioned index which is a
20666 * primary key, verify that all the columns in the partition are marked NOT
20667 * NULL.
20669 static void
20670 verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20672 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20674 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20675 iinfo->ii_IndexAttrNumbers[i] - 1);
20677 if (!att->attnotnull)
20678 ereport(ERROR,
20679 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20680 errmsg("invalid primary key definition"),
20681 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20682 NameStr(att->attname),
20683 RelationGetRelationName(partition)));
20688 * Return an OID list of constraints that reference the given relation
20689 * that are marked as having a parent constraints.
20691 static List *
20692 GetParentedForeignKeyRefs(Relation partition)
20694 Relation pg_constraint;
20695 HeapTuple tuple;
20696 SysScanDesc scan;
20697 ScanKeyData key[2];
20698 List *constraints = NIL;
20701 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20702 * scan.
20704 if (RelationGetIndexList(partition) == NIL ||
20705 bms_is_empty(RelationGetIndexAttrBitmap(partition,
20706 INDEX_ATTR_BITMAP_KEY)))
20707 return NIL;
20709 /* Search for constraints referencing this table */
20710 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20711 ScanKeyInit(&key[0],
20712 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20713 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20714 ScanKeyInit(&key[1],
20715 Anum_pg_constraint_contype, BTEqualStrategyNumber,
20716 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20718 /* XXX This is a seqscan, as we don't have a usable index */
20719 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20720 while ((tuple = systable_getnext(scan)) != NULL)
20722 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20725 * We only need to process constraints that are part of larger ones.
20727 if (!OidIsValid(constrForm->conparentid))
20728 continue;
20730 constraints = lappend_oid(constraints, constrForm->oid);
20733 systable_endscan(scan);
20734 table_close(pg_constraint, AccessShareLock);
20736 return constraints;
20740 * During DETACH PARTITION, verify that any foreign keys pointing to the
20741 * partitioned table would not become invalid. An error is raised if any
20742 * referenced values exist.
20744 static void
20745 ATDetachCheckNoForeignKeyRefs(Relation partition)
20747 List *constraints;
20748 ListCell *cell;
20750 constraints = GetParentedForeignKeyRefs(partition);
20752 foreach(cell, constraints)
20754 Oid constrOid = lfirst_oid(cell);
20755 HeapTuple tuple;
20756 Form_pg_constraint constrForm;
20757 Relation rel;
20758 Trigger trig = {0};
20760 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20761 if (!HeapTupleIsValid(tuple))
20762 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20763 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20765 Assert(OidIsValid(constrForm->conparentid));
20766 Assert(constrForm->confrelid == RelationGetRelid(partition));
20768 /* prevent data changes into the referencing table until commit */
20769 rel = table_open(constrForm->conrelid, ShareLock);
20771 trig.tgoid = InvalidOid;
20772 trig.tgname = NameStr(constrForm->conname);
20773 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20774 trig.tgisinternal = true;
20775 trig.tgconstrrelid = RelationGetRelid(partition);
20776 trig.tgconstrindid = constrForm->conindid;
20777 trig.tgconstraint = constrForm->oid;
20778 trig.tgdeferrable = false;
20779 trig.tginitdeferred = false;
20780 /* we needn't fill in remaining fields */
20782 RI_PartitionRemove_Check(&trig, rel, partition);
20784 ReleaseSysCache(tuple);
20786 table_close(rel, NoLock);
20791 * resolve column compression specification to compression method.
20793 static char
20794 GetAttributeCompression(Oid atttypid, const char *compression)
20796 char cmethod;
20798 if (compression == NULL || strcmp(compression, "default") == 0)
20799 return InvalidCompressionMethod;
20802 * To specify a nondefault method, the column data type must be toastable.
20803 * Note this says nothing about whether the column's attstorage setting
20804 * permits compression; we intentionally allow attstorage and
20805 * attcompression to be independent. But with a non-toastable type,
20806 * attstorage could not be set to a value that would permit compression.
20808 * We don't actually need to enforce this, since nothing bad would happen
20809 * if attcompression were non-default; it would never be consulted. But
20810 * it seems more user-friendly to complain about a certainly-useless
20811 * attempt to set the property.
20813 if (!TypeIsToastable(atttypid))
20814 ereport(ERROR,
20815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20816 errmsg("column data type %s does not support compression",
20817 format_type_be(atttypid))));
20819 cmethod = CompressionNameToMethod(compression);
20820 if (!CompressionMethodIsValid(cmethod))
20821 ereport(ERROR,
20822 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20823 errmsg("invalid compression method \"%s\"", compression)));
20825 return cmethod;
20829 * resolve column storage specification
20831 static char
20832 GetAttributeStorage(Oid atttypid, const char *storagemode)
20834 char cstorage = 0;
20836 if (pg_strcasecmp(storagemode, "plain") == 0)
20837 cstorage = TYPSTORAGE_PLAIN;
20838 else if (pg_strcasecmp(storagemode, "external") == 0)
20839 cstorage = TYPSTORAGE_EXTERNAL;
20840 else if (pg_strcasecmp(storagemode, "extended") == 0)
20841 cstorage = TYPSTORAGE_EXTENDED;
20842 else if (pg_strcasecmp(storagemode, "main") == 0)
20843 cstorage = TYPSTORAGE_MAIN;
20844 else if (pg_strcasecmp(storagemode, "default") == 0)
20845 cstorage = get_typstorage(atttypid);
20846 else
20847 ereport(ERROR,
20848 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20849 errmsg("invalid storage type \"%s\"",
20850 storagemode)));
20853 * safety check: do not allow toasted storage modes unless column datatype
20854 * is TOAST-aware.
20856 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20857 ereport(ERROR,
20858 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20859 errmsg("column data type %s can only have storage PLAIN",
20860 format_type_be(atttypid))));
20862 return cstorage;