Repair ALTER EXTENSION ... SET SCHEMA.
[pgsql.git] / src / backend / commands / tablecmds.c
blobde0d911b468a001af1adddff16d6b781cf4bc7b9
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2024, 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 "executor/executor.h"
70 #include "foreign/fdwapi.h"
71 #include "foreign/foreign.h"
72 #include "miscadmin.h"
73 #include "nodes/makefuncs.h"
74 #include "nodes/nodeFuncs.h"
75 #include "nodes/parsenodes.h"
76 #include "optimizer/optimizer.h"
77 #include "parser/parse_coerce.h"
78 #include "parser/parse_collate.h"
79 #include "parser/parse_expr.h"
80 #include "parser/parse_relation.h"
81 #include "parser/parse_type.h"
82 #include "parser/parse_utilcmd.h"
83 #include "parser/parser.h"
84 #include "partitioning/partbounds.h"
85 #include "partitioning/partdesc.h"
86 #include "pgstat.h"
87 #include "rewrite/rewriteDefine.h"
88 #include "rewrite/rewriteHandler.h"
89 #include "rewrite/rewriteManip.h"
90 #include "storage/bufmgr.h"
91 #include "storage/lmgr.h"
92 #include "storage/lock.h"
93 #include "storage/predicate.h"
94 #include "storage/smgr.h"
95 #include "tcop/utility.h"
96 #include "utils/acl.h"
97 #include "utils/builtins.h"
98 #include "utils/fmgroids.h"
99 #include "utils/inval.h"
100 #include "utils/lsyscache.h"
101 #include "utils/memutils.h"
102 #include "utils/partcache.h"
103 #include "utils/relcache.h"
104 #include "utils/ruleutils.h"
105 #include "utils/snapmgr.h"
106 #include "utils/syscache.h"
107 #include "utils/timestamp.h"
108 #include "utils/typcache.h"
109 #include "utils/usercontext.h"
112 * ON COMMIT action list
114 typedef struct OnCommitItem
116 Oid relid; /* relid of relation */
117 OnCommitAction oncommit; /* what to do at end of xact */
120 * If this entry was created during the current transaction,
121 * creating_subid is the ID of the creating subxact; if created in a prior
122 * transaction, creating_subid is zero. If deleted during the current
123 * transaction, deleting_subid is the ID of the deleting subxact; if no
124 * deletion request is pending, deleting_subid is zero.
126 SubTransactionId creating_subid;
127 SubTransactionId deleting_subid;
128 } OnCommitItem;
130 static List *on_commits = NIL;
134 * State information for ALTER TABLE
136 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
137 * structs, one for each table modified by the operation (the named table
138 * plus any child tables that are affected). We save lists of subcommands
139 * to apply to this table (possibly modified by parse transformation steps);
140 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
141 * necessary information is stored in the constraints and newvals lists.
143 * Phase 2 is divided into multiple passes; subcommands are executed in
144 * a pass determined by subcommand type.
147 typedef enum AlterTablePass
149 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
150 AT_PASS_DROP, /* DROP (all flavors) */
151 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
152 AT_PASS_ADD_COL, /* ADD COLUMN */
153 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
154 AT_PASS_OLD_COL_ATTRS, /* re-install attnotnull */
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
338 * ForeignTruncateInfo
340 * Information related to truncation of foreign tables. This is used for
341 * the elements in a hash table. It uses the server OID as lookup key,
342 * and includes a per-server list of all foreign tables involved in the
343 * truncation.
345 typedef struct ForeignTruncateInfo
347 Oid serverid;
348 List *rels;
349 } ForeignTruncateInfo;
352 * Partition tables are expected to be dropped when the parent partitioned
353 * table gets dropped. Hence for partitioning we use AUTO dependency.
354 * Otherwise, for regular inheritance use NORMAL dependency.
356 #define child_dependency_type(child_is_partition) \
357 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
359 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
360 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
361 static void truncate_check_activity(Relation rel);
362 static void RangeVarCallbackForTruncate(const RangeVar *relation,
363 Oid relId, Oid oldRelId, void *arg);
364 static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
365 bool is_partition, List **supconstr,
366 List **supnotnulls);
367 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
368 static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
369 static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
370 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
371 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
372 static void StoreCatalogInheritance(Oid relationId, List *supers,
373 bool child_is_partition);
374 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
375 int32 seqNumber, Relation inhRelation,
376 bool child_is_partition);
377 static int findAttrByName(const char *attributeName, const List *columns);
378 static void AlterIndexNamespaces(Relation classRel, Relation rel,
379 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
380 static void AlterSeqNamespaces(Relation classRel, Relation rel,
381 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
382 LOCKMODE lockmode);
383 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
384 bool recurse, bool recursing, LOCKMODE lockmode);
385 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
386 Relation rel, HeapTuple contuple, List **otherrelids,
387 LOCKMODE lockmode);
388 static ObjectAddress ATExecValidateConstraint(List **wqueue,
389 Relation rel, char *constrName,
390 bool recurse, bool recursing, LOCKMODE lockmode);
391 static int transformColumnNameList(Oid relId, List *colList,
392 int16 *attnums, Oid *atttypids);
393 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
394 List **attnamelist,
395 int16 *attnums, Oid *atttypids,
396 Oid *opclasses, bool *pk_has_without_overlaps);
397 static Oid transformFkeyCheckAttrs(Relation pkrel,
398 int numattrs, int16 *attnums,
399 bool with_period, Oid *opclasses,
400 bool *pk_has_without_overlaps);
401 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
402 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
403 Oid *funcid);
404 static void validateForeignKeyConstraint(char *conname,
405 Relation rel, Relation pkrel,
406 Oid pkindOid, Oid constraintOid, bool hasperiod);
407 static void ATController(AlterTableStmt *parsetree,
408 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
409 AlterTableUtilityContext *context);
410 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
411 bool recurse, bool recursing, LOCKMODE lockmode,
412 AlterTableUtilityContext *context);
413 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
414 AlterTableUtilityContext *context);
415 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
416 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
417 AlterTableUtilityContext *context);
418 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
419 Relation rel, AlterTableCmd *cmd,
420 bool recurse, LOCKMODE lockmode,
421 AlterTablePass cur_pass,
422 AlterTableUtilityContext *context);
423 static void ATRewriteTables(AlterTableStmt *parsetree,
424 List **wqueue, LOCKMODE lockmode,
425 AlterTableUtilityContext *context);
426 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
427 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
428 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
429 static void ATSimpleRecursion(List **wqueue, Relation rel,
430 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
431 AlterTableUtilityContext *context);
432 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
433 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
434 LOCKMODE lockmode,
435 AlterTableUtilityContext *context);
436 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
437 DropBehavior behavior);
438 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
439 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
440 AlterTableUtilityContext *context);
441 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
442 Relation rel, AlterTableCmd **cmd,
443 bool recurse, bool recursing,
444 LOCKMODE lockmode, AlterTablePass cur_pass,
445 AlterTableUtilityContext *context);
446 static bool check_for_column_name_collision(Relation rel, const char *colname,
447 bool if_not_exists);
448 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
449 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
450 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
451 LOCKMODE lockmode);
452 static bool set_attnotnull(List **wqueue, Relation rel,
453 AttrNumber attnum, bool recurse, LOCKMODE lockmode);
454 static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel,
455 char *constrname, char *colName,
456 bool recurse, bool recursing,
457 List **readyRels, LOCKMODE lockmode);
458 static ObjectAddress ATExecSetAttNotNull(List **wqueue, Relation rel,
459 const char *colName, LOCKMODE lockmode);
460 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
461 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
462 List *testConstraint, List *provenConstraint);
463 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
464 Node *newDefault, LOCKMODE lockmode);
465 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
466 Node *newDefault);
467 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
468 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
469 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
470 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
471 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
472 bool recurse, bool recursing);
473 static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
474 Node *newExpr, LOCKMODE lockmode);
475 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
476 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
477 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
478 Node *newValue, LOCKMODE lockmode);
479 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
480 Node *options, bool isReset, LOCKMODE lockmode);
481 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
482 Node *newValue, LOCKMODE lockmode);
483 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
484 AlterTableCmd *cmd, LOCKMODE lockmode,
485 AlterTableUtilityContext *context);
486 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
487 DropBehavior behavior,
488 bool recurse, bool recursing,
489 bool missing_ok, LOCKMODE lockmode,
490 ObjectAddresses *addrs);
491 static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
492 LOCKMODE lockmode, AlterTableUtilityContext *context);
493 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
494 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
495 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
496 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
497 static ObjectAddress ATExecAddConstraint(List **wqueue,
498 AlteredTableInfo *tab, Relation rel,
499 Constraint *newConstraint, bool recurse, bool is_readd,
500 LOCKMODE lockmode);
501 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
502 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
503 IndexStmt *stmt, LOCKMODE lockmode);
504 static ObjectAddress ATAddCheckNNConstraint(List **wqueue,
505 AlteredTableInfo *tab, Relation rel,
506 Constraint *constr,
507 bool recurse, bool recursing, bool is_readd,
508 LOCKMODE lockmode);
509 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
510 Relation rel, Constraint *fkconstraint,
511 bool recurse, bool recursing,
512 LOCKMODE lockmode);
513 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
514 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
515 int numfks, int16 *pkattnum, int16 *fkattnum,
516 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
517 int numfkdelsetcols, int16 *fkdelsetcols,
518 bool old_check_ok,
519 Oid parentDelTrigger, Oid parentUpdTrigger,
520 bool with_period);
521 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
522 int numfksetcols, const int16 *fksetcolsattnums,
523 List *fksetcols);
524 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
525 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
526 int numfks, int16 *pkattnum, int16 *fkattnum,
527 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
528 int numfkdelsetcols, int16 *fkdelsetcols,
529 bool old_check_ok, LOCKMODE lockmode,
530 Oid parentInsTrigger, Oid parentUpdTrigger,
531 bool with_period);
533 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
534 Relation partitionRel);
535 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
536 static void CloneFkReferencing(List **wqueue, Relation parentRel,
537 Relation partRel);
538 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
539 Constraint *fkconstraint, Oid constraintOid,
540 Oid indexOid,
541 Oid parentInsTrigger, Oid parentUpdTrigger,
542 Oid *insertTrigOid, Oid *updateTrigOid);
543 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
544 Constraint *fkconstraint, Oid constraintOid,
545 Oid indexOid,
546 Oid parentDelTrigger, Oid parentUpdTrigger,
547 Oid *deleteTrigOid, Oid *updateTrigOid);
548 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
549 Oid partRelid,
550 Oid parentConstrOid, int numfks,
551 AttrNumber *mapped_conkey, AttrNumber *confkey,
552 Oid *conpfeqop,
553 Oid parentInsTrigger,
554 Oid parentUpdTrigger,
555 Relation trigrel);
556 static void GetForeignKeyActionTriggers(Relation trigrel,
557 Oid conoid, Oid confrelid, Oid conrelid,
558 Oid *deleteTriggerOid,
559 Oid *updateTriggerOid);
560 static void GetForeignKeyCheckTriggers(Relation trigrel,
561 Oid conoid, Oid confrelid, Oid conrelid,
562 Oid *insertTriggerOid,
563 Oid *updateTriggerOid);
564 static void ATExecDropConstraint(Relation rel, const char *constrName,
565 DropBehavior behavior, bool recurse,
566 bool missing_ok, LOCKMODE lockmode);
567 static ObjectAddress dropconstraint_internal(Relation rel,
568 HeapTuple constraintTup, DropBehavior behavior,
569 bool recurse, bool recursing,
570 bool missing_ok, List **readyRels,
571 LOCKMODE lockmode);
572 static void ATPrepAlterColumnType(List **wqueue,
573 AlteredTableInfo *tab, Relation rel,
574 bool recurse, bool recursing,
575 AlterTableCmd *cmd, LOCKMODE lockmode,
576 AlterTableUtilityContext *context);
577 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
578 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
579 AlterTableCmd *cmd, LOCKMODE lockmode);
580 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
581 Relation rel, AttrNumber attnum, const char *colName);
582 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
583 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
584 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
585 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
586 LOCKMODE lockmode);
587 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
588 char *cmd, List **wqueue, LOCKMODE lockmode,
589 bool rewrite);
590 static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
591 Oid objid, Relation rel, List *domname,
592 const char *conname);
593 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
594 static void TryReuseForeignKey(Oid oldId, Constraint *con);
595 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
596 List *options, LOCKMODE lockmode);
597 static void change_owner_fix_column_acls(Oid relationOid,
598 Oid oldOwnerId, Oid newOwnerId);
599 static void change_owner_recurse_to_sequences(Oid relationOid,
600 Oid newOwnerId, LOCKMODE lockmode);
601 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
602 LOCKMODE lockmode);
603 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
604 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
605 static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethod);
606 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
607 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
608 const char *tablespacename, LOCKMODE lockmode);
609 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
610 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
611 static void ATExecSetRelOptions(Relation rel, List *defList,
612 AlterTableType operation,
613 LOCKMODE lockmode);
614 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
615 char fires_when, bool skip_system, bool recurse,
616 LOCKMODE lockmode);
617 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
618 char fires_when, LOCKMODE lockmode);
619 static void ATPrepAddInherit(Relation child_rel);
620 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
621 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
622 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
623 DependencyType deptype);
624 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
625 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
626 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
627 static void ATExecGenericOptions(Relation rel, List *options);
628 static void ATExecSetRowSecurity(Relation rel, bool rls);
629 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
630 static ObjectAddress ATExecSetCompression(Relation rel,
631 const char *column, Node *newValue, LOCKMODE lockmode);
633 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
634 static const char *storage_name(char c);
636 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
637 Oid oldRelOid, void *arg);
638 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
639 Oid oldrelid, void *arg);
640 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
641 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
642 List **partexprs, Oid *partopclass, Oid *partcollation,
643 PartitionStrategy strategy);
644 static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
645 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
646 bool expect_detached);
647 static void ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel,
648 int inhcount);
649 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
650 PartitionCmd *cmd,
651 AlterTableUtilityContext *context);
652 static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
653 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
654 List *partConstraint,
655 bool validate_default);
656 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
657 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
658 static void DropClonedTriggersFromPartition(Oid partitionId);
659 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
660 Relation rel, RangeVar *name,
661 bool concurrent);
662 static void DetachPartitionFinalize(Relation rel, Relation partRel,
663 bool concurrent, Oid defaultPartOid);
664 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
665 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
666 RangeVar *name);
667 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
668 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
669 Relation partitionTbl);
670 static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partIdx);
671 static List *GetParentedForeignKeyRefs(Relation partition);
672 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
673 static char GetAttributeCompression(Oid atttypid, const char *compression);
674 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
676 static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
677 Relation rel, PartitionCmd *cmd,
678 AlterTableUtilityContext *context);
679 static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
680 PartitionCmd *cmd, AlterTableUtilityContext *context);
682 /* ----------------------------------------------------------------
683 * DefineRelation
684 * Creates a new relation.
686 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
687 * The other arguments are used to extend the behavior for other cases:
688 * relkind: relkind to assign to the new relation
689 * ownerId: if not InvalidOid, use this as the new relation's owner.
690 * typaddress: if not null, it's set to the pg_type entry's address.
691 * queryString: for error reporting
693 * Note that permissions checks are done against current user regardless of
694 * ownerId. A nonzero ownerId is used when someone is creating a relation
695 * "on behalf of" someone else, so we still want to see that the current user
696 * has permissions to do it.
698 * If successful, returns the address of the new relation.
699 * ----------------------------------------------------------------
701 ObjectAddress
702 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
703 ObjectAddress *typaddress, const char *queryString)
705 char relname[NAMEDATALEN];
706 Oid namespaceId;
707 Oid relationId;
708 Oid tablespaceId;
709 Relation rel;
710 TupleDesc descriptor;
711 List *inheritOids;
712 List *old_constraints;
713 List *old_notnulls;
714 List *rawDefaults;
715 List *cookedDefaults;
716 List *nncols;
717 Datum reloptions;
718 ListCell *listptr;
719 AttrNumber attnum;
720 bool partitioned;
721 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
722 Oid ofTypeId;
723 ObjectAddress address;
724 LOCKMODE parentLockmode;
725 Oid accessMethodId = InvalidOid;
728 * Truncate relname to appropriate length (probably a waste of time, as
729 * parser should have done this already).
731 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
734 * Check consistency of arguments
736 if (stmt->oncommit != ONCOMMIT_NOOP
737 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
738 ereport(ERROR,
739 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
740 errmsg("ON COMMIT can only be used on temporary tables")));
742 if (stmt->partspec != NULL)
744 if (relkind != RELKIND_RELATION)
745 elog(ERROR, "unexpected relkind: %d", (int) relkind);
747 relkind = RELKIND_PARTITIONED_TABLE;
748 partitioned = true;
750 else
751 partitioned = false;
754 * Look up the namespace in which we are supposed to create the relation,
755 * check we have permission to create there, lock it against concurrent
756 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
757 * namespace is selected.
759 namespaceId =
760 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
763 * Security check: disallow creating temp tables from security-restricted
764 * code. This is needed because calling code might not expect untrusted
765 * tables to appear in pg_temp at the front of its search path.
767 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
768 && InSecurityRestrictedOperation())
769 ereport(ERROR,
770 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
771 errmsg("cannot create temporary table within security-restricted operation")));
774 * Determine the lockmode to use when scanning parents. A self-exclusive
775 * lock is needed here.
777 * For regular inheritance, if two backends attempt to add children to the
778 * same parent simultaneously, and that parent has no pre-existing
779 * children, then both will attempt to update the parent's relhassubclass
780 * field, leading to a "tuple concurrently updated" error. Also, this
781 * interlocks against a concurrent ANALYZE on the parent table, which
782 * might otherwise be attempting to clear the parent's relhassubclass
783 * field, if its previous children were recently dropped.
785 * If the child table is a partition, then we instead grab an exclusive
786 * lock on the parent because its partition descriptor will be changed by
787 * addition of the new partition.
789 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
790 ShareUpdateExclusiveLock);
792 /* Determine the list of OIDs of the parents. */
793 inheritOids = NIL;
794 foreach(listptr, stmt->inhRelations)
796 RangeVar *rv = (RangeVar *) lfirst(listptr);
797 Oid parentOid;
799 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
802 * Reject duplications in the list of parents.
804 if (list_member_oid(inheritOids, parentOid))
805 ereport(ERROR,
806 (errcode(ERRCODE_DUPLICATE_TABLE),
807 errmsg("relation \"%s\" would be inherited from more than once",
808 get_rel_name(parentOid))));
810 inheritOids = lappend_oid(inheritOids, parentOid);
814 * Select tablespace to use: an explicitly indicated one, or (in the case
815 * of a partitioned table) the parent's, if it has one.
817 if (stmt->tablespacename)
819 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
821 if (partitioned && tablespaceId == MyDatabaseTableSpace)
822 ereport(ERROR,
823 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
824 errmsg("cannot specify default tablespace for partitioned relations")));
826 else if (stmt->partbound)
828 Assert(list_length(inheritOids) == 1);
829 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
831 else
832 tablespaceId = InvalidOid;
834 /* still nothing? use the default */
835 if (!OidIsValid(tablespaceId))
836 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
837 partitioned);
839 /* Check permissions except when using database's default */
840 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
842 AclResult aclresult;
844 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
845 ACL_CREATE);
846 if (aclresult != ACLCHECK_OK)
847 aclcheck_error(aclresult, OBJECT_TABLESPACE,
848 get_tablespace_name(tablespaceId));
851 /* In all cases disallow placing user relations in pg_global */
852 if (tablespaceId == GLOBALTABLESPACE_OID)
853 ereport(ERROR,
854 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
855 errmsg("only shared relations can be placed in pg_global tablespace")));
857 /* Identify user ID that will own the table */
858 if (!OidIsValid(ownerId))
859 ownerId = GetUserId();
862 * Parse and validate reloptions, if any.
864 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
865 true, false);
867 switch (relkind)
869 case RELKIND_VIEW:
870 (void) view_reloptions(reloptions, true);
871 break;
872 case RELKIND_PARTITIONED_TABLE:
873 (void) partitioned_table_reloptions(reloptions, true);
874 break;
875 default:
876 (void) heap_reloptions(relkind, reloptions, true);
879 if (stmt->ofTypename)
881 AclResult aclresult;
883 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
885 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
886 if (aclresult != ACLCHECK_OK)
887 aclcheck_error_type(aclresult, ofTypeId);
889 else
890 ofTypeId = InvalidOid;
893 * Look up inheritance ancestors and generate relation schema, including
894 * inherited attributes. (Note that stmt->tableElts is destructively
895 * modified by MergeAttributes.)
897 stmt->tableElts =
898 MergeAttributes(stmt->tableElts, inheritOids,
899 stmt->relation->relpersistence,
900 stmt->partbound != NULL,
901 &old_constraints, &old_notnulls);
904 * Create a tuple descriptor from the relation schema. Note that this
905 * deals with column names, types, and in-descriptor NOT NULL flags, but
906 * not default values, NOT NULL or CHECK constraints; we handle those
907 * below.
909 descriptor = BuildDescForRelation(stmt->tableElts);
912 * Find columns with default values and prepare for insertion of the
913 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
914 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
915 * while raw defaults go into a list of RawColumnDefault structs that will
916 * be processed by AddRelationNewConstraints. (We can't deal with raw
917 * expressions until we can do transformExpr.)
919 * We can set the atthasdef flags now in the tuple descriptor; this just
920 * saves StoreAttrDefault from having to do an immediate update of the
921 * pg_attribute rows.
923 rawDefaults = NIL;
924 cookedDefaults = NIL;
925 attnum = 0;
927 foreach(listptr, stmt->tableElts)
929 ColumnDef *colDef = lfirst(listptr);
930 Form_pg_attribute attr;
932 attnum++;
933 attr = TupleDescAttr(descriptor, attnum - 1);
935 if (colDef->raw_default != NULL)
937 RawColumnDefault *rawEnt;
939 Assert(colDef->cooked_default == NULL);
941 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
942 rawEnt->attnum = attnum;
943 rawEnt->raw_default = colDef->raw_default;
944 rawEnt->missingMode = false;
945 rawEnt->generated = colDef->generated;
946 rawDefaults = lappend(rawDefaults, rawEnt);
947 attr->atthasdef = true;
949 else if (colDef->cooked_default != NULL)
951 CookedConstraint *cooked;
953 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
954 cooked->contype = CONSTR_DEFAULT;
955 cooked->conoid = InvalidOid; /* until created */
956 cooked->name = NULL;
957 cooked->attnum = attnum;
958 cooked->expr = colDef->cooked_default;
959 cooked->skip_validation = false;
960 cooked->is_local = true; /* not used for defaults */
961 cooked->inhcount = 0; /* ditto */
962 cooked->is_no_inherit = false;
963 cookedDefaults = lappend(cookedDefaults, cooked);
964 attr->atthasdef = true;
969 * For relations with table AM and partitioned tables, select access
970 * method to use: an explicitly indicated one, or (in the case of a
971 * partitioned table) the parent's, if it has one.
973 if (stmt->accessMethod != NULL)
975 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
976 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
978 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
980 if (stmt->partbound)
982 Assert(list_length(inheritOids) == 1);
983 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
986 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
987 accessMethodId = get_table_am_oid(default_table_access_method, false);
991 * Create the relation. Inherited defaults and constraints are passed in
992 * for immediate handling --- since they don't need parsing, they can be
993 * stored immediately.
995 relationId = heap_create_with_catalog(relname,
996 namespaceId,
997 tablespaceId,
998 InvalidOid,
999 InvalidOid,
1000 ofTypeId,
1001 ownerId,
1002 accessMethodId,
1003 descriptor,
1004 list_concat(cookedDefaults,
1005 old_constraints),
1006 relkind,
1007 stmt->relation->relpersistence,
1008 false,
1009 false,
1010 stmt->oncommit,
1011 reloptions,
1012 true,
1013 allowSystemTableMods,
1014 false,
1015 InvalidOid,
1016 typaddress);
1019 * We must bump the command counter to make the newly-created relation
1020 * tuple visible for opening.
1022 CommandCounterIncrement();
1025 * Open the new relation and acquire exclusive lock on it. This isn't
1026 * really necessary for locking out other backends (since they can't see
1027 * the new rel anyway until we commit), but it keeps the lock manager from
1028 * complaining about deadlock risks.
1030 rel = relation_open(relationId, AccessExclusiveLock);
1033 * Now add any newly specified column default and generation expressions
1034 * to the new relation. These are passed to us in the form of raw
1035 * parsetrees; we need to transform them to executable expression trees
1036 * before they can be added. The most convenient way to do that is to
1037 * apply the parser's transformExpr routine, but transformExpr doesn't
1038 * work unless we have a pre-existing relation. So, the transformation has
1039 * to be postponed to this final step of CREATE TABLE.
1041 * This needs to be before processing the partitioning clauses because
1042 * those could refer to generated columns.
1044 if (rawDefaults)
1045 AddRelationNewConstraints(rel, rawDefaults, NIL,
1046 true, true, false, queryString);
1049 * Make column generation expressions visible for use by partitioning.
1051 CommandCounterIncrement();
1053 /* Process and store partition bound, if any. */
1054 if (stmt->partbound)
1056 PartitionBoundSpec *bound;
1057 ParseState *pstate;
1058 Oid parentId = linitial_oid(inheritOids),
1059 defaultPartOid;
1060 Relation parent,
1061 defaultRel = NULL;
1062 ParseNamespaceItem *nsitem;
1064 /* Already have strong enough lock on the parent */
1065 parent = table_open(parentId, NoLock);
1068 * We are going to try to validate the partition bound specification
1069 * against the partition key of parentRel, so it better have one.
1071 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1072 ereport(ERROR,
1073 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1074 errmsg("\"%s\" is not partitioned",
1075 RelationGetRelationName(parent))));
1078 * The partition constraint of the default partition depends on the
1079 * partition bounds of every other partition. It is possible that
1080 * another backend might be about to execute a query on the default
1081 * partition table, and that the query relies on previously cached
1082 * default partition constraints. We must therefore take a table lock
1083 * strong enough to prevent all queries on the default partition from
1084 * proceeding until we commit and send out a shared-cache-inval notice
1085 * that will make them update their index lists.
1087 * Order of locking: The relation being added won't be visible to
1088 * other backends until it is committed, hence here in
1089 * DefineRelation() the order of locking the default partition and the
1090 * relation being added does not matter. But at all other places we
1091 * need to lock the default relation before we lock the relation being
1092 * added or removed i.e. we should take the lock in same order at all
1093 * the places such that lock parent, lock default partition and then
1094 * lock the partition so as to avoid a deadlock.
1096 defaultPartOid =
1097 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1098 true));
1099 if (OidIsValid(defaultPartOid))
1100 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1102 /* Transform the bound values */
1103 pstate = make_parsestate(NULL);
1104 pstate->p_sourcetext = queryString;
1107 * Add an nsitem containing this relation, so that transformExpr
1108 * called on partition bound expressions is able to report errors
1109 * using a proper context.
1111 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1112 NULL, false, false);
1113 addNSItemToQuery(pstate, nsitem, false, true, true);
1115 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1118 * Check first that the new partition's bound is valid and does not
1119 * overlap with any of existing partitions of the parent.
1121 check_new_partition_bound(relname, parent, bound, pstate);
1124 * If the default partition exists, its partition constraints will
1125 * change after the addition of this new partition such that it won't
1126 * allow any row that qualifies for this new partition. So, check that
1127 * the existing data in the default partition satisfies the constraint
1128 * as it will exist after adding this partition.
1130 if (OidIsValid(defaultPartOid))
1132 check_default_partition_contents(parent, defaultRel, bound);
1133 /* Keep the lock until commit. */
1134 table_close(defaultRel, NoLock);
1137 /* Update the pg_class entry. */
1138 StorePartitionBound(rel, parent, bound);
1140 table_close(parent, NoLock);
1143 /* Store inheritance information for new rel. */
1144 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1147 * Process the partitioning specification (if any) and store the partition
1148 * key information into the catalog.
1150 if (partitioned)
1152 ParseState *pstate;
1153 int partnatts;
1154 AttrNumber partattrs[PARTITION_MAX_KEYS];
1155 Oid partopclass[PARTITION_MAX_KEYS];
1156 Oid partcollation[PARTITION_MAX_KEYS];
1157 List *partexprs = NIL;
1159 pstate = make_parsestate(NULL);
1160 pstate->p_sourcetext = queryString;
1162 partnatts = list_length(stmt->partspec->partParams);
1164 /* Protect fixed-size arrays here and in executor */
1165 if (partnatts > PARTITION_MAX_KEYS)
1166 ereport(ERROR,
1167 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1168 errmsg("cannot partition using more than %d columns",
1169 PARTITION_MAX_KEYS)));
1172 * We need to transform the raw parsetrees corresponding to partition
1173 * expressions into executable expression trees. Like column defaults
1174 * and CHECK constraints, we could not have done the transformation
1175 * earlier.
1177 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1179 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1180 partattrs, &partexprs, partopclass,
1181 partcollation, stmt->partspec->strategy);
1183 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1184 partexprs,
1185 partopclass, partcollation);
1187 /* make it all visible */
1188 CommandCounterIncrement();
1192 * If we're creating a partition, create now all the indexes, triggers,
1193 * FKs defined in the parent.
1195 * We can't do it earlier, because DefineIndex wants to know the partition
1196 * key which we just stored.
1198 if (stmt->partbound)
1200 Oid parentId = linitial_oid(inheritOids);
1201 Relation parent;
1202 List *idxlist;
1203 ListCell *cell;
1205 /* Already have strong enough lock on the parent */
1206 parent = table_open(parentId, NoLock);
1207 idxlist = RelationGetIndexList(parent);
1210 * For each index in the parent table, create one in the partition
1212 foreach(cell, idxlist)
1214 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1215 AttrMap *attmap;
1216 IndexStmt *idxstmt;
1217 Oid constraintOid;
1219 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1221 if (idxRel->rd_index->indisunique)
1222 ereport(ERROR,
1223 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1224 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1225 RelationGetRelationName(parent)),
1226 errdetail("Table \"%s\" contains indexes that are unique.",
1227 RelationGetRelationName(parent))));
1228 else
1230 index_close(idxRel, AccessShareLock);
1231 continue;
1235 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1236 RelationGetDescr(parent),
1237 false);
1238 idxstmt =
1239 generateClonedIndexStmt(NULL, idxRel,
1240 attmap, &constraintOid);
1241 DefineIndex(RelationGetRelid(rel),
1242 idxstmt,
1243 InvalidOid,
1244 RelationGetRelid(idxRel),
1245 constraintOid,
1247 false, false, false, false, false);
1249 index_close(idxRel, AccessShareLock);
1252 list_free(idxlist);
1255 * If there are any row-level triggers, clone them to the new
1256 * partition.
1258 if (parent->trigdesc != NULL)
1259 CloneRowTriggersToPartition(parent, rel);
1262 * And foreign keys too. Note that because we're freshly creating the
1263 * table, there is no need to verify these new constraints.
1265 CloneForeignKeyConstraints(NULL, parent, rel);
1267 table_close(parent, NoLock);
1271 * Now add any newly specified CHECK constraints to the new relation. Same
1272 * as for defaults above, but these need to come after partitioning is set
1273 * up.
1275 if (stmt->constraints)
1276 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1277 true, true, false, queryString);
1280 * Finally, merge the not-null constraints that are declared directly with
1281 * those that come from parent relations (making sure to count inheritance
1282 * appropriately for each), create them, and set the attnotnull flag on
1283 * columns that don't yet have it.
1285 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1286 old_notnulls);
1287 foreach(listptr, nncols)
1288 set_attnotnull(NULL, rel, lfirst_int(listptr), false, NoLock);
1290 ObjectAddressSet(address, RelationRelationId, relationId);
1293 * Clean up. We keep lock on new relation (although it shouldn't be
1294 * visible to anyone else anyway, until commit).
1296 relation_close(rel, NoLock);
1298 return address;
1302 * BuildDescForRelation
1304 * Given a list of ColumnDef nodes, build a TupleDesc.
1306 * Note: tdtypeid will need to be filled in later on.
1308 TupleDesc
1309 BuildDescForRelation(const List *columns)
1311 int natts;
1312 AttrNumber attnum;
1313 ListCell *l;
1314 TupleDesc desc;
1315 bool has_not_null;
1316 char *attname;
1317 Oid atttypid;
1318 int32 atttypmod;
1319 Oid attcollation;
1320 int attdim;
1323 * allocate a new tuple descriptor
1325 natts = list_length(columns);
1326 desc = CreateTemplateTupleDesc(natts);
1327 has_not_null = false;
1329 attnum = 0;
1331 foreach(l, columns)
1333 ColumnDef *entry = lfirst(l);
1334 AclResult aclresult;
1335 Form_pg_attribute att;
1338 * for each entry in the list, get the name and type information from
1339 * the list and have TupleDescInitEntry fill in the attribute
1340 * information we need.
1342 attnum++;
1344 attname = entry->colname;
1345 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1347 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1348 if (aclresult != ACLCHECK_OK)
1349 aclcheck_error_type(aclresult, atttypid);
1351 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1352 attdim = list_length(entry->typeName->arrayBounds);
1353 if (attdim > PG_INT16_MAX)
1354 ereport(ERROR,
1355 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1356 errmsg("too many array dimensions"));
1358 if (entry->typeName->setof)
1359 ereport(ERROR,
1360 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1361 errmsg("column \"%s\" cannot be declared SETOF",
1362 attname)));
1364 TupleDescInitEntry(desc, attnum, attname,
1365 atttypid, atttypmod, attdim);
1366 att = TupleDescAttr(desc, attnum - 1);
1368 /* Override TupleDescInitEntry's settings as requested */
1369 TupleDescInitEntryCollation(desc, attnum, attcollation);
1371 /* Fill in additional stuff not handled by TupleDescInitEntry */
1372 att->attnotnull = entry->is_not_null;
1373 has_not_null |= entry->is_not_null;
1374 att->attislocal = entry->is_local;
1375 att->attinhcount = entry->inhcount;
1376 att->attidentity = entry->identity;
1377 att->attgenerated = entry->generated;
1378 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1379 if (entry->storage)
1380 att->attstorage = entry->storage;
1381 else if (entry->storage_name)
1382 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1385 if (has_not_null)
1387 TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
1389 constr->has_not_null = true;
1390 constr->has_generated_stored = false;
1391 constr->defval = NULL;
1392 constr->missing = NULL;
1393 constr->num_defval = 0;
1394 constr->check = NULL;
1395 constr->num_check = 0;
1396 desc->constr = constr;
1398 else
1400 desc->constr = NULL;
1403 return desc;
1407 * Emit the right error or warning message for a "DROP" command issued on a
1408 * non-existent relation
1410 static void
1411 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1413 const struct dropmsgstrings *rentry;
1415 if (rel->schemaname != NULL &&
1416 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1418 if (!missing_ok)
1420 ereport(ERROR,
1421 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1422 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1424 else
1426 ereport(NOTICE,
1427 (errmsg("schema \"%s\" does not exist, skipping",
1428 rel->schemaname)));
1430 return;
1433 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1435 if (rentry->kind == rightkind)
1437 if (!missing_ok)
1439 ereport(ERROR,
1440 (errcode(rentry->nonexistent_code),
1441 errmsg(rentry->nonexistent_msg, rel->relname)));
1443 else
1445 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1446 break;
1451 Assert(rentry->kind != '\0'); /* Should be impossible */
1455 * Emit the right error message for a "DROP" command issued on a
1456 * relation of the wrong type
1458 static void
1459 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1461 const struct dropmsgstrings *rentry;
1462 const struct dropmsgstrings *wentry;
1464 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1465 if (rentry->kind == rightkind)
1466 break;
1467 Assert(rentry->kind != '\0');
1469 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1470 if (wentry->kind == wrongkind)
1471 break;
1472 /* wrongkind could be something we don't have in our table... */
1474 ereport(ERROR,
1475 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1476 errmsg(rentry->nota_msg, relname),
1477 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1481 * RemoveRelations
1482 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1483 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1485 void
1486 RemoveRelations(DropStmt *drop)
1488 ObjectAddresses *objects;
1489 char relkind;
1490 ListCell *cell;
1491 int flags = 0;
1492 LOCKMODE lockmode = AccessExclusiveLock;
1494 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1495 if (drop->concurrent)
1498 * Note that for temporary relations this lock may get upgraded later
1499 * on, but as no other session can access a temporary relation, this
1500 * is actually fine.
1502 lockmode = ShareUpdateExclusiveLock;
1503 Assert(drop->removeType == OBJECT_INDEX);
1504 if (list_length(drop->objects) != 1)
1505 ereport(ERROR,
1506 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1507 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1508 if (drop->behavior == DROP_CASCADE)
1509 ereport(ERROR,
1510 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1511 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1515 * First we identify all the relations, then we delete them in a single
1516 * performMultipleDeletions() call. This is to avoid unwanted DROP
1517 * RESTRICT errors if one of the relations depends on another.
1520 /* Determine required relkind */
1521 switch (drop->removeType)
1523 case OBJECT_TABLE:
1524 relkind = RELKIND_RELATION;
1525 break;
1527 case OBJECT_INDEX:
1528 relkind = RELKIND_INDEX;
1529 break;
1531 case OBJECT_SEQUENCE:
1532 relkind = RELKIND_SEQUENCE;
1533 break;
1535 case OBJECT_VIEW:
1536 relkind = RELKIND_VIEW;
1537 break;
1539 case OBJECT_MATVIEW:
1540 relkind = RELKIND_MATVIEW;
1541 break;
1543 case OBJECT_FOREIGN_TABLE:
1544 relkind = RELKIND_FOREIGN_TABLE;
1545 break;
1547 default:
1548 elog(ERROR, "unrecognized drop object type: %d",
1549 (int) drop->removeType);
1550 relkind = 0; /* keep compiler quiet */
1551 break;
1554 /* Lock and validate each relation; build a list of object addresses */
1555 objects = new_object_addresses();
1557 foreach(cell, drop->objects)
1559 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1560 Oid relOid;
1561 ObjectAddress obj;
1562 struct DropRelationCallbackState state;
1565 * These next few steps are a great deal like relation_openrv, but we
1566 * don't bother building a relcache entry since we don't need it.
1568 * Check for shared-cache-inval messages before trying to access the
1569 * relation. This is needed to cover the case where the name
1570 * identifies a rel that has been dropped and recreated since the
1571 * start of our transaction: if we don't flush the old syscache entry,
1572 * then we'll latch onto that entry and suffer an error later.
1574 AcceptInvalidationMessages();
1576 /* Look up the appropriate relation using namespace search. */
1577 state.expected_relkind = relkind;
1578 state.heap_lockmode = drop->concurrent ?
1579 ShareUpdateExclusiveLock : AccessExclusiveLock;
1580 /* We must initialize these fields to show that no locks are held: */
1581 state.heapOid = InvalidOid;
1582 state.partParentOid = InvalidOid;
1584 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1585 RangeVarCallbackForDropRelation,
1586 (void *) &state);
1588 /* Not there? */
1589 if (!OidIsValid(relOid))
1591 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1592 continue;
1596 * Decide if concurrent mode needs to be used here or not. The
1597 * callback retrieved the rel's persistence for us.
1599 if (drop->concurrent &&
1600 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1602 Assert(list_length(drop->objects) == 1 &&
1603 drop->removeType == OBJECT_INDEX);
1604 flags |= PERFORM_DELETION_CONCURRENTLY;
1608 * Concurrent index drop cannot be used with partitioned indexes,
1609 * either.
1611 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1612 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1613 ereport(ERROR,
1614 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1615 errmsg("cannot drop partitioned index \"%s\" concurrently",
1616 rel->relname)));
1619 * If we're told to drop a partitioned index, we must acquire lock on
1620 * all the children of its parent partitioned table before proceeding.
1621 * Otherwise we'd try to lock the child index partitions before their
1622 * tables, leading to potential deadlock against other sessions that
1623 * will lock those objects in the other order.
1625 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1626 (void) find_all_inheritors(state.heapOid,
1627 state.heap_lockmode,
1628 NULL);
1630 /* OK, we're ready to delete this one */
1631 obj.classId = RelationRelationId;
1632 obj.objectId = relOid;
1633 obj.objectSubId = 0;
1635 add_exact_object_address(&obj, objects);
1638 performMultipleDeletions(objects, drop->behavior, flags);
1640 free_object_addresses(objects);
1644 * Before acquiring a table lock, check whether we have sufficient rights.
1645 * In the case of DROP INDEX, also try to lock the table before the index.
1646 * Also, if the table to be dropped is a partition, we try to lock the parent
1647 * first.
1649 static void
1650 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1651 void *arg)
1653 HeapTuple tuple;
1654 struct DropRelationCallbackState *state;
1655 char expected_relkind;
1656 bool is_partition;
1657 Form_pg_class classform;
1658 LOCKMODE heap_lockmode;
1659 bool invalid_system_index = false;
1661 state = (struct DropRelationCallbackState *) arg;
1662 heap_lockmode = state->heap_lockmode;
1665 * If we previously locked some other index's heap, and the name we're
1666 * looking up no longer refers to that relation, release the now-useless
1667 * lock.
1669 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1671 UnlockRelationOid(state->heapOid, heap_lockmode);
1672 state->heapOid = InvalidOid;
1676 * Similarly, if we previously locked some other partition's heap, and the
1677 * name we're looking up no longer refers to that relation, release the
1678 * now-useless lock.
1680 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1682 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1683 state->partParentOid = InvalidOid;
1686 /* Didn't find a relation, so no need for locking or permission checks. */
1687 if (!OidIsValid(relOid))
1688 return;
1690 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1691 if (!HeapTupleIsValid(tuple))
1692 return; /* concurrently dropped, so nothing to do */
1693 classform = (Form_pg_class) GETSTRUCT(tuple);
1694 is_partition = classform->relispartition;
1696 /* Pass back some data to save lookups in RemoveRelations */
1697 state->actual_relkind = classform->relkind;
1698 state->actual_relpersistence = classform->relpersistence;
1701 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1702 * but RemoveRelations() can only pass one relkind for a given relation.
1703 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1704 * That means we must be careful before giving the wrong type error when
1705 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1706 * exists with indexes.
1708 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1709 expected_relkind = RELKIND_RELATION;
1710 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1711 expected_relkind = RELKIND_INDEX;
1712 else
1713 expected_relkind = classform->relkind;
1715 if (state->expected_relkind != expected_relkind)
1716 DropErrorMsgWrongType(rel->relname, classform->relkind,
1717 state->expected_relkind);
1719 /* Allow DROP to either table owner or schema owner */
1720 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1721 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1722 aclcheck_error(ACLCHECK_NOT_OWNER,
1723 get_relkind_objtype(classform->relkind),
1724 rel->relname);
1727 * Check the case of a system index that might have been invalidated by a
1728 * failed concurrent process and allow its drop. For the time being, this
1729 * only concerns indexes of toast relations that became invalid during a
1730 * REINDEX CONCURRENTLY process.
1732 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1734 HeapTuple locTuple;
1735 Form_pg_index indexform;
1736 bool indisvalid;
1738 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1739 if (!HeapTupleIsValid(locTuple))
1741 ReleaseSysCache(tuple);
1742 return;
1745 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1746 indisvalid = indexform->indisvalid;
1747 ReleaseSysCache(locTuple);
1749 /* Mark object as being an invalid index of system catalogs */
1750 if (!indisvalid)
1751 invalid_system_index = true;
1754 /* In the case of an invalid index, it is fine to bypass this check */
1755 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1756 ereport(ERROR,
1757 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1758 errmsg("permission denied: \"%s\" is a system catalog",
1759 rel->relname)));
1761 ReleaseSysCache(tuple);
1764 * In DROP INDEX, attempt to acquire lock on the parent table before
1765 * locking the index. index_drop() will need this anyway, and since
1766 * regular queries lock tables before their indexes, we risk deadlock if
1767 * we do it the other way around. No error if we don't find a pg_index
1768 * entry, though --- the relation may have been dropped. Note that this
1769 * code will execute for either plain or partitioned indexes.
1771 if (expected_relkind == RELKIND_INDEX &&
1772 relOid != oldRelOid)
1774 state->heapOid = IndexGetRelation(relOid, true);
1775 if (OidIsValid(state->heapOid))
1776 LockRelationOid(state->heapOid, heap_lockmode);
1780 * Similarly, if the relation is a partition, we must acquire lock on its
1781 * parent before locking the partition. That's because queries lock the
1782 * parent before its partitions, so we risk deadlock if we do it the other
1783 * way around.
1785 if (is_partition && relOid != oldRelOid)
1787 state->partParentOid = get_partition_parent(relOid, true);
1788 if (OidIsValid(state->partParentOid))
1789 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1794 * ExecuteTruncate
1795 * Executes a TRUNCATE command.
1797 * This is a multi-relation truncate. We first open and grab exclusive
1798 * lock on all relations involved, checking permissions and otherwise
1799 * verifying that the relation is OK for truncation. Note that if relations
1800 * are foreign tables, at this stage, we have not yet checked that their
1801 * foreign data in external data sources are OK for truncation. These are
1802 * checked when foreign data are actually truncated later. In CASCADE mode,
1803 * relations having FK references to the targeted relations are automatically
1804 * added to the group; in RESTRICT mode, we check that all FK references are
1805 * internal to the group that's being truncated. Finally all the relations
1806 * are truncated and reindexed.
1808 void
1809 ExecuteTruncate(TruncateStmt *stmt)
1811 List *rels = NIL;
1812 List *relids = NIL;
1813 List *relids_logged = NIL;
1814 ListCell *cell;
1817 * Open, exclusive-lock, and check all the explicitly-specified relations
1819 foreach(cell, stmt->relations)
1821 RangeVar *rv = lfirst(cell);
1822 Relation rel;
1823 bool recurse = rv->inh;
1824 Oid myrelid;
1825 LOCKMODE lockmode = AccessExclusiveLock;
1827 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1828 0, RangeVarCallbackForTruncate,
1829 NULL);
1831 /* don't throw error for "TRUNCATE foo, foo" */
1832 if (list_member_oid(relids, myrelid))
1833 continue;
1835 /* open the relation, we already hold a lock on it */
1836 rel = table_open(myrelid, NoLock);
1839 * RangeVarGetRelidExtended() has done most checks with its callback,
1840 * but other checks with the now-opened Relation remain.
1842 truncate_check_activity(rel);
1844 rels = lappend(rels, rel);
1845 relids = lappend_oid(relids, myrelid);
1847 /* Log this relation only if needed for logical decoding */
1848 if (RelationIsLogicallyLogged(rel))
1849 relids_logged = lappend_oid(relids_logged, myrelid);
1851 if (recurse)
1853 ListCell *child;
1854 List *children;
1856 children = find_all_inheritors(myrelid, lockmode, NULL);
1858 foreach(child, children)
1860 Oid childrelid = lfirst_oid(child);
1862 if (list_member_oid(relids, childrelid))
1863 continue;
1865 /* find_all_inheritors already got lock */
1866 rel = table_open(childrelid, NoLock);
1869 * It is possible that the parent table has children that are
1870 * temp tables of other backends. We cannot safely access
1871 * such tables (because of buffering issues), and the best
1872 * thing to do is to silently ignore them. Note that this
1873 * check is the same as one of the checks done in
1874 * truncate_check_activity() called below, still it is kept
1875 * here for simplicity.
1877 if (RELATION_IS_OTHER_TEMP(rel))
1879 table_close(rel, lockmode);
1880 continue;
1884 * Inherited TRUNCATE commands perform access permission
1885 * checks on the parent table only. So we skip checking the
1886 * children's permissions and don't call
1887 * truncate_check_perms() here.
1889 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1890 truncate_check_activity(rel);
1892 rels = lappend(rels, rel);
1893 relids = lappend_oid(relids, childrelid);
1895 /* Log this relation only if needed for logical decoding */
1896 if (RelationIsLogicallyLogged(rel))
1897 relids_logged = lappend_oid(relids_logged, childrelid);
1900 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1901 ereport(ERROR,
1902 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1903 errmsg("cannot truncate only a partitioned table"),
1904 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1907 ExecuteTruncateGuts(rels, relids, relids_logged,
1908 stmt->behavior, stmt->restart_seqs, false);
1910 /* And close the rels */
1911 foreach(cell, rels)
1913 Relation rel = (Relation) lfirst(cell);
1915 table_close(rel, NoLock);
1920 * ExecuteTruncateGuts
1922 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1923 * command (see above) as well as replication subscribers that execute a
1924 * replicated TRUNCATE action.
1926 * explicit_rels is the list of Relations to truncate that the command
1927 * specified. relids is the list of Oids corresponding to explicit_rels.
1928 * relids_logged is the list of Oids (a subset of relids) that require
1929 * WAL-logging. This is all a bit redundant, but the existing callers have
1930 * this information handy in this form.
1932 void
1933 ExecuteTruncateGuts(List *explicit_rels,
1934 List *relids,
1935 List *relids_logged,
1936 DropBehavior behavior, bool restart_seqs,
1937 bool run_as_table_owner)
1939 List *rels;
1940 List *seq_relids = NIL;
1941 HTAB *ft_htab = NULL;
1942 EState *estate;
1943 ResultRelInfo *resultRelInfos;
1944 ResultRelInfo *resultRelInfo;
1945 SubTransactionId mySubid;
1946 ListCell *cell;
1947 Oid *logrelids;
1950 * Check the explicitly-specified relations.
1952 * In CASCADE mode, suck in all referencing relations as well. This
1953 * requires multiple iterations to find indirectly-dependent relations. At
1954 * each phase, we need to exclusive-lock new rels before looking for their
1955 * dependencies, else we might miss something. Also, we check each rel as
1956 * soon as we open it, to avoid a faux pas such as holding lock for a long
1957 * time on a rel we have no permissions for.
1959 rels = list_copy(explicit_rels);
1960 if (behavior == DROP_CASCADE)
1962 for (;;)
1964 List *newrelids;
1966 newrelids = heap_truncate_find_FKs(relids);
1967 if (newrelids == NIL)
1968 break; /* nothing else to add */
1970 foreach(cell, newrelids)
1972 Oid relid = lfirst_oid(cell);
1973 Relation rel;
1975 rel = table_open(relid, AccessExclusiveLock);
1976 ereport(NOTICE,
1977 (errmsg("truncate cascades to table \"%s\"",
1978 RelationGetRelationName(rel))));
1979 truncate_check_rel(relid, rel->rd_rel);
1980 truncate_check_perms(relid, rel->rd_rel);
1981 truncate_check_activity(rel);
1982 rels = lappend(rels, rel);
1983 relids = lappend_oid(relids, relid);
1985 /* Log this relation only if needed for logical decoding */
1986 if (RelationIsLogicallyLogged(rel))
1987 relids_logged = lappend_oid(relids_logged, relid);
1993 * Check foreign key references. In CASCADE mode, this should be
1994 * unnecessary since we just pulled in all the references; but as a
1995 * cross-check, do it anyway if in an Assert-enabled build.
1997 #ifdef USE_ASSERT_CHECKING
1998 heap_truncate_check_FKs(rels, false);
1999 #else
2000 if (behavior == DROP_RESTRICT)
2001 heap_truncate_check_FKs(rels, false);
2002 #endif
2005 * If we are asked to restart sequences, find all the sequences, lock them
2006 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2007 * We want to do this early since it's pointless to do all the truncation
2008 * work only to fail on sequence permissions.
2010 if (restart_seqs)
2012 foreach(cell, rels)
2014 Relation rel = (Relation) lfirst(cell);
2015 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
2016 ListCell *seqcell;
2018 foreach(seqcell, seqlist)
2020 Oid seq_relid = lfirst_oid(seqcell);
2021 Relation seq_rel;
2023 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
2025 /* This check must match AlterSequence! */
2026 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
2027 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
2028 RelationGetRelationName(seq_rel));
2030 seq_relids = lappend_oid(seq_relids, seq_relid);
2032 relation_close(seq_rel, NoLock);
2037 /* Prepare to catch AFTER triggers. */
2038 AfterTriggerBeginQuery();
2041 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2042 * each relation. We don't need to call ExecOpenIndices, though.
2044 * We put the ResultRelInfos in the es_opened_result_relations list, even
2045 * though we don't have a range table and don't populate the
2046 * es_result_relations array. That's a bit bogus, but it's enough to make
2047 * ExecGetTriggerResultRel() find them.
2049 estate = CreateExecutorState();
2050 resultRelInfos = (ResultRelInfo *)
2051 palloc(list_length(rels) * sizeof(ResultRelInfo));
2052 resultRelInfo = resultRelInfos;
2053 foreach(cell, rels)
2055 Relation rel = (Relation) lfirst(cell);
2057 InitResultRelInfo(resultRelInfo,
2058 rel,
2059 0, /* dummy rangetable index */
2060 NULL,
2062 estate->es_opened_result_relations =
2063 lappend(estate->es_opened_result_relations, resultRelInfo);
2064 resultRelInfo++;
2068 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2069 * truncating (this is because one of them might throw an error). Also, if
2070 * we were to allow them to prevent statement execution, that would need
2071 * to be handled here.
2073 resultRelInfo = resultRelInfos;
2074 foreach(cell, rels)
2076 UserContext ucxt;
2078 if (run_as_table_owner)
2079 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2080 &ucxt);
2081 ExecBSTruncateTriggers(estate, resultRelInfo);
2082 if (run_as_table_owner)
2083 RestoreUserContext(&ucxt);
2084 resultRelInfo++;
2088 * OK, truncate each table.
2090 mySubid = GetCurrentSubTransactionId();
2092 foreach(cell, rels)
2094 Relation rel = (Relation) lfirst(cell);
2096 /* Skip partitioned tables as there is nothing to do */
2097 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2098 continue;
2101 * Build the lists of foreign tables belonging to each foreign server
2102 * and pass each list to the foreign data wrapper's callback function,
2103 * so that each server can truncate its all foreign tables in bulk.
2104 * Each list is saved as a single entry in a hash table that uses the
2105 * server OID as lookup key.
2107 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2109 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2110 bool found;
2111 ForeignTruncateInfo *ft_info;
2113 /* First time through, initialize hashtable for foreign tables */
2114 if (!ft_htab)
2116 HASHCTL hctl;
2118 memset(&hctl, 0, sizeof(HASHCTL));
2119 hctl.keysize = sizeof(Oid);
2120 hctl.entrysize = sizeof(ForeignTruncateInfo);
2121 hctl.hcxt = CurrentMemoryContext;
2123 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2124 32, /* start small and extend */
2125 &hctl,
2126 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2129 /* Find or create cached entry for the foreign table */
2130 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2131 if (!found)
2132 ft_info->rels = NIL;
2135 * Save the foreign table in the entry of the server that the
2136 * foreign table belongs to.
2138 ft_info->rels = lappend(ft_info->rels, rel);
2139 continue;
2143 * Normally, we need a transaction-safe truncation here. However, if
2144 * the table was either created in the current (sub)transaction or has
2145 * a new relfilenumber in the current (sub)transaction, then we can
2146 * just truncate it in-place, because a rollback would cause the whole
2147 * table or the current physical file to be thrown away anyway.
2149 if (rel->rd_createSubid == mySubid ||
2150 rel->rd_newRelfilelocatorSubid == mySubid)
2152 /* Immediate, non-rollbackable truncation is OK */
2153 heap_truncate_one_rel(rel);
2155 else
2157 Oid heap_relid;
2158 Oid toast_relid;
2159 ReindexParams reindex_params = {0};
2162 * This effectively deletes all rows in the table, and may be done
2163 * in a serializable transaction. In that case we must record a
2164 * rw-conflict in to this transaction from each transaction
2165 * holding a predicate lock on the table.
2167 CheckTableForSerializableConflictIn(rel);
2170 * Need the full transaction-safe pushups.
2172 * Create a new empty storage file for the relation, and assign it
2173 * as the relfilenumber value. The old storage file is scheduled
2174 * for deletion at commit.
2176 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2178 heap_relid = RelationGetRelid(rel);
2181 * The same for the toast table, if any.
2183 toast_relid = rel->rd_rel->reltoastrelid;
2184 if (OidIsValid(toast_relid))
2186 Relation toastrel = relation_open(toast_relid,
2187 AccessExclusiveLock);
2189 RelationSetNewRelfilenumber(toastrel,
2190 toastrel->rd_rel->relpersistence);
2191 table_close(toastrel, NoLock);
2195 * Reconstruct the indexes to match, and we're done.
2197 reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2198 &reindex_params);
2201 pgstat_count_truncate(rel);
2204 /* Now go through the hash table, and truncate foreign tables */
2205 if (ft_htab)
2207 ForeignTruncateInfo *ft_info;
2208 HASH_SEQ_STATUS seq;
2210 hash_seq_init(&seq, ft_htab);
2212 PG_TRY();
2214 while ((ft_info = hash_seq_search(&seq)) != NULL)
2216 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2218 /* truncate_check_rel() has checked that already */
2219 Assert(routine->ExecForeignTruncate != NULL);
2221 routine->ExecForeignTruncate(ft_info->rels,
2222 behavior,
2223 restart_seqs);
2226 PG_FINALLY();
2228 hash_destroy(ft_htab);
2230 PG_END_TRY();
2234 * Restart owned sequences if we were asked to.
2236 foreach(cell, seq_relids)
2238 Oid seq_relid = lfirst_oid(cell);
2240 ResetSequence(seq_relid);
2244 * Write a WAL record to allow this set of actions to be logically
2245 * decoded.
2247 * Assemble an array of relids so we can write a single WAL record for the
2248 * whole action.
2250 if (relids_logged != NIL)
2252 xl_heap_truncate xlrec;
2253 int i = 0;
2255 /* should only get here if wal_level >= logical */
2256 Assert(XLogLogicalInfoActive());
2258 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2259 foreach(cell, relids_logged)
2260 logrelids[i++] = lfirst_oid(cell);
2262 xlrec.dbId = MyDatabaseId;
2263 xlrec.nrelids = list_length(relids_logged);
2264 xlrec.flags = 0;
2265 if (behavior == DROP_CASCADE)
2266 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2267 if (restart_seqs)
2268 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2270 XLogBeginInsert();
2271 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2272 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2274 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2276 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2280 * Process all AFTER STATEMENT TRUNCATE triggers.
2282 resultRelInfo = resultRelInfos;
2283 foreach(cell, rels)
2285 UserContext ucxt;
2287 if (run_as_table_owner)
2288 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2289 &ucxt);
2290 ExecASTruncateTriggers(estate, resultRelInfo);
2291 if (run_as_table_owner)
2292 RestoreUserContext(&ucxt);
2293 resultRelInfo++;
2296 /* Handle queued AFTER triggers */
2297 AfterTriggerEndQuery(estate);
2299 /* We can clean up the EState now */
2300 FreeExecutorState(estate);
2303 * Close any rels opened by CASCADE (can't do this while EState still
2304 * holds refs)
2306 rels = list_difference_ptr(rels, explicit_rels);
2307 foreach(cell, rels)
2309 Relation rel = (Relation) lfirst(cell);
2311 table_close(rel, NoLock);
2316 * Check that a given relation is safe to truncate. Subroutine for
2317 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2319 static void
2320 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2322 char *relname = NameStr(reltuple->relname);
2325 * Only allow truncate on regular tables, foreign tables using foreign
2326 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2327 * latter are only being included here for the following checks; no
2328 * physical truncation will occur in their case.).
2330 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2332 Oid serverid = GetForeignServerIdByRelId(relid);
2333 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2335 if (!fdwroutine->ExecForeignTruncate)
2336 ereport(ERROR,
2337 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2338 errmsg("cannot truncate foreign table \"%s\"",
2339 relname)));
2341 else if (reltuple->relkind != RELKIND_RELATION &&
2342 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2343 ereport(ERROR,
2344 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2345 errmsg("\"%s\" is not a table", relname)));
2348 * Most system catalogs can't be truncated at all, or at least not unless
2349 * allow_system_table_mods=on. As an exception, however, we allow
2350 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2351 * to change its relfilenode to match the old cluster, and allowing a
2352 * TRUNCATE command to be executed is the easiest way of doing that.
2354 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2355 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2356 ereport(ERROR,
2357 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2358 errmsg("permission denied: \"%s\" is a system catalog",
2359 relname)));
2361 InvokeObjectTruncateHook(relid);
2365 * Check that current user has the permission to truncate given relation.
2367 static void
2368 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2370 char *relname = NameStr(reltuple->relname);
2371 AclResult aclresult;
2373 /* Permissions checks */
2374 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2375 if (aclresult != ACLCHECK_OK)
2376 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2377 relname);
2381 * Set of extra sanity checks to check if a given relation is safe to
2382 * truncate. This is split with truncate_check_rel() as
2383 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2385 static void
2386 truncate_check_activity(Relation rel)
2389 * Don't allow truncate on temp tables of other backends ... their local
2390 * buffer manager is not going to cope.
2392 if (RELATION_IS_OTHER_TEMP(rel))
2393 ereport(ERROR,
2394 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2395 errmsg("cannot truncate temporary tables of other sessions")));
2398 * Also check for active uses of the relation in the current transaction,
2399 * including open scans and pending AFTER trigger events.
2401 CheckTableNotInUse(rel, "TRUNCATE");
2405 * storage_name
2406 * returns the name corresponding to a typstorage/attstorage enum value
2408 static const char *
2409 storage_name(char c)
2411 switch (c)
2413 case TYPSTORAGE_PLAIN:
2414 return "PLAIN";
2415 case TYPSTORAGE_EXTERNAL:
2416 return "EXTERNAL";
2417 case TYPSTORAGE_EXTENDED:
2418 return "EXTENDED";
2419 case TYPSTORAGE_MAIN:
2420 return "MAIN";
2421 default:
2422 return "???";
2426 /*----------
2427 * MergeAttributes
2428 * Returns new schema given initial schema and superclasses.
2430 * Input arguments:
2431 * 'columns' is the column/attribute definition for the table. (It's a list
2432 * of ColumnDef's.) It is destructively changed.
2433 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2434 * 'relpersistence' is the persistence type of the table.
2435 * 'is_partition' tells if the table is a partition.
2437 * Output arguments:
2438 * 'supconstr' receives a list of constraints belonging to the parents,
2439 * updated as necessary to be valid for the child.
2440 * 'supnotnulls' receives a list of CookedConstraints that corresponds to
2441 * constraints coming from inheritance parents.
2443 * Return value:
2444 * Completed schema list.
2446 * Notes:
2447 * The order in which the attributes are inherited is very important.
2448 * Intuitively, the inherited attributes should come first. If a table
2449 * inherits from multiple parents, the order of those attributes are
2450 * according to the order of the parents specified in CREATE TABLE.
2452 * Here's an example:
2454 * create table person (name text, age int4, location point);
2455 * create table emp (salary int4, manager text) inherits(person);
2456 * create table student (gpa float8) inherits (person);
2457 * create table stud_emp (percent int4) inherits (emp, student);
2459 * The order of the attributes of stud_emp is:
2461 * person {1:name, 2:age, 3:location}
2462 * / \
2463 * {6:gpa} student emp {4:salary, 5:manager}
2464 * \ /
2465 * stud_emp {7:percent}
2467 * If the same attribute name appears multiple times, then it appears
2468 * in the result table in the proper location for its first appearance.
2470 * Constraints (including not-null constraints) for the child table
2471 * are the union of all relevant constraints, from both the child schema
2472 * and parent tables. In addition, in legacy inheritance, each column that
2473 * appears in a primary key in any of the parents also gets a NOT NULL
2474 * constraint (partitioning doesn't need this, because the PK itself gets
2475 * inherited.)
2477 * The default value for a child column is defined as:
2478 * (1) If the child schema specifies a default, that value is used.
2479 * (2) If neither the child nor any parent specifies a default, then
2480 * the column will not have a default.
2481 * (3) If conflicting defaults are inherited from different parents
2482 * (and not overridden by the child), an error is raised.
2483 * (4) Otherwise the inherited default is used.
2485 * Note that the default-value infrastructure is used for generated
2486 * columns' expressions too, so most of the preceding paragraph applies
2487 * to generation expressions too. We insist that a child column be
2488 * generated if and only if its parent(s) are, but it need not have
2489 * the same generation expression.
2490 *----------
2492 static List *
2493 MergeAttributes(List *columns, const List *supers, char relpersistence,
2494 bool is_partition, List **supconstr, List **supnotnulls)
2496 List *inh_columns = NIL;
2497 List *constraints = NIL;
2498 List *nnconstraints = NIL;
2499 bool have_bogus_defaults = false;
2500 int child_attno;
2501 static Node bogus_marker = {0}; /* marks conflicting defaults */
2502 List *saved_columns = NIL;
2503 ListCell *lc;
2506 * Check for and reject tables with too many columns. We perform this
2507 * check relatively early for two reasons: (a) we don't run the risk of
2508 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2509 * okay if we're processing <= 1600 columns, but could take minutes to
2510 * execute if the user attempts to create a table with hundreds of
2511 * thousands of columns.
2513 * Note that we also need to check that we do not exceed this figure after
2514 * including columns from inherited relations.
2516 if (list_length(columns) > MaxHeapAttributeNumber)
2517 ereport(ERROR,
2518 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2519 errmsg("tables can have at most %d columns",
2520 MaxHeapAttributeNumber)));
2523 * Check for duplicate names in the explicit list of attributes.
2525 * Although we might consider merging such entries in the same way that we
2526 * handle name conflicts for inherited attributes, it seems to make more
2527 * sense to assume such conflicts are errors.
2529 * We don't use foreach() here because we have two nested loops over the
2530 * columns list, with possible element deletions in the inner one. If we
2531 * used foreach_delete_current() it could only fix up the state of one of
2532 * the loops, so it seems cleaner to use looping over list indexes for
2533 * both loops. Note that any deletion will happen beyond where the outer
2534 * loop is, so its index never needs adjustment.
2536 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2538 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2540 if (!is_partition && coldef->typeName == NULL)
2543 * Typed table column option that does not belong to a column from
2544 * the type. This works because the columns from the type come
2545 * first in the list. (We omit this check for partition column
2546 * lists; those are processed separately below.)
2548 ereport(ERROR,
2549 (errcode(ERRCODE_UNDEFINED_COLUMN),
2550 errmsg("column \"%s\" does not exist",
2551 coldef->colname)));
2554 /* restpos scans all entries beyond coldef; incr is in loop body */
2555 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2557 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2559 if (strcmp(coldef->colname, restdef->colname) == 0)
2561 if (coldef->is_from_type)
2564 * merge the column options into the column from the type
2566 coldef->is_not_null = restdef->is_not_null;
2567 coldef->raw_default = restdef->raw_default;
2568 coldef->cooked_default = restdef->cooked_default;
2569 coldef->constraints = restdef->constraints;
2570 coldef->is_from_type = false;
2571 columns = list_delete_nth_cell(columns, restpos);
2573 else
2574 ereport(ERROR,
2575 (errcode(ERRCODE_DUPLICATE_COLUMN),
2576 errmsg("column \"%s\" specified more than once",
2577 coldef->colname)));
2579 else
2580 restpos++;
2585 * In case of a partition, there are no new column definitions, only dummy
2586 * ColumnDefs created for column constraints. Set them aside for now and
2587 * process them at the end.
2589 if (is_partition)
2591 saved_columns = columns;
2592 columns = NIL;
2596 * Scan the parents left-to-right, and merge their attributes to form a
2597 * list of inherited columns (inh_columns).
2599 child_attno = 0;
2600 foreach(lc, supers)
2602 Oid parent = lfirst_oid(lc);
2603 Relation relation;
2604 TupleDesc tupleDesc;
2605 TupleConstr *constr;
2606 AttrMap *newattmap;
2607 List *inherited_defaults;
2608 List *cols_with_defaults;
2609 List *nnconstrs;
2610 ListCell *lc1;
2611 ListCell *lc2;
2612 Bitmapset *pkattrs;
2613 Bitmapset *nncols = NULL;
2615 /* caller already got lock */
2616 relation = table_open(parent, NoLock);
2619 * Check for active uses of the parent partitioned table in the
2620 * current transaction, such as being used in some manner by an
2621 * enclosing command.
2623 if (is_partition)
2624 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2627 * We do not allow partitioned tables and partitions to participate in
2628 * regular inheritance.
2630 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2631 ereport(ERROR,
2632 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2633 errmsg("cannot inherit from partitioned table \"%s\"",
2634 RelationGetRelationName(relation))));
2635 if (relation->rd_rel->relispartition && !is_partition)
2636 ereport(ERROR,
2637 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2638 errmsg("cannot inherit from partition \"%s\"",
2639 RelationGetRelationName(relation))));
2641 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2642 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2643 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2644 ereport(ERROR,
2645 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2646 errmsg("inherited relation \"%s\" is not a table or foreign table",
2647 RelationGetRelationName(relation))));
2650 * If the parent is permanent, so must be all of its partitions. Note
2651 * that inheritance allows that case.
2653 if (is_partition &&
2654 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2655 relpersistence == RELPERSISTENCE_TEMP)
2656 ereport(ERROR,
2657 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2658 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2659 RelationGetRelationName(relation))));
2661 /* Permanent rels cannot inherit from temporary ones */
2662 if (relpersistence != RELPERSISTENCE_TEMP &&
2663 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2664 ereport(ERROR,
2665 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2666 errmsg(!is_partition
2667 ? "cannot inherit from temporary relation \"%s\""
2668 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2669 RelationGetRelationName(relation))));
2671 /* If existing rel is temp, it must belong to this session */
2672 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2673 !relation->rd_islocaltemp)
2674 ereport(ERROR,
2675 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2676 errmsg(!is_partition
2677 ? "cannot inherit from temporary relation of another session"
2678 : "cannot create as partition of temporary relation of another session")));
2681 * We should have an UNDER permission flag for this, but for now,
2682 * demand that creator of a child table own the parent.
2684 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2685 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2686 RelationGetRelationName(relation));
2688 tupleDesc = RelationGetDescr(relation);
2689 constr = tupleDesc->constr;
2692 * newattmap->attnums[] will contain the child-table attribute numbers
2693 * for the attributes of this parent table. (They are not the same
2694 * for parents after the first one, nor if we have dropped columns.)
2696 newattmap = make_attrmap(tupleDesc->natts);
2698 /* We can't process inherited defaults until newattmap is complete. */
2699 inherited_defaults = cols_with_defaults = NIL;
2702 * All columns that are part of the parent's primary key need to be
2703 * NOT NULL; if partition just the attnotnull bit, otherwise a full
2704 * constraint (if they don't have one already). Also, we request
2705 * attnotnull on columns that have a not-null constraint that's not
2706 * marked NO INHERIT.
2708 pkattrs = RelationGetIndexAttrBitmap(relation,
2709 INDEX_ATTR_BITMAP_PRIMARY_KEY);
2710 nnconstrs = RelationGetNotNullConstraints(RelationGetRelid(relation), true);
2711 foreach(lc1, nnconstrs)
2712 nncols = bms_add_member(nncols,
2713 ((CookedConstraint *) lfirst(lc1))->attnum);
2715 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2716 parent_attno++)
2718 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2719 parent_attno - 1);
2720 char *attributeName = NameStr(attribute->attname);
2721 int exist_attno;
2722 ColumnDef *newdef;
2723 ColumnDef *mergeddef;
2726 * Ignore dropped columns in the parent.
2728 if (attribute->attisdropped)
2729 continue; /* leave newattmap->attnums entry as zero */
2732 * Create new column definition
2734 newdef = makeColumnDef(attributeName, attribute->atttypid,
2735 attribute->atttypmod, attribute->attcollation);
2736 newdef->storage = attribute->attstorage;
2737 newdef->generated = attribute->attgenerated;
2738 if (CompressionMethodIsValid(attribute->attcompression))
2739 newdef->compression =
2740 pstrdup(GetCompressionMethodName(attribute->attcompression));
2743 * Regular inheritance children are independent enough not to
2744 * inherit identity columns. But partitions are integral part of
2745 * a partitioned table and inherit identity column.
2747 if (is_partition)
2748 newdef->identity = attribute->attidentity;
2751 * Does it match some previously considered column from another
2752 * parent?
2754 exist_attno = findAttrByName(attributeName, inh_columns);
2755 if (exist_attno > 0)
2758 * Yes, try to merge the two column definitions.
2760 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2762 newattmap->attnums[parent_attno - 1] = exist_attno;
2765 * Partitions have only one parent, so conflict should never
2766 * occur.
2768 Assert(!is_partition);
2770 else
2773 * No, create a new inherited column
2775 newdef->inhcount = 1;
2776 newdef->is_local = false;
2777 inh_columns = lappend(inh_columns, newdef);
2779 newattmap->attnums[parent_attno - 1] = ++child_attno;
2781 mergeddef = newdef;
2785 * mark attnotnull if parent has it and it's not NO INHERIT
2787 if (bms_is_member(parent_attno, nncols) ||
2788 bms_is_member(parent_attno - FirstLowInvalidHeapAttributeNumber,
2789 pkattrs))
2790 mergeddef->is_not_null = true;
2793 * In regular inheritance, columns in the parent's primary key get
2794 * an extra not-null constraint. Partitioning doesn't need this,
2795 * because the PK itself is going to be cloned to the partition.
2797 if (!is_partition &&
2798 bms_is_member(parent_attno -
2799 FirstLowInvalidHeapAttributeNumber,
2800 pkattrs))
2802 CookedConstraint *nn;
2804 nn = palloc(sizeof(CookedConstraint));
2805 nn->contype = CONSTR_NOTNULL;
2806 nn->conoid = InvalidOid;
2807 nn->name = NULL;
2808 nn->attnum = newattmap->attnums[parent_attno - 1];
2809 nn->expr = NULL;
2810 nn->skip_validation = false;
2811 nn->is_local = false;
2812 nn->inhcount = 1;
2813 nn->is_no_inherit = false;
2815 nnconstraints = lappend(nnconstraints, nn);
2819 * Locate default/generation expression if any
2821 if (attribute->atthasdef)
2823 Node *this_default;
2825 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2826 if (this_default == NULL)
2827 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2828 parent_attno, RelationGetRelationName(relation));
2831 * If it's a GENERATED default, it might contain Vars that
2832 * need to be mapped to the inherited column(s)' new numbers.
2833 * We can't do that till newattmap is ready, so just remember
2834 * all the inherited default expressions for the moment.
2836 inherited_defaults = lappend(inherited_defaults, this_default);
2837 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2842 * Now process any inherited default expressions, adjusting attnos
2843 * using the completed newattmap map.
2845 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2847 Node *this_default = (Node *) lfirst(lc1);
2848 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2849 bool found_whole_row;
2851 /* Adjust Vars to match new table's column numbering */
2852 this_default = map_variable_attnos(this_default,
2853 1, 0,
2854 newattmap,
2855 InvalidOid, &found_whole_row);
2858 * For the moment we have to reject whole-row variables. We could
2859 * convert them, if we knew the new table's rowtype OID, but that
2860 * hasn't been assigned yet. (A variable could only appear in a
2861 * generation expression, so the error message is correct.)
2863 if (found_whole_row)
2864 ereport(ERROR,
2865 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2866 errmsg("cannot convert whole-row table reference"),
2867 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2868 def->colname,
2869 RelationGetRelationName(relation))));
2872 * If we already had a default from some prior parent, check to
2873 * see if they are the same. If so, no problem; if not, mark the
2874 * column as having a bogus default. Below, we will complain if
2875 * the bogus default isn't overridden by the child columns.
2877 Assert(def->raw_default == NULL);
2878 if (def->cooked_default == NULL)
2879 def->cooked_default = this_default;
2880 else if (!equal(def->cooked_default, this_default))
2882 def->cooked_default = &bogus_marker;
2883 have_bogus_defaults = true;
2888 * Now copy the CHECK constraints of this parent, adjusting attnos
2889 * using the completed newattmap map. Identically named constraints
2890 * are merged if possible, else we throw error.
2892 if (constr && constr->num_check > 0)
2894 ConstrCheck *check = constr->check;
2896 for (int i = 0; i < constr->num_check; i++)
2898 char *name = check[i].ccname;
2899 Node *expr;
2900 bool found_whole_row;
2902 /* ignore if the constraint is non-inheritable */
2903 if (check[i].ccnoinherit)
2904 continue;
2906 /* Adjust Vars to match new table's column numbering */
2907 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2908 1, 0,
2909 newattmap,
2910 InvalidOid, &found_whole_row);
2913 * For the moment we have to reject whole-row variables. We
2914 * could convert them, if we knew the new table's rowtype OID,
2915 * but that hasn't been assigned yet.
2917 if (found_whole_row)
2918 ereport(ERROR,
2919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2920 errmsg("cannot convert whole-row table reference"),
2921 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2922 name,
2923 RelationGetRelationName(relation))));
2925 constraints = MergeCheckConstraint(constraints, name, expr);
2930 * Also copy the not-null constraints from this parent. The
2931 * attnotnull markings were already installed above.
2933 foreach(lc1, nnconstrs)
2935 CookedConstraint *nn = lfirst(lc1);
2937 Assert(nn->contype == CONSTR_NOTNULL);
2939 nn->attnum = newattmap->attnums[nn->attnum - 1];
2940 nn->is_local = false;
2941 nn->inhcount = 1;
2943 nnconstraints = lappend(nnconstraints, nn);
2946 free_attrmap(newattmap);
2949 * Close the parent rel, but keep our lock on it until xact commit.
2950 * That will prevent someone else from deleting or ALTERing the parent
2951 * before the child is committed.
2953 table_close(relation, NoLock);
2957 * If we had no inherited attributes, the result columns are just the
2958 * explicitly declared columns. Otherwise, we need to merge the declared
2959 * columns into the inherited column list. Although, we never have any
2960 * explicitly declared columns if the table is a partition.
2962 if (inh_columns != NIL)
2964 int newcol_attno = 0;
2966 foreach(lc, columns)
2968 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2969 char *attributeName = newdef->colname;
2970 int exist_attno;
2973 * Partitions have only one parent and have no column definitions
2974 * of their own, so conflict should never occur.
2976 Assert(!is_partition);
2978 newcol_attno++;
2981 * Does it match some inherited column?
2983 exist_attno = findAttrByName(attributeName, inh_columns);
2984 if (exist_attno > 0)
2987 * Yes, try to merge the two column definitions.
2989 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2991 else
2994 * No, attach new column unchanged to result columns.
2996 inh_columns = lappend(inh_columns, newdef);
3000 columns = inh_columns;
3003 * Check that we haven't exceeded the legal # of columns after merging
3004 * in inherited columns.
3006 if (list_length(columns) > MaxHeapAttributeNumber)
3007 ereport(ERROR,
3008 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3009 errmsg("tables can have at most %d columns",
3010 MaxHeapAttributeNumber)));
3014 * Now that we have the column definition list for a partition, we can
3015 * check whether the columns referenced in the column constraint specs
3016 * actually exist. Also, merge column defaults.
3018 if (is_partition)
3020 foreach(lc, saved_columns)
3022 ColumnDef *restdef = lfirst(lc);
3023 bool found = false;
3024 ListCell *l;
3026 foreach(l, columns)
3028 ColumnDef *coldef = lfirst(l);
3030 if (strcmp(coldef->colname, restdef->colname) == 0)
3032 found = true;
3035 * Check for conflicts related to generated columns.
3037 * Same rules as above: generated-ness has to match the
3038 * parent, but the contents of the generation expression
3039 * can be different.
3041 if (coldef->generated)
3043 if (restdef->raw_default && !restdef->generated)
3044 ereport(ERROR,
3045 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3046 errmsg("column \"%s\" inherits from generated column but specifies default",
3047 restdef->colname)));
3048 if (restdef->identity)
3049 ereport(ERROR,
3050 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3051 errmsg("column \"%s\" inherits from generated column but specifies identity",
3052 restdef->colname)));
3054 else
3056 if (restdef->generated)
3057 ereport(ERROR,
3058 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3059 errmsg("child column \"%s\" specifies generation expression",
3060 restdef->colname),
3061 errhint("A child table column cannot be generated unless its parent column is.")));
3065 * Override the parent's default value for this column
3066 * (coldef->cooked_default) with the partition's local
3067 * definition (restdef->raw_default), if there's one. It
3068 * should be physically impossible to get a cooked default
3069 * in the local definition or a raw default in the
3070 * inherited definition, but make sure they're nulls, for
3071 * future-proofing.
3073 Assert(restdef->cooked_default == NULL);
3074 Assert(coldef->raw_default == NULL);
3075 if (restdef->raw_default)
3077 coldef->raw_default = restdef->raw_default;
3078 coldef->cooked_default = NULL;
3083 /* complain for constraints on columns not in parent */
3084 if (!found)
3085 ereport(ERROR,
3086 (errcode(ERRCODE_UNDEFINED_COLUMN),
3087 errmsg("column \"%s\" does not exist",
3088 restdef->colname)));
3093 * If we found any conflicting parent default values, check to make sure
3094 * they were overridden by the child.
3096 if (have_bogus_defaults)
3098 foreach(lc, columns)
3100 ColumnDef *def = lfirst(lc);
3102 if (def->cooked_default == &bogus_marker)
3104 if (def->generated)
3105 ereport(ERROR,
3106 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3107 errmsg("column \"%s\" inherits conflicting generation expressions",
3108 def->colname),
3109 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3110 else
3111 ereport(ERROR,
3112 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3113 errmsg("column \"%s\" inherits conflicting default values",
3114 def->colname),
3115 errhint("To resolve the conflict, specify a default explicitly.")));
3120 *supconstr = constraints;
3121 *supnotnulls = nnconstraints;
3123 return columns;
3128 * MergeCheckConstraint
3129 * Try to merge an inherited CHECK constraint with previous ones
3131 * If we inherit identically-named constraints from multiple parents, we must
3132 * merge them, or throw an error if they don't have identical definitions.
3134 * constraints is a list of CookedConstraint structs for previous constraints.
3136 * If the new constraint matches an existing one, then the existing
3137 * constraint's inheritance count is updated. If there is a conflict (same
3138 * name but different expression), throw an error. If the constraint neither
3139 * matches nor conflicts with an existing one, a new constraint is appended to
3140 * the list.
3142 static List *
3143 MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3145 ListCell *lc;
3146 CookedConstraint *newcon;
3148 foreach(lc, constraints)
3150 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3152 Assert(ccon->contype == CONSTR_CHECK);
3154 /* Non-matching names never conflict */
3155 if (strcmp(ccon->name, name) != 0)
3156 continue;
3158 if (equal(expr, ccon->expr))
3160 /* OK to merge constraint with existing */
3161 ccon->inhcount++;
3162 if (ccon->inhcount < 0)
3163 ereport(ERROR,
3164 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3165 errmsg("too many inheritance parents"));
3166 return constraints;
3169 ereport(ERROR,
3170 (errcode(ERRCODE_DUPLICATE_OBJECT),
3171 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3172 name)));
3176 * Constraint couldn't be merged with an existing one and also didn't
3177 * conflict with an existing one, so add it as a new one to the list.
3179 newcon = palloc0_object(CookedConstraint);
3180 newcon->contype = CONSTR_CHECK;
3181 newcon->name = pstrdup(name);
3182 newcon->expr = expr;
3183 newcon->inhcount = 1;
3184 return lappend(constraints, newcon);
3188 * MergeChildAttribute
3189 * Merge given child attribute definition into given inherited attribute.
3191 * Input arguments:
3192 * 'inh_columns' is the list of inherited ColumnDefs.
3193 * 'exist_attno' is the number of the inherited attribute in inh_columns
3194 * 'newcol_attno' is the attribute number in child table's schema definition
3195 * 'newdef' is the column/attribute definition from the child table.
3197 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3198 * ColumnDef remains unchanged.
3200 * Notes:
3201 * - The attribute is merged according to the rules laid out in the prologue
3202 * of MergeAttributes().
3203 * - If matching inherited attribute exists but the child attribute can not be
3204 * merged into it, the function throws respective errors.
3205 * - A partition can not have its own column definitions. Hence this function
3206 * is applicable only to a regular inheritance child.
3208 static void
3209 MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3211 char *attributeName = newdef->colname;
3212 ColumnDef *inhdef;
3213 Oid inhtypeid,
3214 newtypeid;
3215 int32 inhtypmod,
3216 newtypmod;
3217 Oid inhcollid,
3218 newcollid;
3220 if (exist_attno == newcol_attno)
3221 ereport(NOTICE,
3222 (errmsg("merging column \"%s\" with inherited definition",
3223 attributeName)));
3224 else
3225 ereport(NOTICE,
3226 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3227 errdetail("User-specified column moved to the position of the inherited column.")));
3229 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3232 * Must have the same type and typmod
3234 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3235 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3236 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3237 ereport(ERROR,
3238 (errcode(ERRCODE_DATATYPE_MISMATCH),
3239 errmsg("column \"%s\" has a type conflict",
3240 attributeName),
3241 errdetail("%s versus %s",
3242 format_type_with_typemod(inhtypeid, inhtypmod),
3243 format_type_with_typemod(newtypeid, newtypmod))));
3246 * Must have the same collation
3248 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3249 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3250 if (inhcollid != newcollid)
3251 ereport(ERROR,
3252 (errcode(ERRCODE_COLLATION_MISMATCH),
3253 errmsg("column \"%s\" has a collation conflict",
3254 attributeName),
3255 errdetail("\"%s\" versus \"%s\"",
3256 get_collation_name(inhcollid),
3257 get_collation_name(newcollid))));
3260 * Identity is never inherited by a regular inheritance child. Pick
3261 * child's identity definition if there's one.
3263 inhdef->identity = newdef->identity;
3266 * Copy storage parameter
3268 if (inhdef->storage == 0)
3269 inhdef->storage = newdef->storage;
3270 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3271 ereport(ERROR,
3272 (errcode(ERRCODE_DATATYPE_MISMATCH),
3273 errmsg("column \"%s\" has a storage parameter conflict",
3274 attributeName),
3275 errdetail("%s versus %s",
3276 storage_name(inhdef->storage),
3277 storage_name(newdef->storage))));
3280 * Copy compression parameter
3282 if (inhdef->compression == NULL)
3283 inhdef->compression = newdef->compression;
3284 else if (newdef->compression != NULL)
3286 if (strcmp(inhdef->compression, newdef->compression) != 0)
3287 ereport(ERROR,
3288 (errcode(ERRCODE_DATATYPE_MISMATCH),
3289 errmsg("column \"%s\" has a compression method conflict",
3290 attributeName),
3291 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3295 * Merge of not-null constraints = OR 'em together
3297 inhdef->is_not_null |= newdef->is_not_null;
3300 * Check for conflicts related to generated columns.
3302 * If the parent column is generated, the child column will be made a
3303 * generated column if it isn't already. If it is a generated column,
3304 * we'll take its generation expression in preference to the parent's. We
3305 * must check that the child column doesn't specify a default value or
3306 * identity, which matches the rules for a single column in
3307 * parse_utilcmd.c.
3309 * Conversely, if the parent column is not generated, the child column
3310 * can't be either. (We used to allow that, but it results in being able
3311 * to override the generation expression via UPDATEs through the parent.)
3313 if (inhdef->generated)
3315 if (newdef->raw_default && !newdef->generated)
3316 ereport(ERROR,
3317 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3318 errmsg("column \"%s\" inherits from generated column but specifies default",
3319 inhdef->colname)));
3320 if (newdef->identity)
3321 ereport(ERROR,
3322 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3323 errmsg("column \"%s\" inherits from generated column but specifies identity",
3324 inhdef->colname)));
3326 else
3328 if (newdef->generated)
3329 ereport(ERROR,
3330 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3331 errmsg("child column \"%s\" specifies generation expression",
3332 inhdef->colname),
3333 errhint("A child table column cannot be generated unless its parent column is.")));
3337 * If new def has a default, override previous default
3339 if (newdef->raw_default != NULL)
3341 inhdef->raw_default = newdef->raw_default;
3342 inhdef->cooked_default = newdef->cooked_default;
3345 /* Mark the column as locally defined */
3346 inhdef->is_local = true;
3350 * MergeInheritedAttribute
3351 * Merge given parent attribute definition into specified attribute
3352 * inherited from the previous parents.
3354 * Input arguments:
3355 * 'inh_columns' is the list of previously inherited ColumnDefs.
3356 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3357 * 'newdef' is the new parent column/attribute definition to be merged.
3359 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3361 * Notes:
3362 * - The attribute is merged according to the rules laid out in the prologue
3363 * of MergeAttributes().
3364 * - If matching inherited attribute exists but the new attribute can not be
3365 * merged into it, the function throws respective errors.
3366 * - A partition inherits from only a single parent. Hence this function is
3367 * applicable only to a regular inheritance.
3369 static ColumnDef *
3370 MergeInheritedAttribute(List *inh_columns,
3371 int exist_attno,
3372 const ColumnDef *newdef)
3374 char *attributeName = newdef->colname;
3375 ColumnDef *prevdef;
3376 Oid prevtypeid,
3377 newtypeid;
3378 int32 prevtypmod,
3379 newtypmod;
3380 Oid prevcollid,
3381 newcollid;
3383 ereport(NOTICE,
3384 (errmsg("merging multiple inherited definitions of column \"%s\"",
3385 attributeName)));
3386 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3389 * Must have the same type and typmod
3391 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3392 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3393 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3394 ereport(ERROR,
3395 (errcode(ERRCODE_DATATYPE_MISMATCH),
3396 errmsg("inherited column \"%s\" has a type conflict",
3397 attributeName),
3398 errdetail("%s versus %s",
3399 format_type_with_typemod(prevtypeid, prevtypmod),
3400 format_type_with_typemod(newtypeid, newtypmod))));
3403 * Must have the same collation
3405 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3406 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3407 if (prevcollid != newcollid)
3408 ereport(ERROR,
3409 (errcode(ERRCODE_COLLATION_MISMATCH),
3410 errmsg("inherited column \"%s\" has a collation conflict",
3411 attributeName),
3412 errdetail("\"%s\" versus \"%s\"",
3413 get_collation_name(prevcollid),
3414 get_collation_name(newcollid))));
3417 * Copy/check storage parameter
3419 if (prevdef->storage == 0)
3420 prevdef->storage = newdef->storage;
3421 else if (prevdef->storage != newdef->storage)
3422 ereport(ERROR,
3423 (errcode(ERRCODE_DATATYPE_MISMATCH),
3424 errmsg("inherited column \"%s\" has a storage parameter conflict",
3425 attributeName),
3426 errdetail("%s versus %s",
3427 storage_name(prevdef->storage),
3428 storage_name(newdef->storage))));
3431 * Copy/check compression parameter
3433 if (prevdef->compression == NULL)
3434 prevdef->compression = newdef->compression;
3435 else if (newdef->compression != NULL)
3437 if (strcmp(prevdef->compression, newdef->compression) != 0)
3438 ereport(ERROR,
3439 (errcode(ERRCODE_DATATYPE_MISMATCH),
3440 errmsg("column \"%s\" has a compression method conflict",
3441 attributeName),
3442 errdetail("%s versus %s",
3443 prevdef->compression, newdef->compression)));
3447 * Check for GENERATED conflicts
3449 if (prevdef->generated != newdef->generated)
3450 ereport(ERROR,
3451 (errcode(ERRCODE_DATATYPE_MISMATCH),
3452 errmsg("inherited column \"%s\" has a generation conflict",
3453 attributeName)));
3456 * Default and other constraints are handled by the caller.
3459 prevdef->inhcount++;
3460 if (prevdef->inhcount < 0)
3461 ereport(ERROR,
3462 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3463 errmsg("too many inheritance parents"));
3465 return prevdef;
3469 * StoreCatalogInheritance
3470 * Updates the system catalogs with proper inheritance information.
3472 * supers is a list of the OIDs of the new relation's direct ancestors.
3474 static void
3475 StoreCatalogInheritance(Oid relationId, List *supers,
3476 bool child_is_partition)
3478 Relation relation;
3479 int32 seqNumber;
3480 ListCell *entry;
3483 * sanity checks
3485 Assert(OidIsValid(relationId));
3487 if (supers == NIL)
3488 return;
3491 * Store INHERITS information in pg_inherits using direct ancestors only.
3492 * Also enter dependencies on the direct ancestors, and make sure they are
3493 * marked with relhassubclass = true.
3495 * (Once upon a time, both direct and indirect ancestors were found here
3496 * and then entered into pg_ipl. Since that catalog doesn't exist
3497 * anymore, there's no need to look for indirect ancestors.)
3499 relation = table_open(InheritsRelationId, RowExclusiveLock);
3501 seqNumber = 1;
3502 foreach(entry, supers)
3504 Oid parentOid = lfirst_oid(entry);
3506 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3507 child_is_partition);
3508 seqNumber++;
3511 table_close(relation, RowExclusiveLock);
3515 * Make catalog entries showing relationId as being an inheritance child
3516 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3518 static void
3519 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3520 int32 seqNumber, Relation inhRelation,
3521 bool child_is_partition)
3523 ObjectAddress childobject,
3524 parentobject;
3526 /* store the pg_inherits row */
3527 StoreSingleInheritance(relationId, parentOid, seqNumber);
3530 * Store a dependency too
3532 parentobject.classId = RelationRelationId;
3533 parentobject.objectId = parentOid;
3534 parentobject.objectSubId = 0;
3535 childobject.classId = RelationRelationId;
3536 childobject.objectId = relationId;
3537 childobject.objectSubId = 0;
3539 recordDependencyOn(&childobject, &parentobject,
3540 child_dependency_type(child_is_partition));
3543 * Post creation hook of this inheritance. Since object_access_hook
3544 * doesn't take multiple object identifiers, we relay oid of parent
3545 * relation using auxiliary_id argument.
3547 InvokeObjectPostAlterHookArg(InheritsRelationId,
3548 relationId, 0,
3549 parentOid, false);
3552 * Mark the parent as having subclasses.
3554 SetRelationHasSubclass(parentOid, true);
3558 * Look for an existing column entry with the given name.
3560 * Returns the index (starting with 1) if attribute already exists in columns,
3561 * 0 if it doesn't.
3563 static int
3564 findAttrByName(const char *attributeName, const List *columns)
3566 ListCell *lc;
3567 int i = 1;
3569 foreach(lc, columns)
3571 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3572 return i;
3574 i++;
3576 return 0;
3581 * SetRelationHasSubclass
3582 * Set the value of the relation's relhassubclass field in pg_class.
3584 * NOTE: caller must be holding an appropriate lock on the relation.
3585 * ShareUpdateExclusiveLock is sufficient.
3587 * NOTE: an important side-effect of this operation is that an SI invalidation
3588 * message is sent out to all backends --- including me --- causing plans
3589 * referencing the relation to be rebuilt with the new list of children.
3590 * This must happen even if we find that no change is needed in the pg_class
3591 * row.
3593 void
3594 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3596 Relation relationRelation;
3597 HeapTuple tuple;
3598 Form_pg_class classtuple;
3601 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3603 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3604 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3605 if (!HeapTupleIsValid(tuple))
3606 elog(ERROR, "cache lookup failed for relation %u", relationId);
3607 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3609 if (classtuple->relhassubclass != relhassubclass)
3611 classtuple->relhassubclass = relhassubclass;
3612 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3614 else
3616 /* no need to change tuple, but force relcache rebuild anyway */
3617 CacheInvalidateRelcacheByTuple(tuple);
3620 heap_freetuple(tuple);
3621 table_close(relationRelation, RowExclusiveLock);
3625 * CheckRelationTableSpaceMove
3626 * Check if relation can be moved to new tablespace.
3628 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3630 * Returns true if the relation can be moved to the new tablespace; raises
3631 * an error if it is not possible to do the move; returns false if the move
3632 * would have no effect.
3634 bool
3635 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3637 Oid oldTableSpaceId;
3640 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3641 * stored as 0.
3643 oldTableSpaceId = rel->rd_rel->reltablespace;
3644 if (newTableSpaceId == oldTableSpaceId ||
3645 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3646 return false;
3649 * We cannot support moving mapped relations into different tablespaces.
3650 * (In particular this eliminates all shared catalogs.)
3652 if (RelationIsMapped(rel))
3653 ereport(ERROR,
3654 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3655 errmsg("cannot move system relation \"%s\"",
3656 RelationGetRelationName(rel))));
3658 /* Cannot move a non-shared relation into pg_global */
3659 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3660 ereport(ERROR,
3661 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3662 errmsg("only shared relations can be placed in pg_global tablespace")));
3665 * Do not allow moving temp tables of other backends ... their local
3666 * buffer manager is not going to cope.
3668 if (RELATION_IS_OTHER_TEMP(rel))
3669 ereport(ERROR,
3670 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3671 errmsg("cannot move temporary tables of other sessions")));
3673 return true;
3677 * SetRelationTableSpace
3678 * Set new reltablespace and relfilenumber in pg_class entry.
3680 * newTableSpaceId is the new tablespace for the relation, and
3681 * newRelFilenumber its new filenumber. If newRelFilenumber is
3682 * InvalidRelFileNumber, this field is not updated.
3684 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3686 * The caller of this routine had better check if a relation can be
3687 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3688 * first, and is responsible for making the change visible with
3689 * CommandCounterIncrement().
3691 void
3692 SetRelationTableSpace(Relation rel,
3693 Oid newTableSpaceId,
3694 RelFileNumber newRelFilenumber)
3696 Relation pg_class;
3697 HeapTuple tuple;
3698 Form_pg_class rd_rel;
3699 Oid reloid = RelationGetRelid(rel);
3701 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3703 /* Get a modifiable copy of the relation's pg_class row. */
3704 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3706 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3707 if (!HeapTupleIsValid(tuple))
3708 elog(ERROR, "cache lookup failed for relation %u", reloid);
3709 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3711 /* Update the pg_class row. */
3712 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3713 InvalidOid : newTableSpaceId;
3714 if (RelFileNumberIsValid(newRelFilenumber))
3715 rd_rel->relfilenode = newRelFilenumber;
3716 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3719 * Record dependency on tablespace. This is only required for relations
3720 * that have no physical storage.
3722 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3723 changeDependencyOnTablespace(RelationRelationId, reloid,
3724 rd_rel->reltablespace);
3726 heap_freetuple(tuple);
3727 table_close(pg_class, RowExclusiveLock);
3731 * renameatt_check - basic sanity checks before attribute rename
3733 static void
3734 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3736 char relkind = classform->relkind;
3738 if (classform->reloftype && !recursing)
3739 ereport(ERROR,
3740 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3741 errmsg("cannot rename column of typed table")));
3744 * Renaming the columns of sequences or toast tables doesn't actually
3745 * break anything from the system's point of view, since internal
3746 * references are by attnum. But it doesn't seem right to allow users to
3747 * change names that are hardcoded into the system, hence the following
3748 * restriction.
3750 if (relkind != RELKIND_RELATION &&
3751 relkind != RELKIND_VIEW &&
3752 relkind != RELKIND_MATVIEW &&
3753 relkind != RELKIND_COMPOSITE_TYPE &&
3754 relkind != RELKIND_INDEX &&
3755 relkind != RELKIND_PARTITIONED_INDEX &&
3756 relkind != RELKIND_FOREIGN_TABLE &&
3757 relkind != RELKIND_PARTITIONED_TABLE)
3758 ereport(ERROR,
3759 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3760 errmsg("cannot rename columns of relation \"%s\"",
3761 NameStr(classform->relname)),
3762 errdetail_relkind_not_supported(relkind)));
3765 * permissions checking. only the owner of a class can change its schema.
3767 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3768 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3769 NameStr(classform->relname));
3770 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3771 ereport(ERROR,
3772 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3773 errmsg("permission denied: \"%s\" is a system catalog",
3774 NameStr(classform->relname))));
3778 * renameatt_internal - workhorse for renameatt
3780 * Return value is the attribute number in the 'myrelid' relation.
3782 static AttrNumber
3783 renameatt_internal(Oid myrelid,
3784 const char *oldattname,
3785 const char *newattname,
3786 bool recurse,
3787 bool recursing,
3788 int expected_parents,
3789 DropBehavior behavior)
3791 Relation targetrelation;
3792 Relation attrelation;
3793 HeapTuple atttup;
3794 Form_pg_attribute attform;
3795 AttrNumber attnum;
3798 * Grab an exclusive lock on the target table, which we will NOT release
3799 * until end of transaction.
3801 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3802 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3805 * if the 'recurse' flag is set then we are supposed to rename this
3806 * attribute in all classes that inherit from 'relname' (as well as in
3807 * 'relname').
3809 * any permissions or problems with duplicate attributes will cause the
3810 * whole transaction to abort, which is what we want -- all or nothing.
3812 if (recurse)
3814 List *child_oids,
3815 *child_numparents;
3816 ListCell *lo,
3817 *li;
3820 * we need the number of parents for each child so that the recursive
3821 * calls to renameatt() can determine whether there are any parents
3822 * outside the inheritance hierarchy being processed.
3824 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3825 &child_numparents);
3828 * find_all_inheritors does the recursive search of the inheritance
3829 * hierarchy, so all we have to do is process all of the relids in the
3830 * list that it returns.
3832 forboth(lo, child_oids, li, child_numparents)
3834 Oid childrelid = lfirst_oid(lo);
3835 int numparents = lfirst_int(li);
3837 if (childrelid == myrelid)
3838 continue;
3839 /* note we need not recurse again */
3840 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3843 else
3846 * If we are told not to recurse, there had better not be any child
3847 * tables; else the rename would put them out of step.
3849 * expected_parents will only be 0 if we are not already recursing.
3851 if (expected_parents == 0 &&
3852 find_inheritance_children(myrelid, NoLock) != NIL)
3853 ereport(ERROR,
3854 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3855 errmsg("inherited column \"%s\" must be renamed in child tables too",
3856 oldattname)));
3859 /* rename attributes in typed tables of composite type */
3860 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3862 List *child_oids;
3863 ListCell *lo;
3865 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3866 RelationGetRelationName(targetrelation),
3867 behavior);
3869 foreach(lo, child_oids)
3870 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3873 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3875 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3876 if (!HeapTupleIsValid(atttup))
3877 ereport(ERROR,
3878 (errcode(ERRCODE_UNDEFINED_COLUMN),
3879 errmsg("column \"%s\" does not exist",
3880 oldattname)));
3881 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3883 attnum = attform->attnum;
3884 if (attnum <= 0)
3885 ereport(ERROR,
3886 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3887 errmsg("cannot rename system column \"%s\"",
3888 oldattname)));
3891 * if the attribute is inherited, forbid the renaming. if this is a
3892 * top-level call to renameatt(), then expected_parents will be 0, so the
3893 * effect of this code will be to prohibit the renaming if the attribute
3894 * is inherited at all. if this is a recursive call to renameatt(),
3895 * expected_parents will be the number of parents the current relation has
3896 * within the inheritance hierarchy being processed, so we'll prohibit the
3897 * renaming only if there are additional parents from elsewhere.
3899 if (attform->attinhcount > expected_parents)
3900 ereport(ERROR,
3901 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3902 errmsg("cannot rename inherited column \"%s\"",
3903 oldattname)));
3905 /* new name should not already exist */
3906 (void) check_for_column_name_collision(targetrelation, newattname, false);
3908 /* apply the update */
3909 namestrcpy(&(attform->attname), newattname);
3911 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3913 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3915 heap_freetuple(atttup);
3917 table_close(attrelation, RowExclusiveLock);
3919 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3921 return attnum;
3925 * Perform permissions and integrity checks before acquiring a relation lock.
3927 static void
3928 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3929 void *arg)
3931 HeapTuple tuple;
3932 Form_pg_class form;
3934 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3935 if (!HeapTupleIsValid(tuple))
3936 return; /* concurrently dropped */
3937 form = (Form_pg_class) GETSTRUCT(tuple);
3938 renameatt_check(relid, form, false);
3939 ReleaseSysCache(tuple);
3943 * renameatt - changes the name of an attribute in a relation
3945 * The returned ObjectAddress is that of the renamed column.
3947 ObjectAddress
3948 renameatt(RenameStmt *stmt)
3950 Oid relid;
3951 AttrNumber attnum;
3952 ObjectAddress address;
3954 /* lock level taken here should match renameatt_internal */
3955 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3956 stmt->missing_ok ? RVR_MISSING_OK : 0,
3957 RangeVarCallbackForRenameAttribute,
3958 NULL);
3960 if (!OidIsValid(relid))
3962 ereport(NOTICE,
3963 (errmsg("relation \"%s\" does not exist, skipping",
3964 stmt->relation->relname)));
3965 return InvalidObjectAddress;
3968 attnum =
3969 renameatt_internal(relid,
3970 stmt->subname, /* old att name */
3971 stmt->newname, /* new att name */
3972 stmt->relation->inh, /* recursive? */
3973 false, /* recursing? */
3974 0, /* expected inhcount */
3975 stmt->behavior);
3977 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3979 return address;
3983 * same logic as renameatt_internal
3985 static ObjectAddress
3986 rename_constraint_internal(Oid myrelid,
3987 Oid mytypid,
3988 const char *oldconname,
3989 const char *newconname,
3990 bool recurse,
3991 bool recursing,
3992 int expected_parents)
3994 Relation targetrelation = NULL;
3995 Oid constraintOid;
3996 HeapTuple tuple;
3997 Form_pg_constraint con;
3998 ObjectAddress address;
4000 Assert(!myrelid || !mytypid);
4002 if (mytypid)
4004 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
4006 else
4008 targetrelation = relation_open(myrelid, AccessExclusiveLock);
4011 * don't tell it whether we're recursing; we allow changing typed
4012 * tables here
4014 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
4016 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
4019 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
4020 if (!HeapTupleIsValid(tuple))
4021 elog(ERROR, "cache lookup failed for constraint %u",
4022 constraintOid);
4023 con = (Form_pg_constraint) GETSTRUCT(tuple);
4025 if (myrelid &&
4026 (con->contype == CONSTRAINT_CHECK ||
4027 con->contype == CONSTRAINT_NOTNULL) &&
4028 !con->connoinherit)
4030 if (recurse)
4032 List *child_oids,
4033 *child_numparents;
4034 ListCell *lo,
4035 *li;
4037 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
4038 &child_numparents);
4040 forboth(lo, child_oids, li, child_numparents)
4042 Oid childrelid = lfirst_oid(lo);
4043 int numparents = lfirst_int(li);
4045 if (childrelid == myrelid)
4046 continue;
4048 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
4051 else
4053 if (expected_parents == 0 &&
4054 find_inheritance_children(myrelid, NoLock) != NIL)
4055 ereport(ERROR,
4056 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4057 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4058 oldconname)));
4061 if (con->coninhcount > expected_parents)
4062 ereport(ERROR,
4063 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
4064 errmsg("cannot rename inherited constraint \"%s\"",
4065 oldconname)));
4068 if (con->conindid
4069 && (con->contype == CONSTRAINT_PRIMARY
4070 || con->contype == CONSTRAINT_UNIQUE
4071 || con->contype == CONSTRAINT_EXCLUSION))
4072 /* rename the index; this renames the constraint as well */
4073 RenameRelationInternal(con->conindid, newconname, false, true);
4074 else
4075 RenameConstraintById(constraintOid, newconname);
4077 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
4079 ReleaseSysCache(tuple);
4081 if (targetrelation)
4084 * Invalidate relcache so as others can see the new constraint name.
4086 CacheInvalidateRelcache(targetrelation);
4088 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4091 return address;
4094 ObjectAddress
4095 RenameConstraint(RenameStmt *stmt)
4097 Oid relid = InvalidOid;
4098 Oid typid = InvalidOid;
4100 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4102 Relation rel;
4103 HeapTuple tup;
4105 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
4106 rel = table_open(TypeRelationId, RowExclusiveLock);
4107 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4108 if (!HeapTupleIsValid(tup))
4109 elog(ERROR, "cache lookup failed for type %u", typid);
4110 checkDomainOwner(tup);
4111 ReleaseSysCache(tup);
4112 table_close(rel, NoLock);
4114 else
4116 /* lock level taken here should match rename_constraint_internal */
4117 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4118 stmt->missing_ok ? RVR_MISSING_OK : 0,
4119 RangeVarCallbackForRenameAttribute,
4120 NULL);
4121 if (!OidIsValid(relid))
4123 ereport(NOTICE,
4124 (errmsg("relation \"%s\" does not exist, skipping",
4125 stmt->relation->relname)));
4126 return InvalidObjectAddress;
4130 return
4131 rename_constraint_internal(relid, typid,
4132 stmt->subname,
4133 stmt->newname,
4134 (stmt->relation &&
4135 stmt->relation->inh), /* recursive? */
4136 false, /* recursing? */
4137 0 /* expected inhcount */ );
4141 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4142 * RENAME
4144 ObjectAddress
4145 RenameRelation(RenameStmt *stmt)
4147 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4148 Oid relid;
4149 ObjectAddress address;
4152 * Grab an exclusive lock on the target table, index, sequence, view,
4153 * materialized view, or foreign table, which we will NOT release until
4154 * end of transaction.
4156 * Lock level used here should match RenameRelationInternal, to avoid lock
4157 * escalation. However, because ALTER INDEX can be used with any relation
4158 * type, we mustn't believe without verification.
4160 for (;;)
4162 LOCKMODE lockmode;
4163 char relkind;
4164 bool obj_is_index;
4166 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4168 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4169 stmt->missing_ok ? RVR_MISSING_OK : 0,
4170 RangeVarCallbackForAlterRelation,
4171 (void *) stmt);
4173 if (!OidIsValid(relid))
4175 ereport(NOTICE,
4176 (errmsg("relation \"%s\" does not exist, skipping",
4177 stmt->relation->relname)));
4178 return InvalidObjectAddress;
4182 * We allow mismatched statement and object types (e.g., ALTER INDEX
4183 * to rename a table), but we might've used the wrong lock level. If
4184 * that happens, retry with the correct lock level. We don't bother
4185 * if we already acquired AccessExclusiveLock with an index, however.
4187 relkind = get_rel_relkind(relid);
4188 obj_is_index = (relkind == RELKIND_INDEX ||
4189 relkind == RELKIND_PARTITIONED_INDEX);
4190 if (obj_is_index || is_index_stmt == obj_is_index)
4191 break;
4193 UnlockRelationOid(relid, lockmode);
4194 is_index_stmt = obj_is_index;
4197 /* Do the work */
4198 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4200 ObjectAddressSet(address, RelationRelationId, relid);
4202 return address;
4206 * RenameRelationInternal - change the name of a relation
4208 void
4209 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4211 Relation targetrelation;
4212 Relation relrelation; /* for RELATION relation */
4213 HeapTuple reltup;
4214 Form_pg_class relform;
4215 Oid namespaceId;
4218 * Grab a lock on the target relation, which we will NOT release until end
4219 * of transaction. We need at least a self-exclusive lock so that
4220 * concurrent DDL doesn't overwrite the rename if they start updating
4221 * while still seeing the old version. The lock also guards against
4222 * triggering relcache reloads in concurrent sessions, which might not
4223 * handle this information changing under them. For indexes, we can use a
4224 * reduced lock level because RelationReloadIndexInfo() handles indexes
4225 * specially.
4227 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4228 namespaceId = RelationGetNamespace(targetrelation);
4231 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4233 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4235 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4236 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4237 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4238 relform = (Form_pg_class) GETSTRUCT(reltup);
4240 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4241 ereport(ERROR,
4242 (errcode(ERRCODE_DUPLICATE_TABLE),
4243 errmsg("relation \"%s\" already exists",
4244 newrelname)));
4247 * RenameRelation is careful not to believe the caller's idea of the
4248 * relation kind being handled. We don't have to worry about this, but
4249 * let's not be totally oblivious to it. We can process an index as
4250 * not-an-index, but not the other way around.
4252 Assert(!is_index ||
4253 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4254 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4257 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4258 * because it's a copy...)
4260 namestrcpy(&(relform->relname), newrelname);
4262 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4264 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4265 InvalidOid, is_internal);
4267 heap_freetuple(reltup);
4268 table_close(relrelation, RowExclusiveLock);
4271 * Also rename the associated type, if any.
4273 if (OidIsValid(targetrelation->rd_rel->reltype))
4274 RenameTypeInternal(targetrelation->rd_rel->reltype,
4275 newrelname, namespaceId);
4278 * Also rename the associated constraint, if any.
4280 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4281 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4283 Oid constraintId = get_index_constraint(myrelid);
4285 if (OidIsValid(constraintId))
4286 RenameConstraintById(constraintId, newrelname);
4290 * Close rel, but keep lock!
4292 relation_close(targetrelation, NoLock);
4296 * ResetRelRewrite - reset relrewrite
4298 void
4299 ResetRelRewrite(Oid myrelid)
4301 Relation relrelation; /* for RELATION relation */
4302 HeapTuple reltup;
4303 Form_pg_class relform;
4306 * Find relation's pg_class tuple.
4308 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4310 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4311 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4312 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4313 relform = (Form_pg_class) GETSTRUCT(reltup);
4316 * Update pg_class tuple.
4318 relform->relrewrite = InvalidOid;
4320 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4322 heap_freetuple(reltup);
4323 table_close(relrelation, RowExclusiveLock);
4327 * Disallow ALTER TABLE (and similar commands) when the current backend has
4328 * any open reference to the target table besides the one just acquired by
4329 * the calling command; this implies there's an open cursor or active plan.
4330 * We need this check because our lock doesn't protect us against stomping
4331 * on our own foot, only other people's feet!
4333 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4334 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4335 * possibly be relaxed to only error out for certain types of alterations.
4336 * But the use-case for allowing any of these things is not obvious, so we
4337 * won't work hard at it for now.
4339 * We also reject these commands if there are any pending AFTER trigger events
4340 * for the rel. This is certainly necessary for the rewriting variants of
4341 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4342 * events would try to fetch the wrong tuples. It might be overly cautious
4343 * in other cases, but again it seems better to err on the side of paranoia.
4345 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4346 * we are worried about active indexscans on the index. The trigger-event
4347 * check can be skipped, since we are doing no damage to the parent table.
4349 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4351 void
4352 CheckTableNotInUse(Relation rel, const char *stmt)
4354 int expected_refcnt;
4356 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4357 if (rel->rd_refcnt != expected_refcnt)
4358 ereport(ERROR,
4359 (errcode(ERRCODE_OBJECT_IN_USE),
4360 /* translator: first %s is a SQL command, eg ALTER TABLE */
4361 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4362 stmt, RelationGetRelationName(rel))));
4364 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4365 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4366 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4367 ereport(ERROR,
4368 (errcode(ERRCODE_OBJECT_IN_USE),
4369 /* translator: first %s is a SQL command, eg ALTER TABLE */
4370 errmsg("cannot %s \"%s\" because it has pending trigger events",
4371 stmt, RelationGetRelationName(rel))));
4375 * AlterTableLookupRelation
4376 * Look up, and lock, the OID for the relation named by an alter table
4377 * statement.
4380 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4382 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4383 stmt->missing_ok ? RVR_MISSING_OK : 0,
4384 RangeVarCallbackForAlterRelation,
4385 (void *) stmt);
4389 * AlterTable
4390 * Execute ALTER TABLE, which can be a list of subcommands
4392 * ALTER TABLE is performed in three phases:
4393 * 1. Examine subcommands and perform pre-transformation checking.
4394 * 2. Validate and transform subcommands, and update system catalogs.
4395 * 3. Scan table(s) to check new constraints, and optionally recopy
4396 * the data into new table(s).
4397 * Phase 3 is not performed unless one or more of the subcommands requires
4398 * it. The intention of this design is to allow multiple independent
4399 * updates of the table schema to be performed with only one pass over the
4400 * data.
4402 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4403 * each table to be affected (there may be multiple affected tables if the
4404 * commands traverse a table inheritance hierarchy). Also we do preliminary
4405 * validation of the subcommands. Because earlier subcommands may change
4406 * the catalog state seen by later commands, there are limits to what can
4407 * be done in this phase. Generally, this phase acquires table locks,
4408 * checks permissions and relkind, and recurses to find child tables.
4410 * ATRewriteCatalogs performs phase 2 for each affected table.
4411 * Certain subcommands need to be performed before others to avoid
4412 * unnecessary conflicts; for example, DROP COLUMN should come before
4413 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4414 * lists, one for each logical "pass" of phase 2.
4416 * ATRewriteTables performs phase 3 for those tables that need it.
4418 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4419 * since phase 1 already does it. However, for certain subcommand types
4420 * it is only possible to determine how to recurse at phase 2 time; for
4421 * those cases, phase 1 sets the cmd->recurse flag.
4423 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4424 * the whole operation; we don't have to do anything special to clean up.
4426 * The caller must lock the relation, with an appropriate lock level
4427 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4428 * or higher. We pass the lock level down
4429 * so that we can apply it recursively to inherited tables. Note that the
4430 * lock level we want as we recurse might well be higher than required for
4431 * that specific subcommand. So we pass down the overall lock requirement,
4432 * rather than reassess it at lower levels.
4434 * The caller also provides a "context" which is to be passed back to
4435 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4436 * Some of the fields therein, such as the relid, are used here as well.
4438 void
4439 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4440 AlterTableUtilityContext *context)
4442 Relation rel;
4444 /* Caller is required to provide an adequate lock. */
4445 rel = relation_open(context->relid, NoLock);
4447 CheckTableNotInUse(rel, "ALTER TABLE");
4449 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4453 * AlterTableInternal
4455 * ALTER TABLE with target specified by OID
4457 * We do not reject if the relation is already open, because it's quite
4458 * likely that one or more layers of caller have it open. That means it
4459 * is unsafe to use this entry point for alterations that could break
4460 * existing query plans. On the assumption it's not used for such, we
4461 * don't have to reject pending AFTER triggers, either.
4463 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4464 * used for any subcommand types that require parse transformation or
4465 * could generate subcommands that have to be passed to ProcessUtility.
4467 void
4468 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4470 Relation rel;
4471 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4473 rel = relation_open(relid, lockmode);
4475 EventTriggerAlterTableRelid(relid);
4477 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4481 * AlterTableGetLockLevel
4483 * Sets the overall lock level required for the supplied list of subcommands.
4484 * Policy for doing this set according to needs of AlterTable(), see
4485 * comments there for overall explanation.
4487 * Function is called before and after parsing, so it must give same
4488 * answer each time it is called. Some subcommands are transformed
4489 * into other subcommand types, so the transform must never be made to a
4490 * lower lock level than previously assigned. All transforms are noted below.
4492 * Since this is called before we lock the table we cannot use table metadata
4493 * to influence the type of lock we acquire.
4495 * There should be no lockmodes hardcoded into the subcommand functions. All
4496 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4497 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4498 * and does not travel through this section of code and cannot be combined with
4499 * any of the subcommands given here.
4501 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4502 * so any changes that might affect SELECTs running on standbys need to use
4503 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4504 * have a solution for that also.
4506 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4507 * that takes a lock less than AccessExclusiveLock can change object definitions
4508 * while pg_dump is running. Be careful to check that the appropriate data is
4509 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4510 * otherwise we might end up with an inconsistent dump that can't restore.
4512 LOCKMODE
4513 AlterTableGetLockLevel(List *cmds)
4516 * This only works if we read catalog tables using MVCC snapshots.
4518 ListCell *lcmd;
4519 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4521 foreach(lcmd, cmds)
4523 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4524 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4526 switch (cmd->subtype)
4529 * These subcommands rewrite the heap, so require full locks.
4531 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4532 * to SELECT */
4533 case AT_SetAccessMethod: /* must rewrite heap */
4534 case AT_SetTableSpace: /* must rewrite heap */
4535 case AT_AlterColumnType: /* must rewrite heap */
4536 cmd_lockmode = AccessExclusiveLock;
4537 break;
4540 * These subcommands may require addition of toast tables. If
4541 * we add a toast table to a table currently being scanned, we
4542 * might miss data added to the new toast table by concurrent
4543 * insert transactions.
4545 case AT_SetStorage: /* may add toast tables, see
4546 * ATRewriteCatalogs() */
4547 cmd_lockmode = AccessExclusiveLock;
4548 break;
4551 * Removing constraints can affect SELECTs that have been
4552 * optimized assuming the constraint holds true. See also
4553 * CloneFkReferenced.
4555 case AT_DropConstraint: /* as DROP INDEX */
4556 case AT_DropNotNull: /* may change some SQL plans */
4557 cmd_lockmode = AccessExclusiveLock;
4558 break;
4561 * Subcommands that may be visible to concurrent SELECTs
4563 case AT_DropColumn: /* change visible to SELECT */
4564 case AT_AddColumnToView: /* CREATE VIEW */
4565 case AT_DropOids: /* used to equiv to DropColumn */
4566 case AT_EnableAlwaysRule: /* may change SELECT rules */
4567 case AT_EnableReplicaRule: /* may change SELECT rules */
4568 case AT_EnableRule: /* may change SELECT rules */
4569 case AT_DisableRule: /* may change SELECT rules */
4570 cmd_lockmode = AccessExclusiveLock;
4571 break;
4574 * Changing owner may remove implicit SELECT privileges
4576 case AT_ChangeOwner: /* change visible to SELECT */
4577 cmd_lockmode = AccessExclusiveLock;
4578 break;
4581 * Changing foreign table options may affect optimization.
4583 case AT_GenericOptions:
4584 case AT_AlterColumnGenericOptions:
4585 cmd_lockmode = AccessExclusiveLock;
4586 break;
4589 * These subcommands affect write operations only.
4591 case AT_EnableTrig:
4592 case AT_EnableAlwaysTrig:
4593 case AT_EnableReplicaTrig:
4594 case AT_EnableTrigAll:
4595 case AT_EnableTrigUser:
4596 case AT_DisableTrig:
4597 case AT_DisableTrigAll:
4598 case AT_DisableTrigUser:
4599 cmd_lockmode = ShareRowExclusiveLock;
4600 break;
4603 * These subcommands affect write operations only. XXX
4604 * Theoretically, these could be ShareRowExclusiveLock.
4606 case AT_ColumnDefault:
4607 case AT_CookedColumnDefault:
4608 case AT_AlterConstraint:
4609 case AT_AddIndex: /* from ADD CONSTRAINT */
4610 case AT_AddIndexConstraint:
4611 case AT_ReplicaIdentity:
4612 case AT_SetNotNull:
4613 case AT_SetAttNotNull:
4614 case AT_EnableRowSecurity:
4615 case AT_DisableRowSecurity:
4616 case AT_ForceRowSecurity:
4617 case AT_NoForceRowSecurity:
4618 case AT_AddIdentity:
4619 case AT_DropIdentity:
4620 case AT_SetIdentity:
4621 case AT_SetExpression:
4622 case AT_DropExpression:
4623 case AT_SetCompression:
4624 cmd_lockmode = AccessExclusiveLock;
4625 break;
4627 case AT_AddConstraint:
4628 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4629 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4630 if (IsA(cmd->def, Constraint))
4632 Constraint *con = (Constraint *) cmd->def;
4634 switch (con->contype)
4636 case CONSTR_EXCLUSION:
4637 case CONSTR_PRIMARY:
4638 case CONSTR_UNIQUE:
4641 * Cases essentially the same as CREATE INDEX. We
4642 * could reduce the lock strength to ShareLock if
4643 * we can work out how to allow concurrent catalog
4644 * updates. XXX Might be set down to
4645 * ShareRowExclusiveLock but requires further
4646 * analysis.
4648 cmd_lockmode = AccessExclusiveLock;
4649 break;
4650 case CONSTR_FOREIGN:
4653 * We add triggers to both tables when we add a
4654 * Foreign Key, so the lock level must be at least
4655 * as strong as CREATE TRIGGER.
4657 cmd_lockmode = ShareRowExclusiveLock;
4658 break;
4660 default:
4661 cmd_lockmode = AccessExclusiveLock;
4664 break;
4667 * These subcommands affect inheritance behaviour. Queries
4668 * started before us will continue to see the old inheritance
4669 * behaviour, while queries started after we commit will see
4670 * new behaviour. No need to prevent reads or writes to the
4671 * subtable while we hook it up though. Changing the TupDesc
4672 * may be a problem, so keep highest lock.
4674 case AT_AddInherit:
4675 case AT_DropInherit:
4676 cmd_lockmode = AccessExclusiveLock;
4677 break;
4680 * These subcommands affect implicit row type conversion. They
4681 * have affects similar to CREATE/DROP CAST on queries. don't
4682 * provide for invalidating parse trees as a result of such
4683 * changes, so we keep these at AccessExclusiveLock.
4685 case AT_AddOf:
4686 case AT_DropOf:
4687 cmd_lockmode = AccessExclusiveLock;
4688 break;
4691 * Only used by CREATE OR REPLACE VIEW which must conflict
4692 * with an SELECTs currently using the view.
4694 case AT_ReplaceRelOptions:
4695 cmd_lockmode = AccessExclusiveLock;
4696 break;
4699 * These subcommands affect general strategies for performance
4700 * and maintenance, though don't change the semantic results
4701 * from normal data reads and writes. Delaying an ALTER TABLE
4702 * behind currently active writes only delays the point where
4703 * the new strategy begins to take effect, so there is no
4704 * benefit in waiting. In this case the minimum restriction
4705 * applies: we don't currently allow concurrent catalog
4706 * updates.
4708 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4709 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4710 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4711 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4712 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4713 cmd_lockmode = ShareUpdateExclusiveLock;
4714 break;
4716 case AT_SetLogged:
4717 case AT_SetUnLogged:
4718 cmd_lockmode = AccessExclusiveLock;
4719 break;
4721 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4722 cmd_lockmode = ShareUpdateExclusiveLock;
4723 break;
4726 * Rel options are more complex than first appears. Options
4727 * are set here for tables, views and indexes; for historical
4728 * reasons these can all be used with ALTER TABLE, so we can't
4729 * decide between them using the basic grammar.
4731 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4732 * getTables() */
4733 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4734 * getTables() */
4735 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4736 break;
4738 case AT_AttachPartition:
4739 cmd_lockmode = ShareUpdateExclusiveLock;
4740 break;
4742 case AT_DetachPartition:
4743 if (((PartitionCmd *) cmd->def)->concurrent)
4744 cmd_lockmode = ShareUpdateExclusiveLock;
4745 else
4746 cmd_lockmode = AccessExclusiveLock;
4747 break;
4749 case AT_DetachPartitionFinalize:
4750 cmd_lockmode = ShareUpdateExclusiveLock;
4751 break;
4753 case AT_SplitPartition:
4754 cmd_lockmode = AccessExclusiveLock;
4755 break;
4757 case AT_MergePartitions:
4758 cmd_lockmode = AccessExclusiveLock;
4759 break;
4761 default: /* oops */
4762 elog(ERROR, "unrecognized alter table type: %d",
4763 (int) cmd->subtype);
4764 break;
4768 * Take the greatest lockmode from any subcommand
4770 if (cmd_lockmode > lockmode)
4771 lockmode = cmd_lockmode;
4774 return lockmode;
4778 * ATController provides top level control over the phases.
4780 * parsetree is passed in to allow it to be passed to event triggers
4781 * when requested.
4783 static void
4784 ATController(AlterTableStmt *parsetree,
4785 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4786 AlterTableUtilityContext *context)
4788 List *wqueue = NIL;
4789 ListCell *lcmd;
4791 /* Phase 1: preliminary examination of commands, create work queue */
4792 foreach(lcmd, cmds)
4794 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4796 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4799 /* Close the relation, but keep lock until commit */
4800 relation_close(rel, NoLock);
4802 /* Phase 2: update system catalogs */
4803 ATRewriteCatalogs(&wqueue, lockmode, context);
4805 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4806 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4810 * ATPrepCmd
4812 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4813 * recursion and permission checks.
4815 * Caller must have acquired appropriate lock type on relation already.
4816 * This lock should be held until commit.
4818 static void
4819 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4820 bool recurse, bool recursing, LOCKMODE lockmode,
4821 AlterTableUtilityContext *context)
4823 AlteredTableInfo *tab;
4824 AlterTablePass pass = AT_PASS_UNSET;
4826 /* Find or create work queue entry for this table */
4827 tab = ATGetQueueEntry(wqueue, rel);
4830 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4831 * partitions that are pending detach.
4833 if (rel->rd_rel->relispartition &&
4834 cmd->subtype != AT_DetachPartitionFinalize &&
4835 PartitionHasPendingDetach(RelationGetRelid(rel)))
4836 ereport(ERROR,
4837 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4838 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4839 RelationGetRelationName(rel)),
4840 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4843 * Copy the original subcommand for each table, so we can scribble on it.
4844 * This avoids conflicts when different child tables need to make
4845 * different parse transformations (for example, the same column may have
4846 * different column numbers in different children).
4848 cmd = copyObject(cmd);
4851 * Do permissions and relkind checking, recursion to child tables if
4852 * needed, and any additional phase-1 processing needed. (But beware of
4853 * adding any processing that looks at table details that another
4854 * subcommand could change. In some cases we reject multiple subcommands
4855 * that could try to change the same state in contrary ways.)
4857 switch (cmd->subtype)
4859 case AT_AddColumn: /* ADD COLUMN */
4860 ATSimplePermissions(cmd->subtype, rel,
4861 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4862 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4863 lockmode, context);
4864 /* Recursion occurs during execution phase */
4865 pass = AT_PASS_ADD_COL;
4866 break;
4867 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4868 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4869 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4870 lockmode, context);
4871 /* Recursion occurs during execution phase */
4872 pass = AT_PASS_ADD_COL;
4873 break;
4874 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4877 * We allow defaults on views so that INSERT into a view can have
4878 * default-ish behavior. This works because the rewriter
4879 * substitutes default values into INSERTs before it expands
4880 * rules.
4882 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4883 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4884 /* No command-specific prep needed */
4885 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4886 break;
4887 case AT_CookedColumnDefault: /* add a pre-cooked default */
4888 /* This is currently used only in CREATE TABLE */
4889 /* (so the permission check really isn't necessary) */
4890 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4891 /* This command never recurses */
4892 pass = AT_PASS_ADD_OTHERCONSTR;
4893 break;
4894 case AT_AddIdentity:
4895 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4896 /* Set up recursion for phase 2; no other prep needed */
4897 if (recurse)
4898 cmd->recurse = true;
4899 pass = AT_PASS_ADD_OTHERCONSTR;
4900 break;
4901 case AT_SetIdentity:
4902 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4903 /* Set up recursion for phase 2; no other prep needed */
4904 if (recurse)
4905 cmd->recurse = true;
4906 /* This should run after AddIdentity, so do it in MISC pass */
4907 pass = AT_PASS_MISC;
4908 break;
4909 case AT_DropIdentity:
4910 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4911 /* Set up recursion for phase 2; no other prep needed */
4912 if (recurse)
4913 cmd->recurse = true;
4914 pass = AT_PASS_DROP;
4915 break;
4916 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4917 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4918 /* Set up recursion for phase 2; no other prep needed */
4919 if (recurse)
4920 cmd->recurse = true;
4921 pass = AT_PASS_DROP;
4922 break;
4923 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4924 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4925 /* Set up recursion for phase 2; no other prep needed */
4926 if (recurse)
4927 cmd->recurse = true;
4928 pass = AT_PASS_COL_ATTRS;
4929 break;
4930 case AT_SetAttNotNull: /* set pg_attribute.attnotnull without adding
4931 * a constraint */
4932 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4933 /* Need command-specific recursion decision */
4934 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4935 pass = AT_PASS_COL_ATTRS;
4936 break;
4937 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4938 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4939 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4940 pass = AT_PASS_SET_EXPRESSION;
4941 break;
4942 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4943 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4944 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4945 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4946 pass = AT_PASS_DROP;
4947 break;
4948 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4949 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4950 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4951 /* No command-specific prep needed */
4952 pass = AT_PASS_MISC;
4953 break;
4954 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4955 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4956 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4957 /* This command never recurses */
4958 pass = AT_PASS_MISC;
4959 break;
4960 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4961 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4962 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4963 /* No command-specific prep needed */
4964 pass = AT_PASS_MISC;
4965 break;
4966 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4967 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4968 /* This command never recurses */
4969 /* No command-specific prep needed */
4970 pass = AT_PASS_MISC;
4971 break;
4972 case AT_DropColumn: /* DROP COLUMN */
4973 ATSimplePermissions(cmd->subtype, rel,
4974 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4975 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4976 lockmode, context);
4977 /* Recursion occurs during execution phase */
4978 pass = AT_PASS_DROP;
4979 break;
4980 case AT_AddIndex: /* ADD INDEX */
4981 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4982 /* This command never recurses */
4983 /* No command-specific prep needed */
4984 pass = AT_PASS_ADD_INDEX;
4985 break;
4986 case AT_AddConstraint: /* ADD CONSTRAINT */
4987 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4988 if (recurse)
4990 /* recurses at exec time; lock descendants and set flag */
4991 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
4992 cmd->recurse = true;
4994 pass = AT_PASS_ADD_CONSTR;
4995 break;
4996 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4997 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4998 /* This command never recurses */
4999 /* No command-specific prep needed */
5000 pass = AT_PASS_ADD_INDEXCONSTR;
5001 break;
5002 case AT_DropConstraint: /* DROP CONSTRAINT */
5003 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5004 ATCheckPartitionsNotInUse(rel, lockmode);
5005 /* Other recursion occurs during execution phase */
5006 /* No command-specific prep needed except saving recurse flag */
5007 if (recurse)
5008 cmd->recurse = true;
5009 pass = AT_PASS_DROP;
5010 break;
5011 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5012 ATSimplePermissions(cmd->subtype, rel,
5013 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
5014 /* See comments for ATPrepAlterColumnType */
5015 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5016 AT_PASS_UNSET, context);
5017 Assert(cmd != NULL);
5018 /* Performs own recursion */
5019 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5020 lockmode, context);
5021 pass = AT_PASS_ALTER_TYPE;
5022 break;
5023 case AT_AlterColumnGenericOptions:
5024 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5025 /* This command never recurses */
5026 /* No command-specific prep needed */
5027 pass = AT_PASS_MISC;
5028 break;
5029 case AT_ChangeOwner: /* ALTER OWNER */
5030 /* This command never recurses */
5031 /* No command-specific prep needed */
5032 pass = AT_PASS_MISC;
5033 break;
5034 case AT_ClusterOn: /* CLUSTER ON */
5035 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5036 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5037 /* These commands never recurse */
5038 /* No command-specific prep needed */
5039 pass = AT_PASS_MISC;
5040 break;
5041 case AT_SetLogged: /* SET LOGGED */
5042 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5043 if (tab->chgPersistence)
5044 ereport(ERROR,
5045 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5046 errmsg("cannot change persistence setting twice")));
5047 tab->chgPersistence = ATPrepChangePersistence(rel, true);
5048 /* force rewrite if necessary; see comment in ATRewriteTables */
5049 if (tab->chgPersistence)
5051 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5052 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
5054 pass = AT_PASS_MISC;
5055 break;
5056 case AT_SetUnLogged: /* SET UNLOGGED */
5057 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
5058 if (tab->chgPersistence)
5059 ereport(ERROR,
5060 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5061 errmsg("cannot change persistence setting twice")));
5062 tab->chgPersistence = ATPrepChangePersistence(rel, false);
5063 /* force rewrite if necessary; see comment in ATRewriteTables */
5064 if (tab->chgPersistence)
5066 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
5067 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
5069 pass = AT_PASS_MISC;
5070 break;
5071 case AT_DropOids: /* SET WITHOUT OIDS */
5072 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5073 pass = AT_PASS_DROP;
5074 break;
5075 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5076 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5078 /* check if another access method change was already requested */
5079 if (tab->chgAccessMethod)
5080 ereport(ERROR,
5081 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5082 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5084 ATPrepSetAccessMethod(tab, rel, cmd->name);
5085 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5086 break;
5087 case AT_SetTableSpace: /* SET TABLESPACE */
5088 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5089 ATT_PARTITIONED_INDEX);
5090 /* This command never recurses */
5091 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5092 pass = AT_PASS_MISC; /* doesn't actually matter */
5093 break;
5094 case AT_SetRelOptions: /* SET (...) */
5095 case AT_ResetRelOptions: /* RESET (...) */
5096 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5097 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5098 /* This command never recurses */
5099 /* No command-specific prep needed */
5100 pass = AT_PASS_MISC;
5101 break;
5102 case AT_AddInherit: /* INHERIT */
5103 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5104 /* This command never recurses */
5105 ATPrepAddInherit(rel);
5106 pass = AT_PASS_MISC;
5107 break;
5108 case AT_DropInherit: /* NO INHERIT */
5109 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5110 /* This command never recurses */
5111 /* No command-specific prep needed */
5112 pass = AT_PASS_MISC;
5113 break;
5114 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5115 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5116 /* Recursion occurs during execution phase */
5117 pass = AT_PASS_MISC;
5118 break;
5119 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5120 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5121 /* Recursion occurs during execution phase */
5122 /* No command-specific prep needed except saving recurse flag */
5123 if (recurse)
5124 cmd->recurse = true;
5125 pass = AT_PASS_MISC;
5126 break;
5127 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5128 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5129 pass = AT_PASS_MISC;
5130 /* This command never recurses */
5131 /* No command-specific prep needed */
5132 break;
5133 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5134 case AT_EnableAlwaysTrig:
5135 case AT_EnableReplicaTrig:
5136 case AT_EnableTrigAll:
5137 case AT_EnableTrigUser:
5138 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5139 case AT_DisableTrigAll:
5140 case AT_DisableTrigUser:
5141 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5142 /* Set up recursion for phase 2; no other prep needed */
5143 if (recurse)
5144 cmd->recurse = true;
5145 pass = AT_PASS_MISC;
5146 break;
5147 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5148 case AT_EnableAlwaysRule:
5149 case AT_EnableReplicaRule:
5150 case AT_DisableRule:
5151 case AT_AddOf: /* OF */
5152 case AT_DropOf: /* NOT OF */
5153 case AT_EnableRowSecurity:
5154 case AT_DisableRowSecurity:
5155 case AT_ForceRowSecurity:
5156 case AT_NoForceRowSecurity:
5157 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5158 /* These commands never recurse */
5159 /* No command-specific prep needed */
5160 pass = AT_PASS_MISC;
5161 break;
5162 case AT_GenericOptions:
5163 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5164 /* No command-specific prep needed */
5165 pass = AT_PASS_MISC;
5166 break;
5167 case AT_AttachPartition:
5168 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5169 /* No command-specific prep needed */
5170 pass = AT_PASS_MISC;
5171 break;
5172 case AT_DetachPartition:
5173 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5174 /* No command-specific prep needed */
5175 pass = AT_PASS_MISC;
5176 break;
5177 case AT_DetachPartitionFinalize:
5178 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5179 /* No command-specific prep needed */
5180 pass = AT_PASS_MISC;
5181 break;
5182 case AT_SplitPartition:
5183 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5184 /* No command-specific prep needed */
5185 pass = AT_PASS_MISC;
5186 break;
5187 case AT_MergePartitions:
5188 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5189 /* No command-specific prep needed */
5190 pass = AT_PASS_MISC;
5191 break;
5192 default: /* oops */
5193 elog(ERROR, "unrecognized alter table type: %d",
5194 (int) cmd->subtype);
5195 pass = AT_PASS_UNSET; /* keep compiler quiet */
5196 break;
5198 Assert(pass > AT_PASS_UNSET);
5200 /* Add the subcommand to the appropriate list for phase 2 */
5201 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5205 * ATRewriteCatalogs
5207 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5208 * dispatched in a "safe" execution order (designed to avoid unnecessary
5209 * conflicts).
5211 static void
5212 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5213 AlterTableUtilityContext *context)
5215 ListCell *ltab;
5218 * We process all the tables "in parallel", one pass at a time. This is
5219 * needed because we may have to propagate work from one table to another
5220 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5221 * re-adding of the foreign key constraint to the other table). Work can
5222 * only be propagated into later passes, however.
5224 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5226 /* Go through each table that needs to be processed */
5227 foreach(ltab, *wqueue)
5229 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5230 List *subcmds = tab->subcmds[pass];
5231 ListCell *lcmd;
5233 if (subcmds == NIL)
5234 continue;
5237 * Open the relation and store it in tab. This allows subroutines
5238 * close and reopen, if necessary. Appropriate lock was obtained
5239 * by phase 1, needn't get it again.
5241 tab->rel = relation_open(tab->relid, NoLock);
5243 foreach(lcmd, subcmds)
5244 ATExecCmd(wqueue, tab,
5245 lfirst_node(AlterTableCmd, lcmd),
5246 lockmode, pass, context);
5249 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5250 * (this is not done in ATExecAlterColumnType since it should be
5251 * done only once if multiple columns of a table are altered).
5253 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5254 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5256 if (tab->rel)
5258 relation_close(tab->rel, NoLock);
5259 tab->rel = NULL;
5264 /* Check to see if a toast table must be added. */
5265 foreach(ltab, *wqueue)
5267 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5270 * If the table is source table of ATTACH PARTITION command, we did
5271 * not modify anything about it that will change its toasting
5272 * requirement, so no need to check.
5274 if (((tab->relkind == RELKIND_RELATION ||
5275 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5276 tab->partition_constraint == NULL) ||
5277 tab->relkind == RELKIND_MATVIEW)
5278 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5283 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5285 static void
5286 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5287 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5288 AlterTableUtilityContext *context)
5290 ObjectAddress address = InvalidObjectAddress;
5291 Relation rel = tab->rel;
5293 switch (cmd->subtype)
5295 case AT_AddColumn: /* ADD COLUMN */
5296 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5297 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5298 cmd->recurse, false,
5299 lockmode, cur_pass, context);
5300 break;
5301 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5302 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5303 break;
5304 case AT_CookedColumnDefault: /* add a pre-cooked default */
5305 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5306 break;
5307 case AT_AddIdentity:
5308 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5309 cur_pass, context);
5310 Assert(cmd != NULL);
5311 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5312 break;
5313 case AT_SetIdentity:
5314 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5315 cur_pass, context);
5316 Assert(cmd != NULL);
5317 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5318 break;
5319 case AT_DropIdentity:
5320 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5321 break;
5322 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5323 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5324 break;
5325 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5326 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5327 cmd->recurse, false, NULL, lockmode);
5328 break;
5329 case AT_SetAttNotNull: /* set pg_attribute.attnotnull */
5330 address = ATExecSetAttNotNull(wqueue, rel, cmd->name, lockmode);
5331 break;
5332 case AT_SetExpression:
5333 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5334 break;
5335 case AT_DropExpression:
5336 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5337 break;
5338 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5339 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5340 break;
5341 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5342 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5343 break;
5344 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5345 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5346 break;
5347 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5348 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5349 break;
5350 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5351 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5352 lockmode);
5353 break;
5354 case AT_DropColumn: /* DROP COLUMN */
5355 address = ATExecDropColumn(wqueue, rel, cmd->name,
5356 cmd->behavior, cmd->recurse, false,
5357 cmd->missing_ok, lockmode,
5358 NULL);
5359 break;
5360 case AT_AddIndex: /* ADD INDEX */
5361 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5362 lockmode);
5363 break;
5364 case AT_ReAddIndex: /* ADD INDEX */
5365 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5366 lockmode);
5367 break;
5368 case AT_ReAddStatistics: /* ADD STATISTICS */
5369 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5370 true, lockmode);
5371 break;
5372 case AT_AddConstraint: /* ADD CONSTRAINT */
5373 /* Transform the command only during initial examination */
5374 if (cur_pass == AT_PASS_ADD_CONSTR)
5375 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5376 cmd->recurse, lockmode,
5377 cur_pass, context);
5378 /* Depending on constraint type, might be no more work to do now */
5379 if (cmd != NULL)
5380 address =
5381 ATExecAddConstraint(wqueue, tab, rel,
5382 (Constraint *) cmd->def,
5383 cmd->recurse, false, lockmode);
5384 break;
5385 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5386 address =
5387 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5388 true, true, lockmode);
5389 break;
5390 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5391 * constraint */
5392 address =
5393 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5394 ((AlterDomainStmt *) cmd->def)->def,
5395 NULL);
5396 break;
5397 case AT_ReAddComment: /* Re-add existing comment */
5398 address = CommentObject((CommentStmt *) cmd->def);
5399 break;
5400 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5401 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5402 lockmode);
5403 break;
5404 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5405 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5406 break;
5407 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5408 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5409 false, lockmode);
5410 break;
5411 case AT_DropConstraint: /* DROP CONSTRAINT */
5412 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5413 cmd->recurse,
5414 cmd->missing_ok, lockmode);
5415 break;
5416 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5417 /* parse transformation was done earlier */
5418 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5419 break;
5420 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5421 address =
5422 ATExecAlterColumnGenericOptions(rel, cmd->name,
5423 (List *) cmd->def, lockmode);
5424 break;
5425 case AT_ChangeOwner: /* ALTER OWNER */
5426 ATExecChangeOwner(RelationGetRelid(rel),
5427 get_rolespec_oid(cmd->newowner, false),
5428 false, lockmode);
5429 break;
5430 case AT_ClusterOn: /* CLUSTER ON */
5431 address = ATExecClusterOn(rel, cmd->name, lockmode);
5432 break;
5433 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5434 ATExecDropCluster(rel, lockmode);
5435 break;
5436 case AT_SetLogged: /* SET LOGGED */
5437 case AT_SetUnLogged: /* SET UNLOGGED */
5438 break;
5439 case AT_DropOids: /* SET WITHOUT OIDS */
5440 /* nothing to do here, oid columns don't exist anymore */
5441 break;
5442 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5445 * Only do this for partitioned tables, for which this is just a
5446 * catalog change. Tables with storage are handled by Phase 3.
5448 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5449 tab->chgAccessMethod)
5450 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5451 break;
5452 case AT_SetTableSpace: /* SET TABLESPACE */
5455 * Only do this for partitioned tables and indexes, for which this
5456 * is just a catalog change. Other relation types which have
5457 * storage are handled by Phase 3.
5459 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5460 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5461 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5463 break;
5464 case AT_SetRelOptions: /* SET (...) */
5465 case AT_ResetRelOptions: /* RESET (...) */
5466 case AT_ReplaceRelOptions: /* replace entire option list */
5467 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5468 break;
5469 case AT_EnableTrig: /* ENABLE TRIGGER name */
5470 ATExecEnableDisableTrigger(rel, cmd->name,
5471 TRIGGER_FIRES_ON_ORIGIN, false,
5472 cmd->recurse,
5473 lockmode);
5474 break;
5475 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5476 ATExecEnableDisableTrigger(rel, cmd->name,
5477 TRIGGER_FIRES_ALWAYS, false,
5478 cmd->recurse,
5479 lockmode);
5480 break;
5481 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5482 ATExecEnableDisableTrigger(rel, cmd->name,
5483 TRIGGER_FIRES_ON_REPLICA, false,
5484 cmd->recurse,
5485 lockmode);
5486 break;
5487 case AT_DisableTrig: /* DISABLE TRIGGER name */
5488 ATExecEnableDisableTrigger(rel, cmd->name,
5489 TRIGGER_DISABLED, false,
5490 cmd->recurse,
5491 lockmode);
5492 break;
5493 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5494 ATExecEnableDisableTrigger(rel, NULL,
5495 TRIGGER_FIRES_ON_ORIGIN, false,
5496 cmd->recurse,
5497 lockmode);
5498 break;
5499 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5500 ATExecEnableDisableTrigger(rel, NULL,
5501 TRIGGER_DISABLED, false,
5502 cmd->recurse,
5503 lockmode);
5504 break;
5505 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5506 ATExecEnableDisableTrigger(rel, NULL,
5507 TRIGGER_FIRES_ON_ORIGIN, true,
5508 cmd->recurse,
5509 lockmode);
5510 break;
5511 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5512 ATExecEnableDisableTrigger(rel, NULL,
5513 TRIGGER_DISABLED, true,
5514 cmd->recurse,
5515 lockmode);
5516 break;
5518 case AT_EnableRule: /* ENABLE RULE name */
5519 ATExecEnableDisableRule(rel, cmd->name,
5520 RULE_FIRES_ON_ORIGIN, lockmode);
5521 break;
5522 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5523 ATExecEnableDisableRule(rel, cmd->name,
5524 RULE_FIRES_ALWAYS, lockmode);
5525 break;
5526 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5527 ATExecEnableDisableRule(rel, cmd->name,
5528 RULE_FIRES_ON_REPLICA, lockmode);
5529 break;
5530 case AT_DisableRule: /* DISABLE RULE name */
5531 ATExecEnableDisableRule(rel, cmd->name,
5532 RULE_DISABLED, lockmode);
5533 break;
5535 case AT_AddInherit:
5536 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5537 break;
5538 case AT_DropInherit:
5539 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5540 break;
5541 case AT_AddOf:
5542 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5543 break;
5544 case AT_DropOf:
5545 ATExecDropOf(rel, lockmode);
5546 break;
5547 case AT_ReplicaIdentity:
5548 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5549 break;
5550 case AT_EnableRowSecurity:
5551 ATExecSetRowSecurity(rel, true);
5552 break;
5553 case AT_DisableRowSecurity:
5554 ATExecSetRowSecurity(rel, false);
5555 break;
5556 case AT_ForceRowSecurity:
5557 ATExecForceNoForceRowSecurity(rel, true);
5558 break;
5559 case AT_NoForceRowSecurity:
5560 ATExecForceNoForceRowSecurity(rel, false);
5561 break;
5562 case AT_GenericOptions:
5563 ATExecGenericOptions(rel, (List *) cmd->def);
5564 break;
5565 case AT_AttachPartition:
5566 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5567 cur_pass, context);
5568 Assert(cmd != NULL);
5569 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5570 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5571 context);
5572 else
5573 address = ATExecAttachPartitionIdx(wqueue, rel,
5574 ((PartitionCmd *) cmd->def)->name);
5575 break;
5576 case AT_DetachPartition:
5577 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5578 cur_pass, context);
5579 Assert(cmd != NULL);
5580 /* ATPrepCmd ensures it must be a table */
5581 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5582 address = ATExecDetachPartition(wqueue, tab, rel,
5583 ((PartitionCmd *) cmd->def)->name,
5584 ((PartitionCmd *) cmd->def)->concurrent);
5585 break;
5586 case AT_DetachPartitionFinalize:
5587 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5588 break;
5589 case AT_SplitPartition:
5590 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5591 cur_pass, context);
5592 Assert(cmd != NULL);
5593 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5594 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5595 context);
5596 break;
5597 case AT_MergePartitions:
5598 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5599 cur_pass, context);
5600 Assert(cmd != NULL);
5601 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5602 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5603 context);
5604 break;
5605 default: /* oops */
5606 elog(ERROR, "unrecognized alter table type: %d",
5607 (int) cmd->subtype);
5608 break;
5612 * Report the subcommand to interested event triggers.
5614 if (cmd)
5615 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5618 * Bump the command counter to ensure the next subcommand in the sequence
5619 * can see the changes so far
5621 CommandCounterIncrement();
5625 * ATParseTransformCmd: perform parse transformation for one subcommand
5627 * Returns the transformed subcommand tree, if there is one, else NULL.
5629 * The parser may hand back additional AlterTableCmd(s) and/or other
5630 * utility statements, either before or after the original subcommand.
5631 * Other AlterTableCmds are scheduled into the appropriate slot of the
5632 * AlteredTableInfo (they had better be for later passes than the current one).
5633 * Utility statements that are supposed to happen before the AlterTableCmd
5634 * are executed immediately. Those that are supposed to happen afterwards
5635 * are added to the tab->afterStmts list to be done at the very end.
5637 static AlterTableCmd *
5638 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5639 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5640 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5642 AlterTableCmd *newcmd = NULL;
5643 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5644 List *beforeStmts;
5645 List *afterStmts;
5646 ListCell *lc;
5648 /* Gin up an AlterTableStmt with just this subcommand and this table */
5649 atstmt->relation =
5650 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5651 pstrdup(RelationGetRelationName(rel)),
5652 -1);
5653 atstmt->relation->inh = recurse;
5654 atstmt->cmds = list_make1(cmd);
5655 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5656 atstmt->missing_ok = false;
5658 /* Transform the AlterTableStmt */
5659 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5660 atstmt,
5661 context->queryString,
5662 &beforeStmts,
5663 &afterStmts);
5665 /* Execute any statements that should happen before these subcommand(s) */
5666 foreach(lc, beforeStmts)
5668 Node *stmt = (Node *) lfirst(lc);
5670 ProcessUtilityForAlterTable(stmt, context);
5671 CommandCounterIncrement();
5674 /* Examine the transformed subcommands and schedule them appropriately */
5675 foreach(lc, atstmt->cmds)
5677 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5678 AlterTablePass pass;
5681 * This switch need only cover the subcommand types that can be added
5682 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5683 * executing the subcommand immediately, as a substitute for the
5684 * original subcommand. (Note, however, that this does cause
5685 * AT_AddConstraint subcommands to be rescheduled into later passes,
5686 * which is important for index and foreign key constraints.)
5688 * We assume we needn't do any phase-1 checks for added subcommands.
5690 switch (cmd2->subtype)
5692 case AT_SetAttNotNull:
5693 ATSimpleRecursion(wqueue, rel, cmd2, recurse, lockmode, context);
5694 pass = AT_PASS_COL_ATTRS;
5695 break;
5696 case AT_AddIndex:
5699 * A primary key on an inheritance parent needs supporting NOT
5700 * NULL constraint on its children; enqueue commands to create
5701 * those or mark them inherited if they already exist.
5703 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5704 pass = AT_PASS_ADD_INDEX;
5705 break;
5706 case AT_AddIndexConstraint:
5707 /* as above */
5708 ATPrepAddPrimaryKey(wqueue, rel, cmd2, lockmode, context);
5709 pass = AT_PASS_ADD_INDEXCONSTR;
5710 break;
5711 case AT_AddConstraint:
5712 /* Recursion occurs during execution phase */
5713 if (recurse)
5714 cmd2->recurse = true;
5715 switch (castNode(Constraint, cmd2->def)->contype)
5717 case CONSTR_PRIMARY:
5718 case CONSTR_UNIQUE:
5719 case CONSTR_EXCLUSION:
5720 pass = AT_PASS_ADD_INDEXCONSTR;
5721 break;
5722 default:
5723 pass = AT_PASS_ADD_OTHERCONSTR;
5724 break;
5726 break;
5727 case AT_AlterColumnGenericOptions:
5728 /* This command never recurses */
5729 /* No command-specific prep needed */
5730 pass = AT_PASS_MISC;
5731 break;
5732 default:
5733 pass = cur_pass;
5734 break;
5737 if (pass < cur_pass)
5739 /* Cannot schedule into a pass we already finished */
5740 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5741 pass);
5743 else if (pass > cur_pass)
5745 /* OK, queue it up for later */
5746 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5748 else
5751 * We should see at most one subcommand for the current pass,
5752 * which is the transformed version of the original subcommand.
5754 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5756 /* Found the transformed version of our subcommand */
5757 newcmd = cmd2;
5759 else
5760 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5761 pass);
5765 /* Queue up any after-statements to happen at the end */
5766 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5768 return newcmd;
5772 * ATRewriteTables: ALTER TABLE phase 3
5774 static void
5775 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5776 AlterTableUtilityContext *context)
5778 ListCell *ltab;
5780 /* Go through each table that needs to be checked or rewritten */
5781 foreach(ltab, *wqueue)
5783 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5785 /* Relations without storage may be ignored here */
5786 if (!RELKIND_HAS_STORAGE(tab->relkind))
5787 continue;
5790 * If we change column data types, the operation has to be propagated
5791 * to tables that use this table's rowtype as a column type.
5792 * tab->newvals will also be non-NULL in the case where we're adding a
5793 * column with a default. We choose to forbid that case as well,
5794 * since composite types might eventually support defaults.
5796 * (Eventually we'll probably need to check for composite type
5797 * dependencies even when we're just scanning the table without a
5798 * rewrite, but at the moment a composite type does not enforce any
5799 * constraints, so it's not necessary/appropriate to enforce them just
5800 * during ALTER.)
5802 if (tab->newvals != NIL || tab->rewrite > 0)
5804 Relation rel;
5806 rel = table_open(tab->relid, NoLock);
5807 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5808 table_close(rel, NoLock);
5812 * We only need to rewrite the table if at least one column needs to
5813 * be recomputed, or we are changing its persistence or access method.
5815 * There are two reasons for requiring a rewrite when changing
5816 * persistence: on one hand, we need to ensure that the buffers
5817 * belonging to each of the two relations are marked with or without
5818 * BM_PERMANENT properly. On the other hand, since rewriting creates
5819 * and assigns a new relfilenumber, we automatically create or drop an
5820 * init fork for the relation as appropriate.
5822 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5824 /* Build a temporary relation and copy data */
5825 Relation OldHeap;
5826 Oid OIDNewHeap;
5827 Oid NewAccessMethod;
5828 Oid NewTableSpace;
5829 char persistence;
5831 OldHeap = table_open(tab->relid, NoLock);
5834 * We don't support rewriting of system catalogs; there are too
5835 * many corner cases and too little benefit. In particular this
5836 * is certainly not going to work for mapped catalogs.
5838 if (IsSystemRelation(OldHeap))
5839 ereport(ERROR,
5840 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5841 errmsg("cannot rewrite system relation \"%s\"",
5842 RelationGetRelationName(OldHeap))));
5844 if (RelationIsUsedAsCatalogTable(OldHeap))
5845 ereport(ERROR,
5846 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5847 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5848 RelationGetRelationName(OldHeap))));
5851 * Don't allow rewrite on temp tables of other backends ... their
5852 * local buffer manager is not going to cope.
5854 if (RELATION_IS_OTHER_TEMP(OldHeap))
5855 ereport(ERROR,
5856 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5857 errmsg("cannot rewrite temporary tables of other sessions")));
5860 * Select destination tablespace (same as original unless user
5861 * requested a change)
5863 if (tab->newTableSpace)
5864 NewTableSpace = tab->newTableSpace;
5865 else
5866 NewTableSpace = OldHeap->rd_rel->reltablespace;
5869 * Select destination access method (same as original unless user
5870 * requested a change)
5872 if (tab->chgAccessMethod)
5873 NewAccessMethod = tab->newAccessMethod;
5874 else
5875 NewAccessMethod = OldHeap->rd_rel->relam;
5878 * Select persistence of transient table (same as original unless
5879 * user requested a change)
5881 persistence = tab->chgPersistence ?
5882 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5884 table_close(OldHeap, NoLock);
5887 * Fire off an Event Trigger now, before actually rewriting the
5888 * table.
5890 * We don't support Event Trigger for nested commands anywhere,
5891 * here included, and parsetree is given NULL when coming from
5892 * AlterTableInternal.
5894 * And fire it only once.
5896 if (parsetree)
5897 EventTriggerTableRewrite((Node *) parsetree,
5898 tab->relid,
5899 tab->rewrite);
5902 * Create transient table that will receive the modified data.
5904 * Ensure it is marked correctly as logged or unlogged. We have
5905 * to do this here so that buffers for the new relfilenumber will
5906 * have the right persistence set, and at the same time ensure
5907 * that the original filenumbers's buffers will get read in with
5908 * the correct setting (i.e. the original one). Otherwise a
5909 * rollback after the rewrite would possibly result with buffers
5910 * for the original filenumbers having the wrong persistence
5911 * setting.
5913 * NB: This relies on swap_relation_files() also swapping the
5914 * persistence. That wouldn't work for pg_class, but that can't be
5915 * unlogged anyway.
5917 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5918 persistence, lockmode);
5921 * Copy the heap data into the new table with the desired
5922 * modifications, and test the current data within the table
5923 * against new constraints generated by ALTER TABLE commands.
5925 ATRewriteTable(tab, OIDNewHeap, lockmode);
5928 * Swap the physical files of the old and new heaps, then rebuild
5929 * indexes and discard the old heap. We can use RecentXmin for
5930 * the table's new relfrozenxid because we rewrote all the tuples
5931 * in ATRewriteTable, so no older Xid remains in the table. Also,
5932 * we never try to swap toast tables by content, since we have no
5933 * interest in letting this code work on system catalogs.
5935 finish_heap_swap(tab->relid, OIDNewHeap,
5936 false, false, true,
5937 !OidIsValid(tab->newTableSpace),
5938 RecentXmin,
5939 ReadNextMultiXactId(),
5940 persistence);
5942 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5944 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5946 if (tab->chgPersistence)
5947 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5949 else
5952 * If required, test the current data within the table against new
5953 * constraints generated by ALTER TABLE commands, but don't
5954 * rebuild data.
5956 if (tab->constraints != NIL || tab->verify_new_notnull ||
5957 tab->partition_constraint != NULL)
5958 ATRewriteTable(tab, InvalidOid, lockmode);
5961 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5962 * just do a block-by-block copy.
5964 if (tab->newTableSpace)
5965 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5969 * Also change persistence of owned sequences, so that it matches the
5970 * table persistence.
5972 if (tab->chgPersistence)
5974 List *seqlist = getOwnedSequences(tab->relid);
5975 ListCell *lc;
5977 foreach(lc, seqlist)
5979 Oid seq_relid = lfirst_oid(lc);
5981 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5987 * Foreign key constraints are checked in a final pass, since (a) it's
5988 * generally best to examine each one separately, and (b) it's at least
5989 * theoretically possible that we have changed both relations of the
5990 * foreign key, and we'd better have finished both rewrites before we try
5991 * to read the tables.
5993 foreach(ltab, *wqueue)
5995 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5996 Relation rel = NULL;
5997 ListCell *lcon;
5999 /* Relations without storage may be ignored here too */
6000 if (!RELKIND_HAS_STORAGE(tab->relkind))
6001 continue;
6003 foreach(lcon, tab->constraints)
6005 NewConstraint *con = lfirst(lcon);
6007 if (con->contype == CONSTR_FOREIGN)
6009 Constraint *fkconstraint = (Constraint *) con->qual;
6010 Relation refrel;
6012 if (rel == NULL)
6014 /* Long since locked, no need for another */
6015 rel = table_open(tab->relid, NoLock);
6018 refrel = table_open(con->refrelid, RowShareLock);
6020 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
6021 con->refindid,
6022 con->conid,
6023 con->conwithperiod);
6026 * No need to mark the constraint row as validated, we did
6027 * that when we inserted the row earlier.
6030 table_close(refrel, NoLock);
6034 if (rel)
6035 table_close(rel, NoLock);
6038 /* Finally, run any afterStmts that were queued up */
6039 foreach(ltab, *wqueue)
6041 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
6042 ListCell *lc;
6044 foreach(lc, tab->afterStmts)
6046 Node *stmt = (Node *) lfirst(lc);
6048 ProcessUtilityForAlterTable(stmt, context);
6049 CommandCounterIncrement();
6055 * ATRewriteTable: scan or rewrite one table
6057 * OIDNewHeap is InvalidOid if we don't need to rewrite
6059 static void
6060 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
6062 Relation oldrel;
6063 Relation newrel;
6064 TupleDesc oldTupDesc;
6065 TupleDesc newTupDesc;
6066 bool needscan = false;
6067 List *notnull_attrs;
6068 int i;
6069 ListCell *l;
6070 EState *estate;
6071 CommandId mycid;
6072 BulkInsertState bistate;
6073 int ti_options;
6074 ExprState *partqualstate = NULL;
6077 * Open the relation(s). We have surely already locked the existing
6078 * table.
6080 oldrel = table_open(tab->relid, NoLock);
6081 oldTupDesc = tab->oldDesc;
6082 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6084 if (OidIsValid(OIDNewHeap))
6085 newrel = table_open(OIDNewHeap, lockmode);
6086 else
6087 newrel = NULL;
6090 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6091 * is empty, so don't bother using it.
6093 if (newrel)
6095 mycid = GetCurrentCommandId(true);
6096 bistate = GetBulkInsertState();
6097 ti_options = TABLE_INSERT_SKIP_FSM;
6099 else
6101 /* keep compiler quiet about using these uninitialized */
6102 mycid = 0;
6103 bistate = NULL;
6104 ti_options = 0;
6108 * Generate the constraint and default execution states
6111 estate = CreateExecutorState();
6113 /* Build the needed expression execution states */
6114 foreach(l, tab->constraints)
6116 NewConstraint *con = lfirst(l);
6118 switch (con->contype)
6120 case CONSTR_CHECK:
6121 needscan = true;
6122 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6123 break;
6124 case CONSTR_FOREIGN:
6125 /* Nothing to do here */
6126 break;
6127 default:
6128 elog(ERROR, "unrecognized constraint type: %d",
6129 (int) con->contype);
6133 /* Build expression execution states for partition check quals */
6134 if (tab->partition_constraint)
6136 needscan = true;
6137 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6140 foreach(l, tab->newvals)
6142 NewColumnValue *ex = lfirst(l);
6144 /* expr already planned */
6145 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6148 notnull_attrs = NIL;
6149 if (newrel || tab->verify_new_notnull)
6152 * If we are rebuilding the tuples OR if we added any new but not
6153 * verified not-null constraints, check all not-null constraints. This
6154 * is a bit of overkill but it minimizes risk of bugs, and
6155 * heap_attisnull is a pretty cheap test anyway.
6157 for (i = 0; i < newTupDesc->natts; i++)
6159 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6161 if (attr->attnotnull && !attr->attisdropped)
6162 notnull_attrs = lappend_int(notnull_attrs, i);
6164 if (notnull_attrs)
6165 needscan = true;
6168 if (newrel || needscan)
6170 ExprContext *econtext;
6171 TupleTableSlot *oldslot;
6172 TupleTableSlot *newslot;
6173 TableScanDesc scan;
6174 MemoryContext oldCxt;
6175 List *dropped_attrs = NIL;
6176 ListCell *lc;
6177 Snapshot snapshot;
6179 if (newrel)
6180 ereport(DEBUG1,
6181 (errmsg_internal("rewriting table \"%s\"",
6182 RelationGetRelationName(oldrel))));
6183 else
6184 ereport(DEBUG1,
6185 (errmsg_internal("verifying table \"%s\"",
6186 RelationGetRelationName(oldrel))));
6188 if (newrel)
6191 * All predicate locks on the tuples or pages are about to be made
6192 * invalid, because we move tuples around. Promote them to
6193 * relation locks.
6195 TransferPredicateLocksToHeapRelation(oldrel);
6198 econtext = GetPerTupleExprContext(estate);
6201 * Create necessary tuple slots. When rewriting, two slots are needed,
6202 * otherwise one suffices. In the case where one slot suffices, we
6203 * need to use the new tuple descriptor, otherwise some constraints
6204 * can't be evaluated. Note that even when the tuple layout is the
6205 * same and no rewrite is required, the tupDescs might not be
6206 * (consider ADD COLUMN without a default).
6208 if (tab->rewrite)
6210 Assert(newrel != NULL);
6211 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6212 table_slot_callbacks(oldrel));
6213 newslot = MakeSingleTupleTableSlot(newTupDesc,
6214 table_slot_callbacks(newrel));
6217 * Set all columns in the new slot to NULL initially, to ensure
6218 * columns added as part of the rewrite are initialized to NULL.
6219 * That is necessary as tab->newvals will not contain an
6220 * expression for columns with a NULL default, e.g. when adding a
6221 * column without a default together with a column with a default
6222 * requiring an actual rewrite.
6224 ExecStoreAllNullTuple(newslot);
6226 else
6228 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6229 table_slot_callbacks(oldrel));
6230 newslot = NULL;
6234 * Any attributes that are dropped according to the new tuple
6235 * descriptor can be set to NULL. We precompute the list of dropped
6236 * attributes to avoid needing to do so in the per-tuple loop.
6238 for (i = 0; i < newTupDesc->natts; i++)
6240 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6241 dropped_attrs = lappend_int(dropped_attrs, i);
6245 * Scan through the rows, generating a new row if needed and then
6246 * checking all the constraints.
6248 snapshot = RegisterSnapshot(GetLatestSnapshot());
6249 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6252 * Switch to per-tuple memory context and reset it for each tuple
6253 * produced, so we don't leak memory.
6255 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6257 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6259 TupleTableSlot *insertslot;
6261 if (tab->rewrite > 0)
6263 /* Extract data from old tuple */
6264 slot_getallattrs(oldslot);
6265 ExecClearTuple(newslot);
6267 /* copy attributes */
6268 memcpy(newslot->tts_values, oldslot->tts_values,
6269 sizeof(Datum) * oldslot->tts_nvalid);
6270 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6271 sizeof(bool) * oldslot->tts_nvalid);
6273 /* Set dropped attributes to null in new tuple */
6274 foreach(lc, dropped_attrs)
6275 newslot->tts_isnull[lfirst_int(lc)] = true;
6278 * Constraints and GENERATED expressions might reference the
6279 * tableoid column, so fill tts_tableOid with the desired
6280 * value. (We must do this each time, because it gets
6281 * overwritten with newrel's OID during storing.)
6283 newslot->tts_tableOid = RelationGetRelid(oldrel);
6286 * Process supplied expressions to replace selected columns.
6288 * First, evaluate expressions whose inputs come from the old
6289 * tuple.
6291 econtext->ecxt_scantuple = oldslot;
6293 foreach(l, tab->newvals)
6295 NewColumnValue *ex = lfirst(l);
6297 if (ex->is_generated)
6298 continue;
6300 newslot->tts_values[ex->attnum - 1]
6301 = ExecEvalExpr(ex->exprstate,
6302 econtext,
6303 &newslot->tts_isnull[ex->attnum - 1]);
6306 ExecStoreVirtualTuple(newslot);
6309 * Now, evaluate any expressions whose inputs come from the
6310 * new tuple. We assume these columns won't reference each
6311 * other, so that there's no ordering dependency.
6313 econtext->ecxt_scantuple = newslot;
6315 foreach(l, tab->newvals)
6317 NewColumnValue *ex = lfirst(l);
6319 if (!ex->is_generated)
6320 continue;
6322 newslot->tts_values[ex->attnum - 1]
6323 = ExecEvalExpr(ex->exprstate,
6324 econtext,
6325 &newslot->tts_isnull[ex->attnum - 1]);
6328 insertslot = newslot;
6330 else
6333 * If there's no rewrite, old and new table are guaranteed to
6334 * have the same AM, so we can just use the old slot to verify
6335 * new constraints etc.
6337 insertslot = oldslot;
6340 /* Now check any constraints on the possibly-changed tuple */
6341 econtext->ecxt_scantuple = insertslot;
6343 foreach(l, notnull_attrs)
6345 int attn = lfirst_int(l);
6347 if (slot_attisnull(insertslot, attn + 1))
6349 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6351 ereport(ERROR,
6352 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6353 errmsg("column \"%s\" of relation \"%s\" contains null values",
6354 NameStr(attr->attname),
6355 RelationGetRelationName(oldrel)),
6356 errtablecol(oldrel, attn + 1)));
6360 foreach(l, tab->constraints)
6362 NewConstraint *con = lfirst(l);
6364 switch (con->contype)
6366 case CONSTR_CHECK:
6367 if (!ExecCheck(con->qualstate, econtext))
6368 ereport(ERROR,
6369 (errcode(ERRCODE_CHECK_VIOLATION),
6370 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6371 con->name,
6372 RelationGetRelationName(oldrel)),
6373 errtableconstraint(oldrel, con->name)));
6374 break;
6375 case CONSTR_NOTNULL:
6376 case CONSTR_FOREIGN:
6377 /* Nothing to do here */
6378 break;
6379 default:
6380 elog(ERROR, "unrecognized constraint type: %d",
6381 (int) con->contype);
6385 if (partqualstate && !ExecCheck(partqualstate, econtext))
6387 if (tab->validate_default)
6388 ereport(ERROR,
6389 (errcode(ERRCODE_CHECK_VIOLATION),
6390 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6391 RelationGetRelationName(oldrel)),
6392 errtable(oldrel)));
6393 else
6394 ereport(ERROR,
6395 (errcode(ERRCODE_CHECK_VIOLATION),
6396 errmsg("partition constraint of relation \"%s\" is violated by some row",
6397 RelationGetRelationName(oldrel)),
6398 errtable(oldrel)));
6401 /* Write the tuple out to the new relation */
6402 if (newrel)
6403 table_tuple_insert(newrel, insertslot, mycid,
6404 ti_options, bistate);
6406 ResetExprContext(econtext);
6408 CHECK_FOR_INTERRUPTS();
6411 MemoryContextSwitchTo(oldCxt);
6412 table_endscan(scan);
6413 UnregisterSnapshot(snapshot);
6415 ExecDropSingleTupleTableSlot(oldslot);
6416 if (newslot)
6417 ExecDropSingleTupleTableSlot(newslot);
6420 FreeExecutorState(estate);
6422 table_close(oldrel, NoLock);
6423 if (newrel)
6425 FreeBulkInsertState(bistate);
6427 table_finish_bulk_insert(newrel, ti_options);
6429 table_close(newrel, NoLock);
6434 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6436 static AlteredTableInfo *
6437 ATGetQueueEntry(List **wqueue, Relation rel)
6439 Oid relid = RelationGetRelid(rel);
6440 AlteredTableInfo *tab;
6441 ListCell *ltab;
6443 foreach(ltab, *wqueue)
6445 tab = (AlteredTableInfo *) lfirst(ltab);
6446 if (tab->relid == relid)
6447 return tab;
6451 * Not there, so add it. Note that we make a copy of the relation's
6452 * existing descriptor before anything interesting can happen to it.
6454 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6455 tab->relid = relid;
6456 tab->rel = NULL; /* set later */
6457 tab->relkind = rel->rd_rel->relkind;
6458 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6459 tab->newAccessMethod = InvalidOid;
6460 tab->chgAccessMethod = false;
6461 tab->newTableSpace = InvalidOid;
6462 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6463 tab->chgPersistence = false;
6465 *wqueue = lappend(*wqueue, tab);
6467 return tab;
6470 static const char *
6471 alter_table_type_to_string(AlterTableType cmdtype)
6473 switch (cmdtype)
6475 case AT_AddColumn:
6476 case AT_AddColumnToView:
6477 return "ADD COLUMN";
6478 case AT_ColumnDefault:
6479 case AT_CookedColumnDefault:
6480 return "ALTER COLUMN ... SET DEFAULT";
6481 case AT_DropNotNull:
6482 return "ALTER COLUMN ... DROP NOT NULL";
6483 case AT_SetNotNull:
6484 return "ALTER COLUMN ... SET NOT NULL";
6485 case AT_SetAttNotNull:
6486 return NULL; /* not real grammar */
6487 case AT_SetExpression:
6488 return "ALTER COLUMN ... SET EXPRESSION";
6489 case AT_DropExpression:
6490 return "ALTER COLUMN ... DROP EXPRESSION";
6491 case AT_SetStatistics:
6492 return "ALTER COLUMN ... SET STATISTICS";
6493 case AT_SetOptions:
6494 return "ALTER COLUMN ... SET";
6495 case AT_ResetOptions:
6496 return "ALTER COLUMN ... RESET";
6497 case AT_SetStorage:
6498 return "ALTER COLUMN ... SET STORAGE";
6499 case AT_SetCompression:
6500 return "ALTER COLUMN ... SET COMPRESSION";
6501 case AT_DropColumn:
6502 return "DROP COLUMN";
6503 case AT_AddIndex:
6504 case AT_ReAddIndex:
6505 return NULL; /* not real grammar */
6506 case AT_AddConstraint:
6507 case AT_ReAddConstraint:
6508 case AT_ReAddDomainConstraint:
6509 case AT_AddIndexConstraint:
6510 return "ADD CONSTRAINT";
6511 case AT_AlterConstraint:
6512 return "ALTER CONSTRAINT";
6513 case AT_ValidateConstraint:
6514 return "VALIDATE CONSTRAINT";
6515 case AT_DropConstraint:
6516 return "DROP CONSTRAINT";
6517 case AT_ReAddComment:
6518 return NULL; /* not real grammar */
6519 case AT_AlterColumnType:
6520 return "ALTER COLUMN ... SET DATA TYPE";
6521 case AT_AlterColumnGenericOptions:
6522 return "ALTER COLUMN ... OPTIONS";
6523 case AT_ChangeOwner:
6524 return "OWNER TO";
6525 case AT_ClusterOn:
6526 return "CLUSTER ON";
6527 case AT_DropCluster:
6528 return "SET WITHOUT CLUSTER";
6529 case AT_SetAccessMethod:
6530 return "SET ACCESS METHOD";
6531 case AT_SetLogged:
6532 return "SET LOGGED";
6533 case AT_SetUnLogged:
6534 return "SET UNLOGGED";
6535 case AT_DropOids:
6536 return "SET WITHOUT OIDS";
6537 case AT_SetTableSpace:
6538 return "SET TABLESPACE";
6539 case AT_SetRelOptions:
6540 return "SET";
6541 case AT_ResetRelOptions:
6542 return "RESET";
6543 case AT_ReplaceRelOptions:
6544 return NULL; /* not real grammar */
6545 case AT_EnableTrig:
6546 return "ENABLE TRIGGER";
6547 case AT_EnableAlwaysTrig:
6548 return "ENABLE ALWAYS TRIGGER";
6549 case AT_EnableReplicaTrig:
6550 return "ENABLE REPLICA TRIGGER";
6551 case AT_DisableTrig:
6552 return "DISABLE TRIGGER";
6553 case AT_EnableTrigAll:
6554 return "ENABLE TRIGGER ALL";
6555 case AT_DisableTrigAll:
6556 return "DISABLE TRIGGER ALL";
6557 case AT_EnableTrigUser:
6558 return "ENABLE TRIGGER USER";
6559 case AT_DisableTrigUser:
6560 return "DISABLE TRIGGER USER";
6561 case AT_EnableRule:
6562 return "ENABLE RULE";
6563 case AT_EnableAlwaysRule:
6564 return "ENABLE ALWAYS RULE";
6565 case AT_EnableReplicaRule:
6566 return "ENABLE REPLICA RULE";
6567 case AT_DisableRule:
6568 return "DISABLE RULE";
6569 case AT_AddInherit:
6570 return "INHERIT";
6571 case AT_DropInherit:
6572 return "NO INHERIT";
6573 case AT_AddOf:
6574 return "OF";
6575 case AT_DropOf:
6576 return "NOT OF";
6577 case AT_ReplicaIdentity:
6578 return "REPLICA IDENTITY";
6579 case AT_EnableRowSecurity:
6580 return "ENABLE ROW SECURITY";
6581 case AT_DisableRowSecurity:
6582 return "DISABLE ROW SECURITY";
6583 case AT_ForceRowSecurity:
6584 return "FORCE ROW SECURITY";
6585 case AT_NoForceRowSecurity:
6586 return "NO FORCE ROW SECURITY";
6587 case AT_GenericOptions:
6588 return "OPTIONS";
6589 case AT_AttachPartition:
6590 return "ATTACH PARTITION";
6591 case AT_DetachPartition:
6592 return "DETACH PARTITION";
6593 case AT_DetachPartitionFinalize:
6594 return "DETACH PARTITION ... FINALIZE";
6595 case AT_SplitPartition:
6596 return "SPLIT PARTITION";
6597 case AT_MergePartitions:
6598 return "MERGE PARTITIONS";
6599 case AT_AddIdentity:
6600 return "ALTER COLUMN ... ADD IDENTITY";
6601 case AT_SetIdentity:
6602 return "ALTER COLUMN ... SET";
6603 case AT_DropIdentity:
6604 return "ALTER COLUMN ... DROP IDENTITY";
6605 case AT_ReAddStatistics:
6606 return NULL; /* not real grammar */
6609 return NULL;
6613 * ATSimplePermissions
6615 * - Ensure that it is a relation (or possibly a view)
6616 * - Ensure this user is the owner
6617 * - Ensure that it is not a system table
6619 static void
6620 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6622 int actual_target;
6624 switch (rel->rd_rel->relkind)
6626 case RELKIND_RELATION:
6627 case RELKIND_PARTITIONED_TABLE:
6628 actual_target = ATT_TABLE;
6629 break;
6630 case RELKIND_VIEW:
6631 actual_target = ATT_VIEW;
6632 break;
6633 case RELKIND_MATVIEW:
6634 actual_target = ATT_MATVIEW;
6635 break;
6636 case RELKIND_INDEX:
6637 actual_target = ATT_INDEX;
6638 break;
6639 case RELKIND_PARTITIONED_INDEX:
6640 actual_target = ATT_PARTITIONED_INDEX;
6641 break;
6642 case RELKIND_COMPOSITE_TYPE:
6643 actual_target = ATT_COMPOSITE_TYPE;
6644 break;
6645 case RELKIND_FOREIGN_TABLE:
6646 actual_target = ATT_FOREIGN_TABLE;
6647 break;
6648 case RELKIND_SEQUENCE:
6649 actual_target = ATT_SEQUENCE;
6650 break;
6651 default:
6652 actual_target = 0;
6653 break;
6656 /* Wrong target type? */
6657 if ((actual_target & allowed_targets) == 0)
6659 const char *action_str = alter_table_type_to_string(cmdtype);
6661 if (action_str)
6662 ereport(ERROR,
6663 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6664 /* translator: %s is a group of some SQL keywords */
6665 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6666 action_str, RelationGetRelationName(rel)),
6667 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6668 else
6669 /* internal error? */
6670 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6671 RelationGetRelationName(rel));
6674 /* Permissions checks */
6675 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6676 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6677 RelationGetRelationName(rel));
6679 if (!allowSystemTableMods && IsSystemRelation(rel))
6680 ereport(ERROR,
6681 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6682 errmsg("permission denied: \"%s\" is a system catalog",
6683 RelationGetRelationName(rel))));
6687 * ATSimpleRecursion
6689 * Simple table recursion sufficient for most ALTER TABLE operations.
6690 * All direct and indirect children are processed in an unspecified order.
6691 * Note that if a child inherits from the original table via multiple
6692 * inheritance paths, it will be visited just once.
6694 static void
6695 ATSimpleRecursion(List **wqueue, Relation rel,
6696 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6697 AlterTableUtilityContext *context)
6700 * Propagate to children, if desired and if there are (or might be) any
6701 * children.
6703 if (recurse && rel->rd_rel->relhassubclass)
6705 Oid relid = RelationGetRelid(rel);
6706 ListCell *child;
6707 List *children;
6709 children = find_all_inheritors(relid, lockmode, NULL);
6712 * find_all_inheritors does the recursive search of the inheritance
6713 * hierarchy, so all we have to do is process all of the relids in the
6714 * list that it returns.
6716 foreach(child, children)
6718 Oid childrelid = lfirst_oid(child);
6719 Relation childrel;
6721 if (childrelid == relid)
6722 continue;
6723 /* find_all_inheritors already got lock */
6724 childrel = relation_open(childrelid, NoLock);
6725 CheckTableNotInUse(childrel, "ALTER TABLE");
6726 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6727 relation_close(childrel, NoLock);
6733 * Obtain list of partitions of the given table, locking them all at the given
6734 * lockmode and ensuring that they all pass CheckTableNotInUse.
6736 * This function is a no-op if the given relation is not a partitioned table;
6737 * in particular, nothing is done if it's a legacy inheritance parent.
6739 static void
6740 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6742 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6744 List *inh;
6745 ListCell *cell;
6747 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6748 /* first element is the parent rel; must ignore it */
6749 for_each_from(cell, inh, 1)
6751 Relation childrel;
6753 /* find_all_inheritors already got lock */
6754 childrel = table_open(lfirst_oid(cell), NoLock);
6755 CheckTableNotInUse(childrel, "ALTER TABLE");
6756 table_close(childrel, NoLock);
6758 list_free(inh);
6763 * ATTypedTableRecursion
6765 * Propagate ALTER TYPE operations to the typed tables of that type.
6766 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6767 * recursion to inheritance children of the typed tables.
6769 static void
6770 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6771 LOCKMODE lockmode, AlterTableUtilityContext *context)
6773 ListCell *child;
6774 List *children;
6776 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6778 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6779 RelationGetRelationName(rel),
6780 cmd->behavior);
6782 foreach(child, children)
6784 Oid childrelid = lfirst_oid(child);
6785 Relation childrel;
6787 childrel = relation_open(childrelid, lockmode);
6788 CheckTableNotInUse(childrel, "ALTER TABLE");
6789 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6790 relation_close(childrel, NoLock);
6796 * find_composite_type_dependencies
6798 * Check to see if the type "typeOid" is being used as a column in some table
6799 * (possibly nested several levels deep in composite types, arrays, etc!).
6800 * Eventually, we'd like to propagate the check or rewrite operation
6801 * into such tables, but for now, just error out if we find any.
6803 * Caller should provide either the associated relation of a rowtype,
6804 * or a type name (not both) for use in the error message, if any.
6806 * Note that "typeOid" is not necessarily a composite type; it could also be
6807 * another container type such as an array or range, or a domain over one of
6808 * these things. The name of this function is therefore somewhat historical,
6809 * but it's not worth changing.
6811 * We assume that functions and views depending on the type are not reasons
6812 * to reject the ALTER. (How safe is this really?)
6814 void
6815 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6816 const char *origTypeName)
6818 Relation depRel;
6819 ScanKeyData key[2];
6820 SysScanDesc depScan;
6821 HeapTuple depTup;
6823 /* since this function recurses, it could be driven to stack overflow */
6824 check_stack_depth();
6827 * We scan pg_depend to find those things that depend on the given type.
6828 * (We assume we can ignore refobjsubid for a type.)
6830 depRel = table_open(DependRelationId, AccessShareLock);
6832 ScanKeyInit(&key[0],
6833 Anum_pg_depend_refclassid,
6834 BTEqualStrategyNumber, F_OIDEQ,
6835 ObjectIdGetDatum(TypeRelationId));
6836 ScanKeyInit(&key[1],
6837 Anum_pg_depend_refobjid,
6838 BTEqualStrategyNumber, F_OIDEQ,
6839 ObjectIdGetDatum(typeOid));
6841 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6842 NULL, 2, key);
6844 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6846 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6847 Relation rel;
6848 TupleDesc tupleDesc;
6849 Form_pg_attribute att;
6851 /* Check for directly dependent types */
6852 if (pg_depend->classid == TypeRelationId)
6855 * This must be an array, domain, or range containing the given
6856 * type, so recursively check for uses of this type. Note that
6857 * any error message will mention the original type not the
6858 * container; this is intentional.
6860 find_composite_type_dependencies(pg_depend->objid,
6861 origRelation, origTypeName);
6862 continue;
6865 /* Else, ignore dependees that aren't relations */
6866 if (pg_depend->classid != RelationRelationId)
6867 continue;
6869 rel = relation_open(pg_depend->objid, AccessShareLock);
6870 tupleDesc = RelationGetDescr(rel);
6873 * If objsubid identifies a specific column, refer to that in error
6874 * messages. Otherwise, search to see if there's a user column of the
6875 * type. (We assume system columns are never of interesting types.)
6876 * The search is needed because an index containing an expression
6877 * column of the target type will just be recorded as a whole-relation
6878 * dependency. If we do not find a column of the type, the dependency
6879 * must indicate that the type is transiently referenced in an index
6880 * expression but not stored on disk, which we assume is OK, just as
6881 * we do for references in views. (It could also be that the target
6882 * type is embedded in some container type that is stored in an index
6883 * column, but the previous recursion should catch such cases.)
6885 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6886 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6887 else
6889 att = NULL;
6890 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6892 att = TupleDescAttr(tupleDesc, attno - 1);
6893 if (att->atttypid == typeOid && !att->attisdropped)
6894 break;
6895 att = NULL;
6897 if (att == NULL)
6899 /* No such column, so assume OK */
6900 relation_close(rel, AccessShareLock);
6901 continue;
6906 * We definitely should reject if the relation has storage. If it's
6907 * partitioned, then perhaps we don't have to reject: if there are
6908 * partitions then we'll fail when we find one, else there is no
6909 * stored data to worry about. However, it's possible that the type
6910 * change would affect conclusions about whether the type is sortable
6911 * or hashable and thus (if it's a partitioning column) break the
6912 * partitioning rule. For now, reject for partitioned rels too.
6914 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6915 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6917 if (origTypeName)
6918 ereport(ERROR,
6919 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6920 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6921 origTypeName,
6922 RelationGetRelationName(rel),
6923 NameStr(att->attname))));
6924 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6925 ereport(ERROR,
6926 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6927 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6928 RelationGetRelationName(origRelation),
6929 RelationGetRelationName(rel),
6930 NameStr(att->attname))));
6931 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6932 ereport(ERROR,
6933 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6934 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6935 RelationGetRelationName(origRelation),
6936 RelationGetRelationName(rel),
6937 NameStr(att->attname))));
6938 else
6939 ereport(ERROR,
6940 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6941 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6942 RelationGetRelationName(origRelation),
6943 RelationGetRelationName(rel),
6944 NameStr(att->attname))));
6946 else if (OidIsValid(rel->rd_rel->reltype))
6949 * A view or composite type itself isn't a problem, but we must
6950 * recursively check for indirect dependencies via its rowtype.
6952 find_composite_type_dependencies(rel->rd_rel->reltype,
6953 origRelation, origTypeName);
6956 relation_close(rel, AccessShareLock);
6959 systable_endscan(depScan);
6961 relation_close(depRel, AccessShareLock);
6966 * find_typed_table_dependencies
6968 * Check to see if a composite type is being used as the type of a
6969 * typed table. Abort if any are found and behavior is RESTRICT.
6970 * Else return the list of tables.
6972 static List *
6973 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6975 Relation classRel;
6976 ScanKeyData key[1];
6977 TableScanDesc scan;
6978 HeapTuple tuple;
6979 List *result = NIL;
6981 classRel = table_open(RelationRelationId, AccessShareLock);
6983 ScanKeyInit(&key[0],
6984 Anum_pg_class_reloftype,
6985 BTEqualStrategyNumber, F_OIDEQ,
6986 ObjectIdGetDatum(typeOid));
6988 scan = table_beginscan_catalog(classRel, 1, key);
6990 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6992 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6994 if (behavior == DROP_RESTRICT)
6995 ereport(ERROR,
6996 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6997 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6998 typeName),
6999 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7000 else
7001 result = lappend_oid(result, classform->oid);
7004 table_endscan(scan);
7005 table_close(classRel, AccessShareLock);
7007 return result;
7012 * check_of_type
7014 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
7015 * isn't suitable, throw an error. Currently, we require that the type
7016 * originated with CREATE TYPE AS. We could support any row type, but doing so
7017 * would require handling a number of extra corner cases in the DDL commands.
7018 * (Also, allowing domain-over-composite would open up a can of worms about
7019 * whether and how the domain's constraints should apply to derived tables.)
7021 void
7022 check_of_type(HeapTuple typetuple)
7024 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
7025 bool typeOk = false;
7027 if (typ->typtype == TYPTYPE_COMPOSITE)
7029 Relation typeRelation;
7031 Assert(OidIsValid(typ->typrelid));
7032 typeRelation = relation_open(typ->typrelid, AccessShareLock);
7033 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7036 * Close the parent rel, but keep our AccessShareLock on it until xact
7037 * commit. That will prevent someone else from deleting or ALTERing
7038 * the type before the typed table creation/conversion commits.
7040 relation_close(typeRelation, NoLock);
7042 if (!typeOk)
7043 ereport(ERROR,
7044 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7045 errmsg("type %s is not a composite type",
7046 format_type_be(typ->oid))));
7051 * ALTER TABLE ADD COLUMN
7053 * Adds an additional attribute to a relation making the assumption that
7054 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
7055 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
7056 * AlterTableCmd's.
7058 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
7059 * have to decide at runtime whether to recurse or not depending on whether we
7060 * actually add a column or merely merge with an existing column. (We can't
7061 * check this in a static pre-pass because it won't handle multiple inheritance
7062 * situations correctly.)
7064 static void
7065 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
7066 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
7067 AlterTableUtilityContext *context)
7069 if (rel->rd_rel->reloftype && !recursing)
7070 ereport(ERROR,
7071 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7072 errmsg("cannot add column to typed table")));
7074 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7075 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7077 if (recurse && !is_view)
7078 cmd->recurse = true;
7082 * Add a column to a table. The return value is the address of the
7083 * new column in the parent relation.
7085 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7086 * copy (but that happens only after we check for IF NOT EXISTS).
7088 static ObjectAddress
7089 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7090 AlterTableCmd **cmd, bool recurse, bool recursing,
7091 LOCKMODE lockmode, AlterTablePass cur_pass,
7092 AlterTableUtilityContext *context)
7094 Oid myrelid = RelationGetRelid(rel);
7095 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7096 bool if_not_exists = (*cmd)->missing_ok;
7097 Relation pgclass,
7098 attrdesc;
7099 HeapTuple reltup;
7100 Form_pg_attribute attribute;
7101 int newattnum;
7102 char relkind;
7103 Expr *defval;
7104 List *children;
7105 ListCell *child;
7106 AlterTableCmd *childcmd;
7107 ObjectAddress address;
7108 TupleDesc tupdesc;
7110 /* since this function recurses, it could be driven to stack overflow */
7111 check_stack_depth();
7113 /* At top level, permission check was done in ATPrepCmd, else do it */
7114 if (recursing)
7115 ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7117 if (rel->rd_rel->relispartition && !recursing)
7118 ereport(ERROR,
7119 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7120 errmsg("cannot add column to a partition")));
7122 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7125 * Are we adding the column to a recursion child? If so, check whether to
7126 * merge with an existing definition for the column. If we do merge, we
7127 * must not recurse. Children will already have the column, and recursing
7128 * into them would mess up attinhcount.
7130 if (colDef->inhcount > 0)
7132 HeapTuple tuple;
7134 /* Does child already have a column by this name? */
7135 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7136 if (HeapTupleIsValid(tuple))
7138 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7139 Oid ctypeId;
7140 int32 ctypmod;
7141 Oid ccollid;
7143 /* Child column must match on type, typmod, and collation */
7144 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7145 if (ctypeId != childatt->atttypid ||
7146 ctypmod != childatt->atttypmod)
7147 ereport(ERROR,
7148 (errcode(ERRCODE_DATATYPE_MISMATCH),
7149 errmsg("child table \"%s\" has different type for column \"%s\"",
7150 RelationGetRelationName(rel), colDef->colname)));
7151 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7152 if (ccollid != childatt->attcollation)
7153 ereport(ERROR,
7154 (errcode(ERRCODE_COLLATION_MISMATCH),
7155 errmsg("child table \"%s\" has different collation for column \"%s\"",
7156 RelationGetRelationName(rel), colDef->colname),
7157 errdetail("\"%s\" versus \"%s\"",
7158 get_collation_name(ccollid),
7159 get_collation_name(childatt->attcollation))));
7161 /* Bump the existing child att's inhcount */
7162 childatt->attinhcount++;
7163 if (childatt->attinhcount < 0)
7164 ereport(ERROR,
7165 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7166 errmsg("too many inheritance parents"));
7167 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7169 heap_freetuple(tuple);
7171 /* Inform the user about the merge */
7172 ereport(NOTICE,
7173 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7174 colDef->colname, RelationGetRelationName(rel))));
7176 table_close(attrdesc, RowExclusiveLock);
7178 /* Make the child column change visible */
7179 CommandCounterIncrement();
7181 return InvalidObjectAddress;
7185 /* skip if the name already exists and if_not_exists is true */
7186 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7188 table_close(attrdesc, RowExclusiveLock);
7189 return InvalidObjectAddress;
7193 * Okay, we need to add the column, so go ahead and do parse
7194 * transformation. This can result in queueing up, or even immediately
7195 * executing, subsidiary operations (such as creation of unique indexes);
7196 * so we mustn't do it until we have made the if_not_exists check.
7198 * When recursing, the command was already transformed and we needn't do
7199 * so again. Also, if context isn't given we can't transform. (That
7200 * currently happens only for AT_AddColumnToView; we expect that view.c
7201 * passed us a ColumnDef that doesn't need work.)
7203 if (context != NULL && !recursing)
7205 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7206 cur_pass, context);
7207 Assert(*cmd != NULL);
7208 colDef = castNode(ColumnDef, (*cmd)->def);
7212 * Regular inheritance children are independent enough not to inherit the
7213 * identity column from parent hence cannot recursively add identity
7214 * column if the table has inheritance children.
7216 * Partitions, on the other hand, are integral part of a partitioned table
7217 * and inherit identity column. Hence propagate identity column down the
7218 * partition hierarchy.
7220 if (colDef->identity &&
7221 recurse &&
7222 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7223 find_inheritance_children(myrelid, NoLock) != NIL)
7224 ereport(ERROR,
7225 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7226 errmsg("cannot recursively add identity column to table that has child tables")));
7228 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7230 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7231 if (!HeapTupleIsValid(reltup))
7232 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7233 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7235 /* Determine the new attribute's number */
7236 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7237 if (newattnum > MaxHeapAttributeNumber)
7238 ereport(ERROR,
7239 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7240 errmsg("tables can have at most %d columns",
7241 MaxHeapAttributeNumber)));
7244 * Construct new attribute's pg_attribute entry.
7246 tupdesc = BuildDescForRelation(list_make1(colDef));
7248 attribute = TupleDescAttr(tupdesc, 0);
7250 /* Fix up attribute number */
7251 attribute->attnum = newattnum;
7253 /* make sure datatype is legal for a column */
7254 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7255 list_make1_oid(rel->rd_rel->reltype),
7258 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7260 table_close(attrdesc, RowExclusiveLock);
7263 * Update pg_class tuple as appropriate
7265 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7267 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7269 heap_freetuple(reltup);
7271 /* Post creation hook for new attribute */
7272 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7274 table_close(pgclass, RowExclusiveLock);
7276 /* Make the attribute's catalog entry visible */
7277 CommandCounterIncrement();
7280 * Store the DEFAULT, if any, in the catalogs
7282 if (colDef->raw_default)
7284 RawColumnDefault *rawEnt;
7286 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7287 rawEnt->attnum = attribute->attnum;
7288 rawEnt->raw_default = copyObject(colDef->raw_default);
7291 * Attempt to skip a complete table rewrite by storing the specified
7292 * DEFAULT value outside of the heap. This may be disabled inside
7293 * AddRelationNewConstraints if the optimization cannot be applied.
7295 rawEnt->missingMode = (!colDef->generated);
7297 rawEnt->generated = colDef->generated;
7300 * This function is intended for CREATE TABLE, so it processes a
7301 * _list_ of defaults, but we just do one.
7303 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7304 false, true, false, NULL);
7306 /* Make the additional catalog changes visible */
7307 CommandCounterIncrement();
7310 * Did the request for a missing value work? If not we'll have to do a
7311 * rewrite
7313 if (!rawEnt->missingMode)
7314 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7318 * Tell Phase 3 to fill in the default expression, if there is one.
7320 * If there is no default, Phase 3 doesn't have to do anything, because
7321 * that effectively means that the default is NULL. The heap tuple access
7322 * routines always check for attnum > # of attributes in tuple, and return
7323 * NULL if so, so without any modification of the tuple data we will get
7324 * the effect of NULL values in the new column.
7326 * An exception occurs when the new column is of a domain type: the domain
7327 * might have a not-null constraint, or a check constraint that indirectly
7328 * rejects nulls. If there are any domain constraints then we construct
7329 * an explicit NULL default value that will be passed through
7330 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7331 * rewriting the table which we really don't have to do, but the present
7332 * design of domain processing doesn't offer any simple way of checking
7333 * the constraints more directly.)
7335 * Note: we use build_column_default, and not just the cooked default
7336 * returned by AddRelationNewConstraints, so that the right thing happens
7337 * when a datatype's default applies.
7339 * Note: it might seem that this should happen at the end of Phase 2, so
7340 * that the effects of subsequent subcommands can be taken into account.
7341 * It's intentional that we do it now, though. The new column should be
7342 * filled according to what is said in the ADD COLUMN subcommand, so that
7343 * the effects are the same as if this subcommand had been run by itself
7344 * and the later subcommands had been issued in new ALTER TABLE commands.
7346 * We can skip this entirely for relations without storage, since Phase 3
7347 * is certainly not going to touch them. System attributes don't have
7348 * interesting defaults, either.
7350 if (RELKIND_HAS_STORAGE(relkind))
7353 * For an identity column, we can't use build_column_default(),
7354 * because the sequence ownership isn't set yet. So do it manually.
7356 if (colDef->identity)
7358 NextValueExpr *nve = makeNode(NextValueExpr);
7360 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7361 nve->typeId = attribute->atttypid;
7363 defval = (Expr *) nve;
7365 /* must do a rewrite for identity columns */
7366 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7368 else
7369 defval = (Expr *) build_column_default(rel, attribute->attnum);
7371 if (!defval && DomainHasConstraints(attribute->atttypid))
7373 Oid baseTypeId;
7374 int32 baseTypeMod;
7375 Oid baseTypeColl;
7377 baseTypeMod = attribute->atttypmod;
7378 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7379 baseTypeColl = get_typcollation(baseTypeId);
7380 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7381 defval = (Expr *) coerce_to_target_type(NULL,
7382 (Node *) defval,
7383 baseTypeId,
7384 attribute->atttypid,
7385 attribute->atttypmod,
7386 COERCION_ASSIGNMENT,
7387 COERCE_IMPLICIT_CAST,
7388 -1);
7389 if (defval == NULL) /* should not happen */
7390 elog(ERROR, "failed to coerce base type to domain");
7393 if (defval)
7395 NewColumnValue *newval;
7397 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7398 newval->attnum = attribute->attnum;
7399 newval->expr = expression_planner(defval);
7400 newval->is_generated = (colDef->generated != '\0');
7402 tab->newvals = lappend(tab->newvals, newval);
7405 if (DomainHasConstraints(attribute->atttypid))
7406 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7408 if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7411 * If the new column is NOT NULL, and there is no missing value,
7412 * tell Phase 3 it needs to check for NULLs.
7414 tab->verify_new_notnull |= colDef->is_not_null;
7419 * Add needed dependency entries for the new column.
7421 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7422 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7425 * Propagate to children as appropriate. Unlike most other ALTER
7426 * routines, we have to do this one level of recursion at a time; we can't
7427 * use find_all_inheritors to do it in one pass.
7429 children =
7430 find_inheritance_children(RelationGetRelid(rel), lockmode);
7433 * If we are told not to recurse, there had better not be any child
7434 * tables; else the addition would put them out of step.
7436 if (children && !recurse)
7437 ereport(ERROR,
7438 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7439 errmsg("column must be added to child tables too")));
7441 /* Children should see column as singly inherited */
7442 if (!recursing)
7444 childcmd = copyObject(*cmd);
7445 colDef = castNode(ColumnDef, childcmd->def);
7446 colDef->inhcount = 1;
7447 colDef->is_local = false;
7449 else
7450 childcmd = *cmd; /* no need to copy again */
7452 foreach(child, children)
7454 Oid childrelid = lfirst_oid(child);
7455 Relation childrel;
7456 AlteredTableInfo *childtab;
7458 /* find_inheritance_children already got lock */
7459 childrel = table_open(childrelid, NoLock);
7460 CheckTableNotInUse(childrel, "ALTER TABLE");
7462 /* Find or create work queue entry for this table */
7463 childtab = ATGetQueueEntry(wqueue, childrel);
7465 /* Recurse to child; return value is ignored */
7466 ATExecAddColumn(wqueue, childtab, childrel,
7467 &childcmd, recurse, true,
7468 lockmode, cur_pass, context);
7470 table_close(childrel, NoLock);
7473 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7474 return address;
7478 * If a new or renamed column will collide with the name of an existing
7479 * column and if_not_exists is false then error out, else do nothing.
7481 static bool
7482 check_for_column_name_collision(Relation rel, const char *colname,
7483 bool if_not_exists)
7485 HeapTuple attTuple;
7486 int attnum;
7489 * this test is deliberately not attisdropped-aware, since if one tries to
7490 * add a column matching a dropped column name, it's gonna fail anyway.
7492 attTuple = SearchSysCache2(ATTNAME,
7493 ObjectIdGetDatum(RelationGetRelid(rel)),
7494 PointerGetDatum(colname));
7495 if (!HeapTupleIsValid(attTuple))
7496 return true;
7498 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7499 ReleaseSysCache(attTuple);
7502 * We throw a different error message for conflicts with system column
7503 * names, since they are normally not shown and the user might otherwise
7504 * be confused about the reason for the conflict.
7506 if (attnum <= 0)
7507 ereport(ERROR,
7508 (errcode(ERRCODE_DUPLICATE_COLUMN),
7509 errmsg("column name \"%s\" conflicts with a system column name",
7510 colname)));
7511 else
7513 if (if_not_exists)
7515 ereport(NOTICE,
7516 (errcode(ERRCODE_DUPLICATE_COLUMN),
7517 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7518 colname, RelationGetRelationName(rel))));
7519 return false;
7522 ereport(ERROR,
7523 (errcode(ERRCODE_DUPLICATE_COLUMN),
7524 errmsg("column \"%s\" of relation \"%s\" already exists",
7525 colname, RelationGetRelationName(rel))));
7528 return true;
7532 * Install a column's dependency on its datatype.
7534 static void
7535 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7537 ObjectAddress myself,
7538 referenced;
7540 myself.classId = RelationRelationId;
7541 myself.objectId = relid;
7542 myself.objectSubId = attnum;
7543 referenced.classId = TypeRelationId;
7544 referenced.objectId = typid;
7545 referenced.objectSubId = 0;
7546 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7550 * Install a column's dependency on its collation.
7552 static void
7553 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7555 ObjectAddress myself,
7556 referenced;
7558 /* We know the default collation is pinned, so don't bother recording it */
7559 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7561 myself.classId = RelationRelationId;
7562 myself.objectId = relid;
7563 myself.objectSubId = attnum;
7564 referenced.classId = CollationRelationId;
7565 referenced.objectId = collid;
7566 referenced.objectSubId = 0;
7567 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7572 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7574 * Return the address of the modified column. If the column was already
7575 * nullable, InvalidObjectAddress is returned.
7577 static ObjectAddress
7578 ATExecDropNotNull(Relation rel, const char *colName, bool recurse,
7579 LOCKMODE lockmode)
7581 HeapTuple tuple;
7582 HeapTuple conTup;
7583 Form_pg_attribute attTup;
7584 AttrNumber attnum;
7585 Relation attr_rel;
7586 ObjectAddress address;
7587 List *readyRels;
7590 * lookup the attribute
7592 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7594 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7595 if (!HeapTupleIsValid(tuple))
7596 ereport(ERROR,
7597 (errcode(ERRCODE_UNDEFINED_COLUMN),
7598 errmsg("column \"%s\" of relation \"%s\" does not exist",
7599 colName, RelationGetRelationName(rel))));
7600 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7601 attnum = attTup->attnum;
7602 ObjectAddressSubSet(address, RelationRelationId,
7603 RelationGetRelid(rel), attnum);
7605 /* If the column is already nullable there's nothing to do. */
7606 if (!attTup->attnotnull)
7608 table_close(attr_rel, RowExclusiveLock);
7609 return InvalidObjectAddress;
7612 /* Prevent them from altering a system attribute */
7613 if (attnum <= 0)
7614 ereport(ERROR,
7615 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7616 errmsg("cannot alter system column \"%s\"",
7617 colName)));
7619 if (attTup->attidentity)
7620 ereport(ERROR,
7621 (errcode(ERRCODE_SYNTAX_ERROR),
7622 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7623 colName, RelationGetRelationName(rel))));
7626 * It's not OK to remove a constraint only for the parent and leave it in
7627 * the children, so disallow that.
7629 if (!recurse)
7631 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7633 PartitionDesc partdesc;
7635 partdesc = RelationGetPartitionDesc(rel, true);
7637 if (partdesc->nparts > 0)
7638 ereport(ERROR,
7639 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7640 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7641 errhint("Do not specify the ONLY keyword."));
7643 else if (rel->rd_rel->relhassubclass &&
7644 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
7646 ereport(ERROR,
7647 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7648 errmsg("not-null constraint on column \"%s\" must be removed in child tables too",
7649 colName),
7650 errhint("Do not specify the ONLY keyword."));
7655 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7657 if (rel->rd_rel->relispartition)
7659 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7660 Relation parent = table_open(parentId, AccessShareLock);
7661 TupleDesc tupDesc = RelationGetDescr(parent);
7662 AttrNumber parent_attnum;
7664 parent_attnum = get_attnum(parentId, colName);
7665 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7666 ereport(ERROR,
7667 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7668 errmsg("column \"%s\" is marked NOT NULL in parent table",
7669 colName)));
7670 table_close(parent, AccessShareLock);
7674 * Find the constraint that makes this column NOT NULL, and drop it if we
7675 * see one. dropconstraint_internal() will do necessary consistency
7676 * checking. If there isn't one, there are two possibilities: either the
7677 * column is marked attnotnull because it's part of the primary key, and
7678 * then we just throw an appropriate error; or it's a leftover marking
7679 * that we can remove. However, before doing the latter, to avoid
7680 * breaking consistency any further, prevent this if the column is part of
7681 * the replica identity.
7683 conTup = findNotNullConstraint(RelationGetRelid(rel), colName);
7684 if (conTup == NULL)
7686 Bitmapset *pkcols;
7687 Bitmapset *ircols;
7690 * If the column is in a primary key, throw a specific error message.
7692 pkcols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_PRIMARY_KEY);
7693 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
7694 pkcols))
7695 ereport(ERROR,
7696 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7697 errmsg("column \"%s\" is in a primary key", colName));
7699 /* Also throw an error if the column is in the replica identity */
7700 ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
7701 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
7702 ereport(ERROR,
7703 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7704 errmsg("column \"%s\" is in index used as replica identity",
7705 get_attname(RelationGetRelid(rel), attnum, false)));
7707 /* Otherwise, just remove the attnotnull marking and do nothing else. */
7708 attTup->attnotnull = false;
7709 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7711 else
7713 /* The normal case: we have a pg_constraint row, remove it */
7714 readyRels = NIL;
7715 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7716 false, &readyRels, lockmode);
7718 heap_freetuple(conTup);
7721 InvokeObjectPostAlterHook(RelationRelationId,
7722 RelationGetRelid(rel), attnum);
7724 table_close(attr_rel, RowExclusiveLock);
7726 return address;
7730 * Helper to set pg_attribute.attnotnull if it isn't set, and to tell phase 3
7731 * to verify it; recurses to apply the same to children.
7733 * When called to alter an existing table, 'wqueue' must be given so that we can
7734 * queue a check that existing tuples pass the constraint. When called from
7735 * table creation, 'wqueue' should be passed as NULL.
7737 * Returns true if the flag was set in any table, otherwise false.
7739 static bool
7740 set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool recurse,
7741 LOCKMODE lockmode)
7743 HeapTuple tuple;
7744 Form_pg_attribute attForm;
7745 bool retval = false;
7747 /* Guard against stack overflow due to overly deep inheritance tree. */
7748 check_stack_depth();
7750 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), attnum);
7751 if (!HeapTupleIsValid(tuple))
7752 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7753 attnum, RelationGetRelid(rel));
7754 attForm = (Form_pg_attribute) GETSTRUCT(tuple);
7755 if (!attForm->attnotnull)
7757 Relation attr_rel;
7759 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7761 attForm->attnotnull = true;
7762 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7764 table_close(attr_rel, RowExclusiveLock);
7767 * And set up for existing values to be checked, unless another
7768 * constraint already proves this.
7770 if (wqueue && !NotNullImpliedByRelConstraints(rel, attForm))
7772 AlteredTableInfo *tab;
7774 tab = ATGetQueueEntry(wqueue, rel);
7775 tab->verify_new_notnull = true;
7778 retval = true;
7781 if (recurse)
7783 List *children;
7784 ListCell *lc;
7786 /* Make above update visible, for multiple inheritance cases */
7787 if (retval)
7788 CommandCounterIncrement();
7790 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
7791 foreach(lc, children)
7793 Oid childrelid = lfirst_oid(lc);
7794 Relation childrel;
7795 AttrNumber childattno;
7797 /* find_inheritance_children already got lock */
7798 childrel = table_open(childrelid, NoLock);
7799 CheckTableNotInUse(childrel, "ALTER TABLE");
7801 childattno = get_attnum(RelationGetRelid(childrel),
7802 get_attname(RelationGetRelid(rel), attnum,
7803 false));
7804 retval |= set_attnotnull(wqueue, childrel, childattno,
7805 recurse, lockmode);
7806 table_close(childrel, NoLock);
7810 return retval;
7814 * ALTER TABLE ALTER COLUMN SET NOT NULL
7816 * Add a not-null constraint to a single table and its children. Returns
7817 * the address of the constraint added to the parent relation, if one gets
7818 * added, or InvalidObjectAddress otherwise.
7820 * We must recurse to child tables during execution, rather than using
7821 * ALTER TABLE's normal prep-time recursion.
7823 static ObjectAddress
7824 ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName,
7825 bool recurse, bool recursing, List **readyRels,
7826 LOCKMODE lockmode)
7828 HeapTuple tuple;
7829 Relation constr_rel;
7830 ScanKeyData skey;
7831 SysScanDesc conscan;
7832 AttrNumber attnum;
7833 ObjectAddress address;
7834 Constraint *constraint;
7835 CookedConstraint *ccon;
7836 List *cooked;
7837 bool is_no_inherit = false;
7838 List *ready = NIL;
7840 /* Guard against stack overflow due to overly deep inheritance tree. */
7841 check_stack_depth();
7844 * In cases of multiple inheritance, we might visit the same child more
7845 * than once. In the topmost call, set up a list that we fill with all
7846 * visited relations, to skip those.
7848 if (readyRels == NULL)
7850 Assert(!recursing);
7851 readyRels = &ready;
7853 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
7854 return InvalidObjectAddress;
7855 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
7857 /* At top level, permission check was done in ATPrepCmd, else do it */
7858 if (recursing)
7860 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7861 Assert(conName != NULL);
7864 attnum = get_attnum(RelationGetRelid(rel), colName);
7865 if (attnum == InvalidAttrNumber)
7866 ereport(ERROR,
7867 (errcode(ERRCODE_UNDEFINED_COLUMN),
7868 errmsg("column \"%s\" of relation \"%s\" does not exist",
7869 colName, RelationGetRelationName(rel))));
7871 /* Prevent them from altering a system attribute */
7872 if (attnum <= 0)
7873 ereport(ERROR,
7874 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7875 errmsg("cannot alter system column \"%s\"",
7876 colName)));
7878 /* See if there's already a constraint */
7879 constr_rel = table_open(ConstraintRelationId, RowExclusiveLock);
7880 ScanKeyInit(&skey,
7881 Anum_pg_constraint_conrelid,
7882 BTEqualStrategyNumber, F_OIDEQ,
7883 ObjectIdGetDatum(RelationGetRelid(rel)));
7884 conscan = systable_beginscan(constr_rel, ConstraintRelidTypidNameIndexId, true,
7885 NULL, 1, &skey);
7887 while (HeapTupleIsValid(tuple = systable_getnext(conscan)))
7889 Form_pg_constraint conForm = (Form_pg_constraint) GETSTRUCT(tuple);
7890 bool changed = false;
7891 HeapTuple copytup;
7893 if (conForm->contype != CONSTRAINT_NOTNULL)
7894 continue;
7896 if (extractNotNullColumn(tuple) != attnum)
7897 continue;
7899 copytup = heap_copytuple(tuple);
7900 conForm = (Form_pg_constraint) GETSTRUCT(copytup);
7903 * Don't let a NO INHERIT constraint be changed into inherit.
7905 if (conForm->connoinherit && recurse)
7906 ereport(ERROR,
7907 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7908 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
7909 NameStr(conForm->conname),
7910 RelationGetRelationName(rel)));
7913 * If we find an appropriate constraint, we're almost done, but just
7914 * need to change some properties on it: if we're recursing, increment
7915 * coninhcount; if not, set conislocal if not already set.
7917 if (recursing)
7919 conForm->coninhcount++;
7920 changed = true;
7922 else if (!conForm->conislocal)
7924 conForm->conislocal = true;
7925 changed = true;
7928 if (changed)
7930 CatalogTupleUpdate(constr_rel, &copytup->t_self, copytup);
7931 ObjectAddressSet(address, ConstraintRelationId, conForm->oid);
7934 systable_endscan(conscan);
7935 table_close(constr_rel, RowExclusiveLock);
7937 if (changed)
7938 return address;
7939 else
7940 return InvalidObjectAddress;
7943 systable_endscan(conscan);
7944 table_close(constr_rel, RowExclusiveLock);
7947 * If we're asked not to recurse, and children exist, raise an error for
7948 * partitioned tables. For inheritance, we act as if NO INHERIT had been
7949 * specified.
7951 if (!recurse &&
7952 find_inheritance_children(RelationGetRelid(rel),
7953 NoLock) != NIL)
7955 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7956 ereport(ERROR,
7957 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7958 errmsg("constraint must be added to child tables too"),
7959 errhint("Do not specify the ONLY keyword."));
7960 else
7961 is_no_inherit = true;
7965 * No constraint exists; we must add one. First determine a name to use,
7966 * if we haven't already.
7968 if (!recursing)
7970 Assert(conName == NULL);
7971 conName = ChooseConstraintName(RelationGetRelationName(rel),
7972 colName, "not_null",
7973 RelationGetNamespace(rel),
7974 NIL);
7976 constraint = makeNode(Constraint);
7977 constraint->contype = CONSTR_NOTNULL;
7978 constraint->conname = conName;
7979 constraint->deferrable = false;
7980 constraint->initdeferred = false;
7981 constraint->location = -1;
7982 constraint->keys = list_make1(makeString(colName));
7983 constraint->is_no_inherit = is_no_inherit;
7984 constraint->inhcount = recursing ? 1 : 0;
7985 constraint->skip_validation = false;
7986 constraint->initially_valid = true;
7988 /* and do it */
7989 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
7990 false, !recursing, false, NULL);
7991 ccon = linitial(cooked);
7992 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
7994 InvokeObjectPostAlterHook(RelationRelationId,
7995 RelationGetRelid(rel), attnum);
7998 * Mark pg_attribute.attnotnull for the column. Tell that function not to
7999 * recurse, because we're going to do it here.
8001 set_attnotnull(wqueue, rel, attnum, false, lockmode);
8004 * Recurse to propagate the constraint to children that don't have one.
8006 if (recurse)
8008 List *children;
8009 ListCell *lc;
8011 children = find_inheritance_children(RelationGetRelid(rel),
8012 lockmode);
8014 foreach(lc, children)
8016 Relation childrel;
8018 childrel = table_open(lfirst_oid(lc), NoLock);
8020 ATExecSetNotNull(wqueue, childrel,
8021 conName, colName, recurse, true,
8022 readyRels, lockmode);
8024 table_close(childrel, NoLock);
8028 return address;
8032 * ALTER TABLE ALTER COLUMN SET ATTNOTNULL
8034 * This doesn't exist in the grammar; it's used when creating a
8035 * primary key and the column is not already marked attnotnull.
8037 static ObjectAddress
8038 ATExecSetAttNotNull(List **wqueue, Relation rel,
8039 const char *colName, LOCKMODE lockmode)
8041 AttrNumber attnum;
8042 ObjectAddress address = InvalidObjectAddress;
8044 attnum = get_attnum(RelationGetRelid(rel), colName);
8045 if (attnum == InvalidAttrNumber)
8046 ereport(ERROR,
8047 errcode(ERRCODE_UNDEFINED_COLUMN),
8048 errmsg("column \"%s\" of relation \"%s\" does not exist",
8049 colName, RelationGetRelationName(rel)));
8052 * Make the change, if necessary, and only if so report the column as
8053 * changed
8055 if (set_attnotnull(wqueue, rel, attnum, false, lockmode))
8056 ObjectAddressSubSet(address, RelationRelationId,
8057 RelationGetRelid(rel), attnum);
8059 return address;
8063 * NotNullImpliedByRelConstraints
8064 * Does rel's existing constraints imply NOT NULL for the given attribute?
8066 static bool
8067 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
8069 NullTest *nnulltest = makeNode(NullTest);
8071 nnulltest->arg = (Expr *) makeVar(1,
8072 attr->attnum,
8073 attr->atttypid,
8074 attr->atttypmod,
8075 attr->attcollation,
8077 nnulltest->nulltesttype = IS_NOT_NULL;
8080 * argisrow = false is correct even for a composite column, because
8081 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8082 * case, just IS DISTINCT FROM NULL.
8084 nnulltest->argisrow = false;
8085 nnulltest->location = -1;
8087 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
8089 ereport(DEBUG1,
8090 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8091 RelationGetRelationName(rel), NameStr(attr->attname))));
8092 return true;
8095 return false;
8099 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
8101 * Return the address of the affected column.
8103 static ObjectAddress
8104 ATExecColumnDefault(Relation rel, const char *colName,
8105 Node *newDefault, LOCKMODE lockmode)
8107 TupleDesc tupdesc = RelationGetDescr(rel);
8108 AttrNumber attnum;
8109 ObjectAddress address;
8112 * get the number of the attribute
8114 attnum = get_attnum(RelationGetRelid(rel), colName);
8115 if (attnum == InvalidAttrNumber)
8116 ereport(ERROR,
8117 (errcode(ERRCODE_UNDEFINED_COLUMN),
8118 errmsg("column \"%s\" of relation \"%s\" does not exist",
8119 colName, RelationGetRelationName(rel))));
8121 /* Prevent them from altering a system attribute */
8122 if (attnum <= 0)
8123 ereport(ERROR,
8124 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8125 errmsg("cannot alter system column \"%s\"",
8126 colName)));
8128 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8129 ereport(ERROR,
8130 (errcode(ERRCODE_SYNTAX_ERROR),
8131 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8132 colName, RelationGetRelationName(rel)),
8133 /* translator: %s is an SQL ALTER command */
8134 newDefault ? 0 : errhint("Use %s instead.",
8135 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8137 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8138 ereport(ERROR,
8139 (errcode(ERRCODE_SYNTAX_ERROR),
8140 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8141 colName, RelationGetRelationName(rel)),
8142 newDefault ?
8143 /* translator: %s is an SQL ALTER command */
8144 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8145 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8146 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8149 * Remove any old default for the column. We use RESTRICT here for
8150 * safety, but at present we do not expect anything to depend on the
8151 * default.
8153 * We treat removing the existing default as an internal operation when it
8154 * is preparatory to adding a new default, but as a user-initiated
8155 * operation when the user asked for a drop.
8157 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8158 newDefault != NULL);
8160 if (newDefault)
8162 /* SET DEFAULT */
8163 RawColumnDefault *rawEnt;
8165 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8166 rawEnt->attnum = attnum;
8167 rawEnt->raw_default = newDefault;
8168 rawEnt->missingMode = false;
8169 rawEnt->generated = '\0';
8172 * This function is intended for CREATE TABLE, so it processes a
8173 * _list_ of defaults, but we just do one.
8175 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8176 false, true, false, NULL);
8179 ObjectAddressSubSet(address, RelationRelationId,
8180 RelationGetRelid(rel), attnum);
8181 return address;
8185 * Add a pre-cooked default expression.
8187 * Return the address of the affected column.
8189 static ObjectAddress
8190 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
8191 Node *newDefault)
8193 ObjectAddress address;
8195 /* We assume no checking is required */
8198 * Remove any old default for the column. We use RESTRICT here for
8199 * safety, but at present we do not expect anything to depend on the
8200 * default. (In ordinary cases, there could not be a default in place
8201 * anyway, but it's possible when combining LIKE with inheritance.)
8203 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
8204 true);
8206 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
8208 ObjectAddressSubSet(address, RelationRelationId,
8209 RelationGetRelid(rel), attnum);
8210 return address;
8214 * ALTER TABLE ALTER COLUMN ADD IDENTITY
8216 * Return the address of the affected column.
8218 static ObjectAddress
8219 ATExecAddIdentity(Relation rel, const char *colName,
8220 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
8222 Relation attrelation;
8223 HeapTuple tuple;
8224 Form_pg_attribute attTup;
8225 AttrNumber attnum;
8226 ObjectAddress address;
8227 ColumnDef *cdef = castNode(ColumnDef, def);
8228 bool ispartitioned;
8230 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8231 if (ispartitioned && !recurse)
8232 ereport(ERROR,
8233 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8234 errmsg("cannot add identity to a column of only the partitioned table"),
8235 errhint("Do not specify the ONLY keyword.")));
8237 if (rel->rd_rel->relispartition && !recursing)
8238 ereport(ERROR,
8239 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8240 errmsg("cannot add identity to a column of a partition"));
8242 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8244 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8245 if (!HeapTupleIsValid(tuple))
8246 ereport(ERROR,
8247 (errcode(ERRCODE_UNDEFINED_COLUMN),
8248 errmsg("column \"%s\" of relation \"%s\" does not exist",
8249 colName, RelationGetRelationName(rel))));
8250 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8251 attnum = attTup->attnum;
8253 /* Can't alter a system attribute */
8254 if (attnum <= 0)
8255 ereport(ERROR,
8256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8257 errmsg("cannot alter system column \"%s\"",
8258 colName)));
8261 * Creating a column as identity implies NOT NULL, so adding the identity
8262 * to an existing column that is not NOT NULL would create a state that
8263 * cannot be reproduced without contortions.
8265 if (!attTup->attnotnull)
8266 ereport(ERROR,
8267 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8268 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8269 colName, RelationGetRelationName(rel))));
8271 if (attTup->attidentity)
8272 ereport(ERROR,
8273 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8274 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8275 colName, RelationGetRelationName(rel))));
8277 if (attTup->atthasdef)
8278 ereport(ERROR,
8279 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8280 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8281 colName, RelationGetRelationName(rel))));
8283 attTup->attidentity = cdef->identity;
8284 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8286 InvokeObjectPostAlterHook(RelationRelationId,
8287 RelationGetRelid(rel),
8288 attTup->attnum);
8289 ObjectAddressSubSet(address, RelationRelationId,
8290 RelationGetRelid(rel), attnum);
8291 heap_freetuple(tuple);
8293 table_close(attrelation, RowExclusiveLock);
8296 * Recurse to propagate the identity column to partitions. Identity is
8297 * not inherited in regular inheritance children.
8299 if (recurse && ispartitioned)
8301 List *children;
8302 ListCell *lc;
8304 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8306 foreach(lc, children)
8308 Relation childrel;
8310 childrel = table_open(lfirst_oid(lc), NoLock);
8311 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8312 table_close(childrel, NoLock);
8316 return address;
8320 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8322 * Return the address of the affected column.
8324 static ObjectAddress
8325 ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8326 LOCKMODE lockmode, bool recurse, bool recursing)
8328 ListCell *option;
8329 DefElem *generatedEl = NULL;
8330 HeapTuple tuple;
8331 Form_pg_attribute attTup;
8332 AttrNumber attnum;
8333 Relation attrelation;
8334 ObjectAddress address;
8335 bool ispartitioned;
8337 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8338 if (ispartitioned && !recurse)
8339 ereport(ERROR,
8340 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8341 errmsg("cannot change identity column of only the partitioned table"),
8342 errhint("Do not specify the ONLY keyword.")));
8344 if (rel->rd_rel->relispartition && !recursing)
8345 ereport(ERROR,
8346 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8347 errmsg("cannot change identity column of a partition"));
8349 foreach(option, castNode(List, def))
8351 DefElem *defel = lfirst_node(DefElem, option);
8353 if (strcmp(defel->defname, "generated") == 0)
8355 if (generatedEl)
8356 ereport(ERROR,
8357 (errcode(ERRCODE_SYNTAX_ERROR),
8358 errmsg("conflicting or redundant options")));
8359 generatedEl = defel;
8361 else
8362 elog(ERROR, "option \"%s\" not recognized",
8363 defel->defname);
8367 * Even if there is nothing to change here, we run all the checks. There
8368 * will be a subsequent ALTER SEQUENCE that relies on everything being
8369 * there.
8372 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8373 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8374 if (!HeapTupleIsValid(tuple))
8375 ereport(ERROR,
8376 (errcode(ERRCODE_UNDEFINED_COLUMN),
8377 errmsg("column \"%s\" of relation \"%s\" does not exist",
8378 colName, RelationGetRelationName(rel))));
8380 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8381 attnum = attTup->attnum;
8383 if (attnum <= 0)
8384 ereport(ERROR,
8385 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8386 errmsg("cannot alter system column \"%s\"",
8387 colName)));
8389 if (!attTup->attidentity)
8390 ereport(ERROR,
8391 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8392 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8393 colName, RelationGetRelationName(rel))));
8395 if (generatedEl)
8397 attTup->attidentity = defGetInt32(generatedEl);
8398 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8400 InvokeObjectPostAlterHook(RelationRelationId,
8401 RelationGetRelid(rel),
8402 attTup->attnum);
8403 ObjectAddressSubSet(address, RelationRelationId,
8404 RelationGetRelid(rel), attnum);
8406 else
8407 address = InvalidObjectAddress;
8409 heap_freetuple(tuple);
8410 table_close(attrelation, RowExclusiveLock);
8413 * Recurse to propagate the identity change to partitions. Identity is not
8414 * inherited in regular inheritance children.
8416 if (generatedEl && recurse && ispartitioned)
8418 List *children;
8419 ListCell *lc;
8421 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8423 foreach(lc, children)
8425 Relation childrel;
8427 childrel = table_open(lfirst_oid(lc), NoLock);
8428 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8429 table_close(childrel, NoLock);
8433 return address;
8437 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8439 * Return the address of the affected column.
8441 static ObjectAddress
8442 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8443 bool recurse, bool recursing)
8445 HeapTuple tuple;
8446 Form_pg_attribute attTup;
8447 AttrNumber attnum;
8448 Relation attrelation;
8449 ObjectAddress address;
8450 Oid seqid;
8451 ObjectAddress seqaddress;
8452 bool ispartitioned;
8454 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8455 if (ispartitioned && !recurse)
8456 ereport(ERROR,
8457 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8458 errmsg("cannot drop identity from a column of only the partitioned table"),
8459 errhint("Do not specify the ONLY keyword.")));
8461 if (rel->rd_rel->relispartition && !recursing)
8462 ereport(ERROR,
8463 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8464 errmsg("cannot drop identity from a column of a partition"));
8466 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8467 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8468 if (!HeapTupleIsValid(tuple))
8469 ereport(ERROR,
8470 (errcode(ERRCODE_UNDEFINED_COLUMN),
8471 errmsg("column \"%s\" of relation \"%s\" does not exist",
8472 colName, RelationGetRelationName(rel))));
8474 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8475 attnum = attTup->attnum;
8477 if (attnum <= 0)
8478 ereport(ERROR,
8479 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8480 errmsg("cannot alter system column \"%s\"",
8481 colName)));
8483 if (!attTup->attidentity)
8485 if (!missing_ok)
8486 ereport(ERROR,
8487 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8488 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8489 colName, RelationGetRelationName(rel))));
8490 else
8492 ereport(NOTICE,
8493 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8494 colName, RelationGetRelationName(rel))));
8495 heap_freetuple(tuple);
8496 table_close(attrelation, RowExclusiveLock);
8497 return InvalidObjectAddress;
8501 attTup->attidentity = '\0';
8502 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8504 InvokeObjectPostAlterHook(RelationRelationId,
8505 RelationGetRelid(rel),
8506 attTup->attnum);
8507 ObjectAddressSubSet(address, RelationRelationId,
8508 RelationGetRelid(rel), attnum);
8509 heap_freetuple(tuple);
8511 table_close(attrelation, RowExclusiveLock);
8514 * Recurse to drop the identity from column in partitions. Identity is
8515 * not inherited in regular inheritance children so ignore them.
8517 if (recurse && ispartitioned)
8519 List *children;
8520 ListCell *lc;
8522 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8524 foreach(lc, children)
8526 Relation childrel;
8528 childrel = table_open(lfirst_oid(lc), NoLock);
8529 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8530 table_close(childrel, NoLock);
8534 if (!recursing)
8536 /* drop the internal sequence */
8537 seqid = getIdentitySequence(rel, attnum, false);
8538 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8539 RelationRelationId, DEPENDENCY_INTERNAL);
8540 CommandCounterIncrement();
8541 seqaddress.classId = RelationRelationId;
8542 seqaddress.objectId = seqid;
8543 seqaddress.objectSubId = 0;
8544 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8547 return address;
8551 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8553 * Return the address of the affected column.
8555 static ObjectAddress
8556 ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8557 Node *newExpr, LOCKMODE lockmode)
8559 HeapTuple tuple;
8560 Form_pg_attribute attTup;
8561 AttrNumber attnum;
8562 Oid attrdefoid;
8563 ObjectAddress address;
8564 Expr *defval;
8565 NewColumnValue *newval;
8566 RawColumnDefault *rawEnt;
8568 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8569 if (!HeapTupleIsValid(tuple))
8570 ereport(ERROR,
8571 (errcode(ERRCODE_UNDEFINED_COLUMN),
8572 errmsg("column \"%s\" of relation \"%s\" does not exist",
8573 colName, RelationGetRelationName(rel))));
8575 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8576 attnum = attTup->attnum;
8578 if (attnum <= 0)
8579 ereport(ERROR,
8580 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8581 errmsg("cannot alter system column \"%s\"",
8582 colName)));
8584 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8585 ereport(ERROR,
8586 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8587 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8588 colName, RelationGetRelationName(rel))));
8589 ReleaseSysCache(tuple);
8592 * Clear all the missing values if we're rewriting the table, since this
8593 * renders them pointless.
8595 RelationClearMissing(rel);
8597 /* make sure we don't conflict with later attribute modifications */
8598 CommandCounterIncrement();
8601 * Find everything that depends on the column (constraints, indexes, etc),
8602 * and record enough information to let us recreate the objects after
8603 * rewrite.
8605 RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8608 * Drop the dependency records of the GENERATED expression, in particular
8609 * its INTERNAL dependency on the column, which would otherwise cause
8610 * dependency.c to refuse to perform the deletion.
8612 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8613 if (!OidIsValid(attrdefoid))
8614 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8615 RelationGetRelid(rel), attnum);
8616 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8618 /* Make above changes visible */
8619 CommandCounterIncrement();
8622 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8623 * safety, but at present we do not expect anything to depend on the
8624 * expression.
8626 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8627 false, false);
8629 /* Prepare to store the new expression, in the catalogs */
8630 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8631 rawEnt->attnum = attnum;
8632 rawEnt->raw_default = newExpr;
8633 rawEnt->missingMode = false;
8634 rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8636 /* Store the generated expression */
8637 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8638 false, true, false, NULL);
8640 /* Make above new expression visible */
8641 CommandCounterIncrement();
8643 /* Prepare for table rewrite */
8644 defval = (Expr *) build_column_default(rel, attnum);
8646 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8647 newval->attnum = attnum;
8648 newval->expr = expression_planner(defval);
8649 newval->is_generated = true;
8651 tab->newvals = lappend(tab->newvals, newval);
8652 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8654 /* Drop any pg_statistic entry for the column */
8655 RemoveStatistics(RelationGetRelid(rel), attnum);
8657 InvokeObjectPostAlterHook(RelationRelationId,
8658 RelationGetRelid(rel), attnum);
8660 ObjectAddressSubSet(address, RelationRelationId,
8661 RelationGetRelid(rel), attnum);
8662 return address;
8666 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8668 static void
8669 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8672 * Reject ONLY if there are child tables. We could implement this, but it
8673 * is a bit complicated. GENERATED clauses must be attached to the column
8674 * definition and cannot be added later like DEFAULT, so if a child table
8675 * has a generation expression that the parent does not have, the child
8676 * column will necessarily be an attislocal column. So to implement ONLY
8677 * here, we'd need extra code to update attislocal of the direct child
8678 * tables, somewhat similar to how DROP COLUMN does it, so that the
8679 * resulting state can be properly dumped and restored.
8681 if (!recurse &&
8682 find_inheritance_children(RelationGetRelid(rel), lockmode))
8683 ereport(ERROR,
8684 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8685 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8688 * Cannot drop generation expression from inherited columns.
8690 if (!recursing)
8692 HeapTuple tuple;
8693 Form_pg_attribute attTup;
8695 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8696 if (!HeapTupleIsValid(tuple))
8697 ereport(ERROR,
8698 (errcode(ERRCODE_UNDEFINED_COLUMN),
8699 errmsg("column \"%s\" of relation \"%s\" does not exist",
8700 cmd->name, RelationGetRelationName(rel))));
8702 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8704 if (attTup->attinhcount > 0)
8705 ereport(ERROR,
8706 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8707 errmsg("cannot drop generation expression from inherited column")));
8712 * Return the address of the affected column.
8714 static ObjectAddress
8715 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8717 HeapTuple tuple;
8718 Form_pg_attribute attTup;
8719 AttrNumber attnum;
8720 Relation attrelation;
8721 Oid attrdefoid;
8722 ObjectAddress address;
8724 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8725 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8726 if (!HeapTupleIsValid(tuple))
8727 ereport(ERROR,
8728 (errcode(ERRCODE_UNDEFINED_COLUMN),
8729 errmsg("column \"%s\" of relation \"%s\" does not exist",
8730 colName, RelationGetRelationName(rel))));
8732 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8733 attnum = attTup->attnum;
8735 if (attnum <= 0)
8736 ereport(ERROR,
8737 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8738 errmsg("cannot alter system column \"%s\"",
8739 colName)));
8741 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8743 if (!missing_ok)
8744 ereport(ERROR,
8745 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8746 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8747 colName, RelationGetRelationName(rel))));
8748 else
8750 ereport(NOTICE,
8751 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8752 colName, RelationGetRelationName(rel))));
8753 heap_freetuple(tuple);
8754 table_close(attrelation, RowExclusiveLock);
8755 return InvalidObjectAddress;
8760 * Mark the column as no longer generated. (The atthasdef flag needs to
8761 * get cleared too, but RemoveAttrDefault will handle that.)
8763 attTup->attgenerated = '\0';
8764 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8766 InvokeObjectPostAlterHook(RelationRelationId,
8767 RelationGetRelid(rel),
8768 attnum);
8769 heap_freetuple(tuple);
8771 table_close(attrelation, RowExclusiveLock);
8774 * Drop the dependency records of the GENERATED expression, in particular
8775 * its INTERNAL dependency on the column, which would otherwise cause
8776 * dependency.c to refuse to perform the deletion.
8778 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8779 if (!OidIsValid(attrdefoid))
8780 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8781 RelationGetRelid(rel), attnum);
8782 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8784 /* Make above changes visible */
8785 CommandCounterIncrement();
8788 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8789 * safety, but at present we do not expect anything to depend on the
8790 * default.
8792 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8793 false, false);
8795 ObjectAddressSubSet(address, RelationRelationId,
8796 RelationGetRelid(rel), attnum);
8797 return address;
8801 * ALTER TABLE ALTER COLUMN SET STATISTICS
8803 * Return value is the address of the modified column
8805 static ObjectAddress
8806 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8808 int newtarget = 0;
8809 bool newtarget_default;
8810 Relation attrelation;
8811 HeapTuple tuple,
8812 newtuple;
8813 Form_pg_attribute attrtuple;
8814 AttrNumber attnum;
8815 ObjectAddress address;
8816 Datum repl_val[Natts_pg_attribute];
8817 bool repl_null[Natts_pg_attribute];
8818 bool repl_repl[Natts_pg_attribute];
8821 * We allow referencing columns by numbers only for indexes, since table
8822 * column numbers could contain gaps if columns are later dropped.
8824 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8825 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8826 !colName)
8827 ereport(ERROR,
8828 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8829 errmsg("cannot refer to non-index column by number")));
8831 /* -1 was used in previous versions for the default setting */
8832 if (newValue && intVal(newValue) != -1)
8834 newtarget = intVal(newValue);
8835 newtarget_default = false;
8837 else
8838 newtarget_default = true;
8840 if (!newtarget_default)
8843 * Limit target to a sane range
8845 if (newtarget < 0)
8847 ereport(ERROR,
8848 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8849 errmsg("statistics target %d is too low",
8850 newtarget)));
8852 else if (newtarget > MAX_STATISTICS_TARGET)
8854 newtarget = MAX_STATISTICS_TARGET;
8855 ereport(WARNING,
8856 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8857 errmsg("lowering statistics target to %d",
8858 newtarget)));
8862 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8864 if (colName)
8866 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8868 if (!HeapTupleIsValid(tuple))
8869 ereport(ERROR,
8870 (errcode(ERRCODE_UNDEFINED_COLUMN),
8871 errmsg("column \"%s\" of relation \"%s\" does not exist",
8872 colName, RelationGetRelationName(rel))));
8874 else
8876 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8878 if (!HeapTupleIsValid(tuple))
8879 ereport(ERROR,
8880 (errcode(ERRCODE_UNDEFINED_COLUMN),
8881 errmsg("column number %d of relation \"%s\" does not exist",
8882 colNum, RelationGetRelationName(rel))));
8885 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8887 attnum = attrtuple->attnum;
8888 if (attnum <= 0)
8889 ereport(ERROR,
8890 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8891 errmsg("cannot alter system column \"%s\"",
8892 colName)));
8894 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8895 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8897 if (attnum > rel->rd_index->indnkeyatts)
8898 ereport(ERROR,
8899 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8900 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8901 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8902 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8903 ereport(ERROR,
8904 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8905 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8906 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8907 errhint("Alter statistics on table column instead.")));
8910 /* Build new tuple. */
8911 memset(repl_null, false, sizeof(repl_null));
8912 memset(repl_repl, false, sizeof(repl_repl));
8913 if (!newtarget_default)
8914 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8915 else
8916 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8917 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8918 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8919 repl_val, repl_null, repl_repl);
8920 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8922 InvokeObjectPostAlterHook(RelationRelationId,
8923 RelationGetRelid(rel),
8924 attrtuple->attnum);
8925 ObjectAddressSubSet(address, RelationRelationId,
8926 RelationGetRelid(rel), attnum);
8928 heap_freetuple(newtuple);
8930 ReleaseSysCache(tuple);
8932 table_close(attrelation, RowExclusiveLock);
8934 return address;
8938 * Return value is the address of the modified column
8940 static ObjectAddress
8941 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8942 bool isReset, LOCKMODE lockmode)
8944 Relation attrelation;
8945 HeapTuple tuple,
8946 newtuple;
8947 Form_pg_attribute attrtuple;
8948 AttrNumber attnum;
8949 Datum datum,
8950 newOptions;
8951 bool isnull;
8952 ObjectAddress address;
8953 Datum repl_val[Natts_pg_attribute];
8954 bool repl_null[Natts_pg_attribute];
8955 bool repl_repl[Natts_pg_attribute];
8957 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8959 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8961 if (!HeapTupleIsValid(tuple))
8962 ereport(ERROR,
8963 (errcode(ERRCODE_UNDEFINED_COLUMN),
8964 errmsg("column \"%s\" of relation \"%s\" does not exist",
8965 colName, RelationGetRelationName(rel))));
8966 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8968 attnum = attrtuple->attnum;
8969 if (attnum <= 0)
8970 ereport(ERROR,
8971 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8972 errmsg("cannot alter system column \"%s\"",
8973 colName)));
8975 /* Generate new proposed attoptions (text array) */
8976 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8977 &isnull);
8978 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8979 castNode(List, options), NULL, NULL,
8980 false, isReset);
8981 /* Validate new options */
8982 (void) attribute_reloptions(newOptions, true);
8984 /* Build new tuple. */
8985 memset(repl_null, false, sizeof(repl_null));
8986 memset(repl_repl, false, sizeof(repl_repl));
8987 if (newOptions != (Datum) 0)
8988 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8989 else
8990 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8991 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8992 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8993 repl_val, repl_null, repl_repl);
8995 /* Update system catalog. */
8996 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8998 InvokeObjectPostAlterHook(RelationRelationId,
8999 RelationGetRelid(rel),
9000 attrtuple->attnum);
9001 ObjectAddressSubSet(address, RelationRelationId,
9002 RelationGetRelid(rel), attnum);
9004 heap_freetuple(newtuple);
9006 ReleaseSysCache(tuple);
9008 table_close(attrelation, RowExclusiveLock);
9010 return address;
9014 * Helper function for ATExecSetStorage and ATExecSetCompression
9016 * Set the attstorage and/or attcompression fields for index columns
9017 * associated with the specified table column.
9019 static void
9020 SetIndexStorageProperties(Relation rel, Relation attrelation,
9021 AttrNumber attnum,
9022 bool setstorage, char newstorage,
9023 bool setcompression, char newcompression,
9024 LOCKMODE lockmode)
9026 ListCell *lc;
9028 foreach(lc, RelationGetIndexList(rel))
9030 Oid indexoid = lfirst_oid(lc);
9031 Relation indrel;
9032 AttrNumber indattnum = 0;
9033 HeapTuple tuple;
9035 indrel = index_open(indexoid, lockmode);
9037 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9039 if (indrel->rd_index->indkey.values[i] == attnum)
9041 indattnum = i + 1;
9042 break;
9046 if (indattnum == 0)
9048 index_close(indrel, lockmode);
9049 continue;
9052 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
9054 if (HeapTupleIsValid(tuple))
9056 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9058 if (setstorage)
9059 attrtuple->attstorage = newstorage;
9061 if (setcompression)
9062 attrtuple->attcompression = newcompression;
9064 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9066 InvokeObjectPostAlterHook(RelationRelationId,
9067 RelationGetRelid(rel),
9068 attrtuple->attnum);
9070 heap_freetuple(tuple);
9073 index_close(indrel, lockmode);
9078 * ALTER TABLE ALTER COLUMN SET STORAGE
9080 * Return value is the address of the modified column
9082 static ObjectAddress
9083 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
9085 Relation attrelation;
9086 HeapTuple tuple;
9087 Form_pg_attribute attrtuple;
9088 AttrNumber attnum;
9089 ObjectAddress address;
9091 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
9093 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
9095 if (!HeapTupleIsValid(tuple))
9096 ereport(ERROR,
9097 (errcode(ERRCODE_UNDEFINED_COLUMN),
9098 errmsg("column \"%s\" of relation \"%s\" does not exist",
9099 colName, RelationGetRelationName(rel))));
9100 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
9102 attnum = attrtuple->attnum;
9103 if (attnum <= 0)
9104 ereport(ERROR,
9105 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9106 errmsg("cannot alter system column \"%s\"",
9107 colName)));
9109 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9111 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9113 InvokeObjectPostAlterHook(RelationRelationId,
9114 RelationGetRelid(rel),
9115 attrtuple->attnum);
9118 * Apply the change to indexes as well (only for simple index columns,
9119 * matching behavior of index.c ConstructTupleDescriptor()).
9121 SetIndexStorageProperties(rel, attrelation, attnum,
9122 true, attrtuple->attstorage,
9123 false, 0,
9124 lockmode);
9126 heap_freetuple(tuple);
9128 table_close(attrelation, RowExclusiveLock);
9130 ObjectAddressSubSet(address, RelationRelationId,
9131 RelationGetRelid(rel), attnum);
9132 return address;
9137 * ALTER TABLE DROP COLUMN
9139 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
9140 * because we have to decide at runtime whether to recurse or not depending
9141 * on whether attinhcount goes to zero or not. (We can't check this in a
9142 * static pre-pass because it won't handle multiple inheritance situations
9143 * correctly.)
9145 static void
9146 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
9147 AlterTableCmd *cmd, LOCKMODE lockmode,
9148 AlterTableUtilityContext *context)
9150 if (rel->rd_rel->reloftype && !recursing)
9151 ereport(ERROR,
9152 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9153 errmsg("cannot drop column from typed table")));
9155 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9156 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9158 if (recurse)
9159 cmd->recurse = true;
9163 * Drops column 'colName' from relation 'rel' and returns the address of the
9164 * dropped column. The column is also dropped (or marked as no longer
9165 * inherited from relation) from the relation's inheritance children, if any.
9167 * In the recursive invocations for inheritance child relations, instead of
9168 * dropping the column directly (if to be dropped at all), its object address
9169 * is added to 'addrs', which must be non-NULL in such invocations. All
9170 * columns are dropped at the same time after all the children have been
9171 * checked recursively.
9173 static ObjectAddress
9174 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
9175 DropBehavior behavior,
9176 bool recurse, bool recursing,
9177 bool missing_ok, LOCKMODE lockmode,
9178 ObjectAddresses *addrs)
9180 HeapTuple tuple;
9181 Form_pg_attribute targetatt;
9182 AttrNumber attnum;
9183 List *children;
9184 ObjectAddress object;
9185 bool is_expr;
9187 /* At top level, permission check was done in ATPrepCmd, else do it */
9188 if (recursing)
9189 ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9191 /* Initialize addrs on the first invocation */
9192 Assert(!recursing || addrs != NULL);
9194 /* since this function recurses, it could be driven to stack overflow */
9195 check_stack_depth();
9197 if (!recursing)
9198 addrs = new_object_addresses();
9201 * get the number of the attribute
9203 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
9204 if (!HeapTupleIsValid(tuple))
9206 if (!missing_ok)
9208 ereport(ERROR,
9209 (errcode(ERRCODE_UNDEFINED_COLUMN),
9210 errmsg("column \"%s\" of relation \"%s\" does not exist",
9211 colName, RelationGetRelationName(rel))));
9213 else
9215 ereport(NOTICE,
9216 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9217 colName, RelationGetRelationName(rel))));
9218 return InvalidObjectAddress;
9221 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9223 attnum = targetatt->attnum;
9225 /* Can't drop a system attribute */
9226 if (attnum <= 0)
9227 ereport(ERROR,
9228 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9229 errmsg("cannot drop system column \"%s\"",
9230 colName)));
9233 * Don't drop inherited columns, unless recursing (presumably from a drop
9234 * of the parent column)
9236 if (targetatt->attinhcount > 0 && !recursing)
9237 ereport(ERROR,
9238 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9239 errmsg("cannot drop inherited column \"%s\"",
9240 colName)));
9243 * Don't drop columns used in the partition key, either. (If we let this
9244 * go through, the key column's dependencies would cause a cascaded drop
9245 * of the whole table, which is surely not what the user expected.)
9247 if (has_partition_attrs(rel,
9248 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9249 &is_expr))
9250 ereport(ERROR,
9251 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9252 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9253 colName, RelationGetRelationName(rel))));
9255 ReleaseSysCache(tuple);
9258 * Propagate to children as appropriate. Unlike most other ALTER
9259 * routines, we have to do this one level of recursion at a time; we can't
9260 * use find_all_inheritors to do it in one pass.
9262 children =
9263 find_inheritance_children(RelationGetRelid(rel), lockmode);
9265 if (children)
9267 Relation attr_rel;
9268 ListCell *child;
9271 * In case of a partitioned table, the column must be dropped from the
9272 * partitions as well.
9274 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9275 ereport(ERROR,
9276 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9277 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9278 errhint("Do not specify the ONLY keyword.")));
9280 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9281 foreach(child, children)
9283 Oid childrelid = lfirst_oid(child);
9284 Relation childrel;
9285 Form_pg_attribute childatt;
9287 /* find_inheritance_children already got lock */
9288 childrel = table_open(childrelid, NoLock);
9289 CheckTableNotInUse(childrel, "ALTER TABLE");
9291 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9292 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9293 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9294 colName, childrelid);
9295 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9297 if (childatt->attinhcount <= 0) /* shouldn't happen */
9298 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9299 childrelid, colName);
9301 if (recurse)
9304 * If the child column has other definition sources, just
9305 * decrement its inheritance count; if not, recurse to delete
9306 * it.
9308 if (childatt->attinhcount == 1 && !childatt->attislocal)
9310 /* Time to delete this child column, too */
9311 ATExecDropColumn(wqueue, childrel, colName,
9312 behavior, true, true,
9313 false, lockmode, addrs);
9315 else
9317 /* Child column must survive my deletion */
9318 childatt->attinhcount--;
9320 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9322 /* Make update visible */
9323 CommandCounterIncrement();
9326 else
9329 * If we were told to drop ONLY in this table (no recursion),
9330 * we need to mark the inheritors' attributes as locally
9331 * defined rather than inherited.
9333 childatt->attinhcount--;
9334 childatt->attislocal = true;
9336 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9338 /* Make update visible */
9339 CommandCounterIncrement();
9342 heap_freetuple(tuple);
9344 table_close(childrel, NoLock);
9346 table_close(attr_rel, RowExclusiveLock);
9349 /* Add object to delete */
9350 object.classId = RelationRelationId;
9351 object.objectId = RelationGetRelid(rel);
9352 object.objectSubId = attnum;
9353 add_exact_object_address(&object, addrs);
9355 if (!recursing)
9357 /* Recursion has ended, drop everything that was collected */
9358 performMultipleDeletions(addrs, behavior, 0);
9359 free_object_addresses(addrs);
9362 return object;
9366 * Prepare to add a primary key on an inheritance parent, by adding NOT NULL
9367 * constraint on its children.
9369 static void
9370 ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd,
9371 LOCKMODE lockmode, AlterTableUtilityContext *context)
9373 List *children;
9374 List *newconstrs = NIL;
9375 IndexStmt *indexstmt;
9377 /* No work if not creating a primary key */
9378 if (!IsA(cmd->def, IndexStmt))
9379 return;
9380 indexstmt = castNode(IndexStmt, cmd->def);
9381 if (!indexstmt->primary)
9382 return;
9384 /* No work if no legacy inheritance children are present */
9385 if (rel->rd_rel->relkind != RELKIND_RELATION ||
9386 !rel->rd_rel->relhassubclass)
9387 return;
9390 * Acquire locks all the way down the hierarchy. The recursion to lower
9391 * levels occurs at execution time as necessary, so we don't need to do it
9392 * here, and we don't need the returned list either.
9394 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
9397 * Construct the list of constraints that we need to add to each child
9398 * relation.
9400 foreach_node(IndexElem, elem, indexstmt->indexParams)
9402 Constraint *nnconstr;
9404 Assert(elem->expr == NULL);
9406 nnconstr = makeNode(Constraint);
9407 nnconstr->contype = CONSTR_NOTNULL;
9408 nnconstr->conname = NULL; /* XXX use PK name? */
9409 nnconstr->inhcount = 1;
9410 nnconstr->deferrable = false;
9411 nnconstr->initdeferred = false;
9412 nnconstr->location = -1;
9413 nnconstr->keys = list_make1(makeString(elem->name));
9414 nnconstr->skip_validation = false;
9415 nnconstr->initially_valid = true;
9417 newconstrs = lappend(newconstrs, nnconstr);
9420 /* Finally, add AT subcommands to add each constraint to each child. */
9421 children = find_inheritance_children(RelationGetRelid(rel), NoLock);
9422 foreach_oid(childrelid, children)
9424 Relation childrel = table_open(childrelid, NoLock);
9425 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
9426 ListCell *lc2;
9428 newcmd->subtype = AT_AddConstraint;
9429 newcmd->recurse = true;
9431 foreach(lc2, newconstrs)
9433 /* ATPrepCmd copies newcmd, so we can scribble on it here */
9434 newcmd->def = lfirst(lc2);
9436 ATPrepCmd(wqueue, childrel, newcmd,
9437 true, false, lockmode, context);
9440 table_close(childrel, NoLock);
9445 * ALTER TABLE ADD INDEX
9447 * There is no such command in the grammar, but parse_utilcmd.c converts
9448 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9449 * us schedule creation of the index at the appropriate time during ALTER.
9451 * Return value is the address of the new index.
9453 static ObjectAddress
9454 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9455 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9457 bool check_rights;
9458 bool skip_build;
9459 bool quiet;
9460 ObjectAddress address;
9462 Assert(IsA(stmt, IndexStmt));
9463 Assert(!stmt->concurrent);
9465 /* The IndexStmt has already been through transformIndexStmt */
9466 Assert(stmt->transformed);
9468 /* suppress schema rights check when rebuilding existing index */
9469 check_rights = !is_rebuild;
9470 /* skip index build if phase 3 will do it or we're reusing an old one */
9471 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9472 /* suppress notices when rebuilding existing index */
9473 quiet = is_rebuild;
9475 address = DefineIndex(RelationGetRelid(rel),
9476 stmt,
9477 InvalidOid, /* no predefined OID */
9478 InvalidOid, /* no parent index */
9479 InvalidOid, /* no parent constraint */
9480 -1, /* total_parts unknown */
9481 true, /* is_alter_table */
9482 check_rights,
9483 false, /* check_not_in_use - we did it already */
9484 skip_build,
9485 quiet);
9488 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9489 * new index instead of building from scratch. Restore associated fields.
9490 * This may store InvalidSubTransactionId in both fields, in which case
9491 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9492 * this after the CCI that made catalog rows visible to any rebuild. The
9493 * DROP of the old edition of this index will have scheduled the storage
9494 * for deletion at commit, so cancel that pending deletion.
9496 if (RelFileNumberIsValid(stmt->oldNumber))
9498 Relation irel = index_open(address.objectId, NoLock);
9500 irel->rd_createSubid = stmt->oldCreateSubid;
9501 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9502 RelationPreserveStorage(irel->rd_locator, true);
9503 index_close(irel, NoLock);
9506 return address;
9510 * ALTER TABLE ADD STATISTICS
9512 * This is no such command in the grammar, but we use this internally to add
9513 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9514 * column type change.
9516 static ObjectAddress
9517 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9518 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9520 ObjectAddress address;
9522 Assert(IsA(stmt, CreateStatsStmt));
9524 /* The CreateStatsStmt has already been through transformStatsStmt */
9525 Assert(stmt->transformed);
9527 address = CreateStatistics(stmt);
9529 return address;
9533 * ALTER TABLE ADD CONSTRAINT USING INDEX
9535 * Returns the address of the new constraint.
9537 static ObjectAddress
9538 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9539 IndexStmt *stmt, LOCKMODE lockmode)
9541 Oid index_oid = stmt->indexOid;
9542 Relation indexRel;
9543 char *indexName;
9544 IndexInfo *indexInfo;
9545 char *constraintName;
9546 char constraintType;
9547 ObjectAddress address;
9548 bits16 flags;
9550 Assert(IsA(stmt, IndexStmt));
9551 Assert(OidIsValid(index_oid));
9552 Assert(stmt->isconstraint);
9555 * Doing this on partitioned tables is not a simple feature to implement,
9556 * so let's punt for now.
9558 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9559 ereport(ERROR,
9560 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9561 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9563 indexRel = index_open(index_oid, AccessShareLock);
9565 indexName = pstrdup(RelationGetRelationName(indexRel));
9567 indexInfo = BuildIndexInfo(indexRel);
9569 /* this should have been checked at parse time */
9570 if (!indexInfo->ii_Unique)
9571 elog(ERROR, "index \"%s\" is not unique", indexName);
9574 * Determine name to assign to constraint. We require a constraint to
9575 * have the same name as the underlying index; therefore, use the index's
9576 * existing name as the default constraint name, and if the user
9577 * explicitly gives some other name for the constraint, rename the index
9578 * to match.
9580 constraintName = stmt->idxname;
9581 if (constraintName == NULL)
9582 constraintName = indexName;
9583 else if (strcmp(constraintName, indexName) != 0)
9585 ereport(NOTICE,
9586 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9587 indexName, constraintName)));
9588 RenameRelationInternal(index_oid, constraintName, false, true);
9591 /* Extra checks needed if making primary key */
9592 if (stmt->primary)
9593 index_check_primary_key(rel, indexInfo, true, stmt);
9595 /* Note we currently don't support EXCLUSION constraints here */
9596 if (stmt->primary)
9597 constraintType = CONSTRAINT_PRIMARY;
9598 else
9599 constraintType = CONSTRAINT_UNIQUE;
9601 /* Create the catalog entries for the constraint */
9602 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9603 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9604 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9605 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9606 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9608 address = index_constraint_create(rel,
9609 index_oid,
9610 InvalidOid,
9611 indexInfo,
9612 constraintName,
9613 constraintType,
9614 flags,
9615 allowSystemTableMods,
9616 false); /* is_internal */
9618 index_close(indexRel, NoLock);
9620 return address;
9624 * ALTER TABLE ADD CONSTRAINT
9626 * Return value is the address of the new constraint; if no constraint was
9627 * added, InvalidObjectAddress is returned.
9629 static ObjectAddress
9630 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9631 Constraint *newConstraint, bool recurse, bool is_readd,
9632 LOCKMODE lockmode)
9634 ObjectAddress address = InvalidObjectAddress;
9636 Assert(IsA(newConstraint, Constraint));
9639 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9640 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9641 * parse_utilcmd.c).
9643 switch (newConstraint->contype)
9645 case CONSTR_CHECK:
9646 case CONSTR_NOTNULL:
9647 address =
9648 ATAddCheckNNConstraint(wqueue, tab, rel,
9649 newConstraint, recurse, false, is_readd,
9650 lockmode);
9651 break;
9653 case CONSTR_FOREIGN:
9656 * Assign or validate constraint name
9658 if (newConstraint->conname)
9660 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9661 RelationGetRelid(rel),
9662 newConstraint->conname))
9663 ereport(ERROR,
9664 (errcode(ERRCODE_DUPLICATE_OBJECT),
9665 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9666 newConstraint->conname,
9667 RelationGetRelationName(rel))));
9669 else
9670 newConstraint->conname =
9671 ChooseConstraintName(RelationGetRelationName(rel),
9672 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9673 "fkey",
9674 RelationGetNamespace(rel),
9675 NIL);
9677 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9678 newConstraint,
9679 recurse, false,
9680 lockmode);
9681 break;
9683 default:
9684 elog(ERROR, "unrecognized constraint type: %d",
9685 (int) newConstraint->contype);
9688 return address;
9692 * Generate the column-name portion of the constraint name for a new foreign
9693 * key given the list of column names that reference the referenced
9694 * table. This will be passed to ChooseConstraintName along with the parent
9695 * table name and the "fkey" suffix.
9697 * We know that less than NAMEDATALEN characters will actually be used, so we
9698 * can truncate the result once we've generated that many.
9700 * XXX see also ChooseExtendedStatisticNameAddition and
9701 * ChooseIndexNameAddition.
9703 static char *
9704 ChooseForeignKeyConstraintNameAddition(List *colnames)
9706 char buf[NAMEDATALEN * 2];
9707 int buflen = 0;
9708 ListCell *lc;
9710 buf[0] = '\0';
9711 foreach(lc, colnames)
9713 const char *name = strVal(lfirst(lc));
9715 if (buflen > 0)
9716 buf[buflen++] = '_'; /* insert _ between names */
9719 * At this point we have buflen <= NAMEDATALEN. name should be less
9720 * than NAMEDATALEN already, but use strlcpy for paranoia.
9722 strlcpy(buf + buflen, name, NAMEDATALEN);
9723 buflen += strlen(buf + buflen);
9724 if (buflen >= NAMEDATALEN)
9725 break;
9727 return pstrdup(buf);
9731 * Add a check or not-null constraint to a single table and its children.
9732 * Returns the address of the constraint added to the parent relation,
9733 * if one gets added, or InvalidObjectAddress otherwise.
9735 * Subroutine for ATExecAddConstraint.
9737 * We must recurse to child tables during execution, rather than using
9738 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9739 * constraints *must* be given the same name, else they won't be seen as
9740 * related later. If the user didn't explicitly specify a name, then
9741 * AddRelationNewConstraints would normally assign different names to the
9742 * child constraints. To fix that, we must capture the name assigned at
9743 * the parent table and pass that down.
9745 static ObjectAddress
9746 ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9747 Constraint *constr, bool recurse, bool recursing,
9748 bool is_readd, LOCKMODE lockmode)
9750 List *newcons;
9751 ListCell *lcon;
9752 List *children;
9753 ListCell *child;
9754 ObjectAddress address = InvalidObjectAddress;
9756 /* Guard against stack overflow due to overly deep inheritance tree. */
9757 check_stack_depth();
9759 /* At top level, permission check was done in ATPrepCmd, else do it */
9760 if (recursing)
9761 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9764 * Call AddRelationNewConstraints to do the work, making sure it works on
9765 * a copy of the Constraint so transformExpr can't modify the original. It
9766 * returns a list of cooked constraints.
9768 * If the constraint ends up getting merged with a pre-existing one, it's
9769 * omitted from the returned list, which is what we want: we do not need
9770 * to do any validation work. That can only happen at child tables,
9771 * though, since we disallow merging at the top level.
9773 newcons = AddRelationNewConstraints(rel, NIL,
9774 list_make1(copyObject(constr)),
9775 recursing || is_readd, /* allow_merge */
9776 !recursing, /* is_local */
9777 is_readd, /* is_internal */
9778 NULL); /* queryString not available
9779 * here */
9781 /* we don't expect more than one constraint here */
9782 Assert(list_length(newcons) <= 1);
9784 /* Add each to-be-validated constraint to Phase 3's queue */
9785 foreach(lcon, newcons)
9787 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9789 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
9791 NewConstraint *newcon;
9793 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9794 newcon->name = ccon->name;
9795 newcon->contype = ccon->contype;
9796 newcon->qual = ccon->expr;
9798 tab->constraints = lappend(tab->constraints, newcon);
9801 /* Save the actually assigned name if it was defaulted */
9802 if (constr->conname == NULL)
9803 constr->conname = ccon->name;
9806 * If adding a not-null constraint, set the pg_attribute flag and tell
9807 * phase 3 to verify existing rows, if needed.
9809 if (constr->contype == CONSTR_NOTNULL)
9810 set_attnotnull(wqueue, rel, ccon->attnum,
9811 !ccon->is_no_inherit, lockmode);
9813 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9816 /* At this point we must have a locked-down name to use */
9817 Assert(newcons == NIL || constr->conname != NULL);
9819 /* Advance command counter in case same table is visited multiple times */
9820 CommandCounterIncrement();
9823 * If the constraint got merged with an existing constraint, we're done.
9824 * We mustn't recurse to child tables in this case, because they've
9825 * already got the constraint, and visiting them again would lead to an
9826 * incorrect value for coninhcount.
9828 if (newcons == NIL)
9829 return address;
9832 * If adding a NO INHERIT constraint, no need to find our children.
9834 if (constr->is_no_inherit)
9835 return address;
9838 * Propagate to children as appropriate. Unlike most other ALTER
9839 * routines, we have to do this one level of recursion at a time; we can't
9840 * use find_all_inheritors to do it in one pass.
9842 children =
9843 find_inheritance_children(RelationGetRelid(rel), lockmode);
9846 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9847 * constraint creation only if there are no children currently. Error out
9848 * otherwise.
9850 if (!recurse && children != NIL)
9851 ereport(ERROR,
9852 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9853 errmsg("constraint must be added to child tables too")));
9856 * The constraint must appear as inherited in children, so create a
9857 * modified constraint object to use.
9859 constr = copyObject(constr);
9860 constr->inhcount = 1;
9861 foreach(child, children)
9863 Oid childrelid = lfirst_oid(child);
9864 Relation childrel;
9865 AlteredTableInfo *childtab;
9867 /* find_inheritance_children already got lock */
9868 childrel = table_open(childrelid, NoLock);
9869 CheckTableNotInUse(childrel, "ALTER TABLE");
9871 /* Find or create work queue entry for this table */
9872 childtab = ATGetQueueEntry(wqueue, childrel);
9875 * Recurse to child. XXX if we didn't create a constraint on the
9876 * parent because it already existed, and we do create one on a child,
9877 * should we return that child's constraint ObjectAddress here?
9879 ATAddCheckNNConstraint(wqueue, childtab, childrel,
9880 constr, recurse, true, is_readd, lockmode);
9882 table_close(childrel, NoLock);
9885 return address;
9889 * Add a foreign-key constraint to a single table; return the new constraint's
9890 * address.
9892 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9893 * lock on the rel, and have done appropriate validity checks for it.
9894 * We do permissions checks here, however.
9896 * When the referenced or referencing tables (or both) are partitioned,
9897 * multiple pg_constraint rows are required -- one for each partitioned table
9898 * and each partition on each side (fortunately, not one for every combination
9899 * thereof). We also need action triggers on each leaf partition on the
9900 * referenced side, and check triggers on each leaf partition on the
9901 * referencing side.
9903 static ObjectAddress
9904 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9905 Constraint *fkconstraint,
9906 bool recurse, bool recursing, LOCKMODE lockmode)
9908 Relation pkrel;
9909 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9910 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9911 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9912 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9913 Oid opclasses[INDEX_MAX_KEYS] = {0};
9914 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9915 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9916 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9917 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9918 bool with_period;
9919 bool pk_has_without_overlaps;
9920 int i;
9921 int numfks,
9922 numpks,
9923 numfkdelsetcols;
9924 Oid indexOid;
9925 bool old_check_ok;
9926 ObjectAddress address;
9927 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9930 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9931 * delete rows out from under us.
9933 if (OidIsValid(fkconstraint->old_pktable_oid))
9934 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9935 else
9936 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9939 * Validity checks (permission checks wait till we have the column
9940 * numbers)
9942 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9944 if (!recurse)
9945 ereport(ERROR,
9946 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9947 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9948 RelationGetRelationName(rel),
9949 RelationGetRelationName(pkrel))));
9950 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9951 ereport(ERROR,
9952 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9953 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9954 RelationGetRelationName(rel),
9955 RelationGetRelationName(pkrel)),
9956 errdetail("This feature is not yet supported on partitioned tables.")));
9959 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9960 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9961 ereport(ERROR,
9962 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9963 errmsg("referenced relation \"%s\" is not a table",
9964 RelationGetRelationName(pkrel))));
9966 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9967 ereport(ERROR,
9968 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9969 errmsg("permission denied: \"%s\" is a system catalog",
9970 RelationGetRelationName(pkrel))));
9973 * References from permanent or unlogged tables to temp tables, and from
9974 * permanent tables to unlogged tables, are disallowed because the
9975 * referenced data can vanish out from under us. References from temp
9976 * tables to any other table type are also disallowed, because other
9977 * backends might need to run the RI triggers on the perm table, but they
9978 * can't reliably see tuples in the local buffers of other backends.
9980 switch (rel->rd_rel->relpersistence)
9982 case RELPERSISTENCE_PERMANENT:
9983 if (!RelationIsPermanent(pkrel))
9984 ereport(ERROR,
9985 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9986 errmsg("constraints on permanent tables may reference only permanent tables")));
9987 break;
9988 case RELPERSISTENCE_UNLOGGED:
9989 if (!RelationIsPermanent(pkrel)
9990 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9991 ereport(ERROR,
9992 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9993 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9994 break;
9995 case RELPERSISTENCE_TEMP:
9996 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9997 ereport(ERROR,
9998 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9999 errmsg("constraints on temporary tables may reference only temporary tables")));
10000 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10001 ereport(ERROR,
10002 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
10003 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10004 break;
10008 * Look up the referencing attributes to make sure they exist, and record
10009 * their attnums and type OIDs.
10011 numfks = transformColumnNameList(RelationGetRelid(rel),
10012 fkconstraint->fk_attrs,
10013 fkattnum, fktypoid);
10014 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10015 if (with_period && !fkconstraint->fk_with_period)
10016 ereport(ERROR,
10017 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10018 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10020 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
10021 fkconstraint->fk_del_set_cols,
10022 fkdelsetcols, NULL);
10023 validateFkOnDeleteSetColumns(numfks, fkattnum,
10024 numfkdelsetcols, fkdelsetcols,
10025 fkconstraint->fk_del_set_cols);
10028 * If the attribute list for the referenced table was omitted, lookup the
10029 * definition of the primary key and use it. Otherwise, validate the
10030 * supplied attribute list. In either case, discover the index OID and
10031 * index opclasses, and the attnums and type OIDs of the attributes.
10033 if (fkconstraint->pk_attrs == NIL)
10035 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
10036 &fkconstraint->pk_attrs,
10037 pkattnum, pktypoid,
10038 opclasses, &pk_has_without_overlaps);
10040 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10041 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10042 ereport(ERROR,
10043 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10044 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10046 else
10048 numpks = transformColumnNameList(RelationGetRelid(pkrel),
10049 fkconstraint->pk_attrs,
10050 pkattnum, pktypoid);
10052 /* Since we got pk_attrs, one should be a period. */
10053 if (with_period && !fkconstraint->pk_with_period)
10054 ereport(ERROR,
10055 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10056 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10058 /* Look for an index matching the column list */
10059 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
10060 with_period, opclasses, &pk_has_without_overlaps);
10064 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10065 * must use PERIOD.
10067 if (pk_has_without_overlaps && !with_period)
10068 ereport(ERROR,
10069 errcode(ERRCODE_INVALID_FOREIGN_KEY),
10070 errmsg("foreign key must use PERIOD when referencing a primary using WITHOUT OVERLAPS"));
10073 * Now we can check permissions.
10075 checkFkeyPermissions(pkrel, pkattnum, numpks);
10078 * Check some things for generated columns.
10080 for (i = 0; i < numfks; i++)
10082 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10084 if (attgenerated)
10087 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10089 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10090 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10091 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10092 ereport(ERROR,
10093 (errcode(ERRCODE_SYNTAX_ERROR),
10094 errmsg("invalid %s action for foreign key constraint containing generated column",
10095 "ON UPDATE")));
10096 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10097 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10098 ereport(ERROR,
10099 (errcode(ERRCODE_SYNTAX_ERROR),
10100 errmsg("invalid %s action for foreign key constraint containing generated column",
10101 "ON DELETE")));
10106 * Some actions are currently unsupported for foreign keys using PERIOD.
10108 if (fkconstraint->fk_with_period)
10110 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10111 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10112 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10113 ereport(ERROR,
10114 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10115 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10116 "ON UPDATE"));
10118 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10119 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10120 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10121 ereport(ERROR,
10122 errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
10123 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10124 "ON DELETE"));
10128 * Look up the equality operators to use in the constraint.
10130 * Note that we have to be careful about the difference between the actual
10131 * PK column type and the opclass' declared input type, which might be
10132 * only binary-compatible with it. The declared opcintype is the right
10133 * thing to probe pg_amop with.
10135 if (numfks != numpks)
10136 ereport(ERROR,
10137 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
10138 errmsg("number of referencing and referenced columns for foreign key disagree")));
10141 * On the strength of a previous constraint, we might avoid scanning
10142 * tables to validate this one. See below.
10144 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10145 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10147 for (i = 0; i < numpks; i++)
10149 Oid pktype = pktypoid[i];
10150 Oid fktype = fktypoid[i];
10151 Oid fktyped;
10152 HeapTuple cla_ht;
10153 Form_pg_opclass cla_tup;
10154 Oid amid;
10155 Oid opfamily;
10156 Oid opcintype;
10157 Oid pfeqop;
10158 Oid ppeqop;
10159 Oid ffeqop;
10160 int16 eqstrategy;
10161 Oid pfeqop_right;
10163 /* We need several fields out of the pg_opclass entry */
10164 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10165 if (!HeapTupleIsValid(cla_ht))
10166 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10167 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
10168 amid = cla_tup->opcmethod;
10169 opfamily = cla_tup->opcfamily;
10170 opcintype = cla_tup->opcintype;
10171 ReleaseSysCache(cla_ht);
10173 if (with_period)
10175 StrategyNumber rtstrategy;
10176 bool for_overlaps = with_period && i == numpks - 1;
10179 * GiST indexes are required to support temporal foreign keys
10180 * because they combine equals and overlaps.
10182 if (amid != GIST_AM_OID)
10183 elog(ERROR, "only GiST indexes are supported for temporal foreign keys");
10185 rtstrategy = for_overlaps ? RTOverlapStrategyNumber : RTEqualStrategyNumber;
10188 * An opclass can use whatever strategy numbers it wants, so we
10189 * ask the opclass what number it actually uses instead of our RT*
10190 * constants.
10192 eqstrategy = GistTranslateStratnum(opclasses[i], rtstrategy);
10193 if (eqstrategy == InvalidStrategy)
10195 HeapTuple tuple;
10197 tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
10198 if (!HeapTupleIsValid(tuple))
10199 elog(ERROR, "cache lookup failed for operator class %u", opclasses[i]);
10201 ereport(ERROR,
10202 errcode(ERRCODE_UNDEFINED_OBJECT),
10203 for_overlaps
10204 ? errmsg("could not identify an overlaps operator for foreign key")
10205 : errmsg("could not identify an equality operator for foreign key"),
10206 errdetail("Could not translate strategy number %d for operator class \"%s\" for access method \"%s\".",
10207 rtstrategy, NameStr(((Form_pg_opclass) GETSTRUCT(tuple))->opcname), "gist"));
10210 else
10213 * Check it's a btree; currently this can never fail since no
10214 * other index AMs support unique indexes. If we ever did have
10215 * other types of unique indexes, we'd need a way to determine
10216 * which operator strategy number is equality. (We could use
10217 * something like GistTranslateStratnum.)
10219 if (amid != BTREE_AM_OID)
10220 elog(ERROR, "only b-tree indexes are supported for foreign keys");
10221 eqstrategy = BTEqualStrategyNumber;
10225 * There had better be a primary equality operator for the index.
10226 * We'll use it for PK = PK comparisons.
10228 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10229 eqstrategy);
10231 if (!OidIsValid(ppeqop))
10232 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10233 eqstrategy, opcintype, opcintype, opfamily);
10236 * Are there equality operators that take exactly the FK type? Assume
10237 * we should look through any domain here.
10239 fktyped = getBaseType(fktype);
10241 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10242 eqstrategy);
10243 if (OidIsValid(pfeqop))
10245 pfeqop_right = fktyped;
10246 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
10247 eqstrategy);
10249 else
10251 /* keep compiler quiet */
10252 pfeqop_right = InvalidOid;
10253 ffeqop = InvalidOid;
10256 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10259 * Otherwise, look for an implicit cast from the FK type to the
10260 * opcintype, and if found, use the primary equality operator.
10261 * This is a bit tricky because opcintype might be a polymorphic
10262 * type such as ANYARRAY or ANYENUM; so what we have to test is
10263 * whether the two actual column types can be concurrently cast to
10264 * that type. (Otherwise, we'd fail to reject combinations such
10265 * as int[] and point[].)
10267 Oid input_typeids[2];
10268 Oid target_typeids[2];
10270 input_typeids[0] = pktype;
10271 input_typeids[1] = fktype;
10272 target_typeids[0] = opcintype;
10273 target_typeids[1] = opcintype;
10274 if (can_coerce_type(2, input_typeids, target_typeids,
10275 COERCION_IMPLICIT))
10277 pfeqop = ffeqop = ppeqop;
10278 pfeqop_right = opcintype;
10282 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10283 ereport(ERROR,
10284 (errcode(ERRCODE_DATATYPE_MISMATCH),
10285 errmsg("foreign key constraint \"%s\" cannot be implemented",
10286 fkconstraint->conname),
10287 errdetail("Key columns \"%s\" and \"%s\" "
10288 "are of incompatible types: %s and %s.",
10289 strVal(list_nth(fkconstraint->fk_attrs, i)),
10290 strVal(list_nth(fkconstraint->pk_attrs, i)),
10291 format_type_be(fktype),
10292 format_type_be(pktype))));
10294 if (old_check_ok)
10297 * When a pfeqop changes, revalidate the constraint. We could
10298 * permit intra-opfamily changes, but that adds subtle complexity
10299 * without any concrete benefit for core types. We need not
10300 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10302 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
10303 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10304 old_pfeqop_item);
10306 if (old_check_ok)
10308 Oid old_fktype;
10309 Oid new_fktype;
10310 CoercionPathType old_pathtype;
10311 CoercionPathType new_pathtype;
10312 Oid old_castfunc;
10313 Oid new_castfunc;
10314 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
10315 fkattnum[i] - 1);
10318 * Identify coercion pathways from each of the old and new FK-side
10319 * column types to the right (foreign) operand type of the pfeqop.
10320 * We may assume that pg_constraint.conkey is not changing.
10322 old_fktype = attr->atttypid;
10323 new_fktype = fktype;
10324 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
10325 &old_castfunc);
10326 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
10327 &new_castfunc);
10330 * Upon a change to the cast from the FK column to its pfeqop
10331 * operand, revalidate the constraint. For this evaluation, a
10332 * binary coercion cast is equivalent to no cast at all. While
10333 * type implementors should design implicit casts with an eye
10334 * toward consistency of operations like equality, we cannot
10335 * assume here that they have done so.
10337 * A function with a polymorphic argument could change behavior
10338 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10339 * when the cast destination is polymorphic, we only avoid
10340 * revalidation if the input type has not changed at all. Given
10341 * just the core data types and operator classes, this requirement
10342 * prevents no would-be optimizations.
10344 * If the cast converts from a base type to a domain thereon, then
10345 * that domain type must be the opcintype of the unique index.
10346 * Necessarily, the primary key column must then be of the domain
10347 * type. Since the constraint was previously valid, all values on
10348 * the foreign side necessarily exist on the primary side and in
10349 * turn conform to the domain. Consequently, we need not treat
10350 * domains specially here.
10352 * Since we require that all collations share the same notion of
10353 * equality (which they do, because texteq reduces to bitwise
10354 * equality), we don't compare collation here.
10356 * We need not directly consider the PK type. It's necessarily
10357 * binary coercible to the opcintype of the unique index column,
10358 * and ri_triggers.c will only deal with PK datums in terms of
10359 * that opcintype. Changing the opcintype also changes pfeqop.
10361 old_check_ok = (new_pathtype == old_pathtype &&
10362 new_castfunc == old_castfunc &&
10363 (!IsPolymorphicType(pfeqop_right) ||
10364 new_fktype == old_fktype));
10367 pfeqoperators[i] = pfeqop;
10368 ppeqoperators[i] = ppeqop;
10369 ffeqoperators[i] = ffeqop;
10373 * For FKs with PERIOD we need additional operators to check whether the
10374 * referencing row's range is contained by the aggregated ranges of the
10375 * referenced row(s). For rangetypes and multirangetypes this is
10376 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10377 * support for now. FKs will look these up at "runtime", but we should
10378 * make sure the lookup works here, even if we don't use the values.
10380 if (with_period)
10382 Oid periodoperoid;
10383 Oid aggedperiodoperoid;
10385 FindFKPeriodOpers(opclasses[numpks - 1], &periodoperoid, &aggedperiodoperoid);
10389 * Create all the constraint and trigger objects, recursing to partitions
10390 * as necessary. First handle the referenced side.
10392 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
10393 indexOid,
10394 InvalidOid, /* no parent constraint */
10395 numfks,
10396 pkattnum,
10397 fkattnum,
10398 pfeqoperators,
10399 ppeqoperators,
10400 ffeqoperators,
10401 numfkdelsetcols,
10402 fkdelsetcols,
10403 old_check_ok,
10404 InvalidOid, InvalidOid,
10405 with_period);
10407 /* Now handle the referencing side. */
10408 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
10409 indexOid,
10410 address.objectId,
10411 numfks,
10412 pkattnum,
10413 fkattnum,
10414 pfeqoperators,
10415 ppeqoperators,
10416 ffeqoperators,
10417 numfkdelsetcols,
10418 fkdelsetcols,
10419 old_check_ok,
10420 lockmode,
10421 InvalidOid, InvalidOid,
10422 with_period);
10425 * Done. Close pk table, but keep lock until we've committed.
10427 table_close(pkrel, NoLock);
10429 return address;
10433 * validateFkOnDeleteSetColumns
10434 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
10435 * column lists are valid.
10437 void
10438 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10439 int numfksetcols, const int16 *fksetcolsattnums,
10440 List *fksetcols)
10442 for (int i = 0; i < numfksetcols; i++)
10444 int16 setcol_attnum = fksetcolsattnums[i];
10445 bool seen = false;
10447 for (int j = 0; j < numfks; j++)
10449 if (fkattnums[j] == setcol_attnum)
10451 seen = true;
10452 break;
10456 if (!seen)
10458 char *col = strVal(list_nth(fksetcols, i));
10460 ereport(ERROR,
10461 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10462 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10468 * addFkRecurseReferenced
10469 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10470 * side of the constraint
10472 * Create pg_constraint rows for the referenced side of the constraint,
10473 * referencing the parent of the referencing side; also create action triggers
10474 * on leaf partitions. If the table is partitioned, recurse to handle each
10475 * partition.
10477 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10478 * of an ALTER TABLE sequence.
10479 * fkconstraint is the constraint being added.
10480 * rel is the root referencing relation.
10481 * pkrel is the referenced relation; might be a partition, if recursing.
10482 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10483 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10484 * top-level constraint.
10485 * numfks is the number of columns in the foreign key
10486 * pkattnum is the attnum array of referenced attributes.
10487 * fkattnum is the attnum array of referencing attributes.
10488 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10489 * (...) clause
10490 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10491 * NULL/DEFAULT clause
10492 * pf/pp/ffeqoperators are OID array of operators between columns.
10493 * old_check_ok signals that this constraint replaces an existing one that
10494 * was already validated (thus this one doesn't need validation).
10495 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10496 * a partition, are the OIDs of the parent action triggers for DELETE and
10497 * UPDATE respectively.
10499 static ObjectAddress
10500 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10501 Relation pkrel, Oid indexOid, Oid parentConstr,
10502 int numfks,
10503 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10504 Oid *ppeqoperators, Oid *ffeqoperators,
10505 int numfkdelsetcols, int16 *fkdelsetcols,
10506 bool old_check_ok,
10507 Oid parentDelTrigger, Oid parentUpdTrigger,
10508 bool with_period)
10510 ObjectAddress address;
10511 Oid constrOid;
10512 char *conname;
10513 bool conislocal;
10514 int coninhcount;
10515 bool connoinherit;
10516 Oid deleteTriggerOid,
10517 updateTriggerOid;
10520 * Verify relkind for each referenced partition. At the top level, this
10521 * is redundant with a previous check, but we need it when recursing.
10523 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10524 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10525 ereport(ERROR,
10526 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10527 errmsg("referenced relation \"%s\" is not a table",
10528 RelationGetRelationName(pkrel))));
10531 * Caller supplies us with a constraint name; however, it may be used in
10532 * this partition, so come up with a different one in that case.
10534 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10535 RelationGetRelid(rel),
10536 fkconstraint->conname))
10537 conname = ChooseConstraintName(RelationGetRelationName(rel),
10538 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10539 "fkey",
10540 RelationGetNamespace(rel), NIL);
10541 else
10542 conname = fkconstraint->conname;
10544 if (OidIsValid(parentConstr))
10546 conislocal = false;
10547 coninhcount = 1;
10548 connoinherit = false;
10550 else
10552 conislocal = true;
10553 coninhcount = 0;
10556 * always inherit for partitioned tables, never for legacy inheritance
10558 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10562 * Record the FK constraint in pg_constraint.
10564 constrOid = CreateConstraintEntry(conname,
10565 RelationGetNamespace(rel),
10566 CONSTRAINT_FOREIGN,
10567 fkconstraint->deferrable,
10568 fkconstraint->initdeferred,
10569 fkconstraint->initially_valid,
10570 parentConstr,
10571 RelationGetRelid(rel),
10572 fkattnum,
10573 numfks,
10574 numfks,
10575 InvalidOid, /* not a domain constraint */
10576 indexOid,
10577 RelationGetRelid(pkrel),
10578 pkattnum,
10579 pfeqoperators,
10580 ppeqoperators,
10581 ffeqoperators,
10582 numfks,
10583 fkconstraint->fk_upd_action,
10584 fkconstraint->fk_del_action,
10585 fkdelsetcols,
10586 numfkdelsetcols,
10587 fkconstraint->fk_matchtype,
10588 NULL, /* no exclusion constraint */
10589 NULL, /* no check constraint */
10590 NULL,
10591 conislocal, /* islocal */
10592 coninhcount, /* inhcount */
10593 connoinherit, /* conNoInherit */
10594 with_period, /* conPeriod */
10595 false); /* is_internal */
10597 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10600 * Mark the child constraint as part of the parent constraint; it must not
10601 * be dropped on its own. (This constraint is deleted when the partition
10602 * is detached, but a special check needs to occur that the partition
10603 * contains no referenced values.)
10605 if (OidIsValid(parentConstr))
10607 ObjectAddress referenced;
10609 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10610 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10613 /* make new constraint visible, in case we add more */
10614 CommandCounterIncrement();
10617 * Create the action triggers that enforce the constraint.
10619 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10620 fkconstraint,
10621 constrOid, indexOid,
10622 parentDelTrigger, parentUpdTrigger,
10623 &deleteTriggerOid, &updateTriggerOid);
10626 * If the referenced table is partitioned, recurse on ourselves to handle
10627 * each partition. We need one pg_constraint row created for each
10628 * partition in addition to the pg_constraint row for the parent table.
10630 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10632 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10634 for (int i = 0; i < pd->nparts; i++)
10636 Relation partRel;
10637 AttrMap *map;
10638 AttrNumber *mapped_pkattnum;
10639 Oid partIndexId;
10641 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10644 * Map the attribute numbers in the referenced side of the FK
10645 * definition to match the partition's column layout.
10647 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10648 RelationGetDescr(pkrel),
10649 false);
10650 if (map)
10652 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10653 for (int j = 0; j < numfks; j++)
10654 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10656 else
10657 mapped_pkattnum = pkattnum;
10659 /* do the deed */
10660 partIndexId = index_get_partition(partRel, indexOid);
10661 if (!OidIsValid(partIndexId))
10662 elog(ERROR, "index for %u not found in partition %s",
10663 indexOid, RelationGetRelationName(partRel));
10664 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10665 partIndexId, constrOid, numfks,
10666 mapped_pkattnum, fkattnum,
10667 pfeqoperators, ppeqoperators, ffeqoperators,
10668 numfkdelsetcols, fkdelsetcols,
10669 old_check_ok,
10670 deleteTriggerOid, updateTriggerOid,
10671 with_period);
10673 /* Done -- clean up (but keep the lock) */
10674 table_close(partRel, NoLock);
10675 if (map)
10677 pfree(mapped_pkattnum);
10678 free_attrmap(map);
10683 return address;
10687 * addFkRecurseReferencing
10688 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10690 * If the referencing relation is a plain relation, create the necessary check
10691 * triggers that implement the constraint, and set up for Phase 3 constraint
10692 * verification. If the referencing relation is a partitioned table, then
10693 * we create a pg_constraint row for it and recurse on this routine for each
10694 * partition.
10696 * We assume that the referenced relation is locked against concurrent
10697 * deletions. If it's a partitioned relation, every partition must be so
10698 * locked.
10700 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10701 * of an ALTER TABLE sequence.
10702 * fkconstraint is the constraint being added.
10703 * rel is the referencing relation; might be a partition, if recursing.
10704 * pkrel is the root referenced relation.
10705 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10706 * parentConstr is the OID of the parent constraint (there is always one).
10707 * numfks is the number of columns in the foreign key
10708 * pkattnum is the attnum array of referenced attributes.
10709 * fkattnum is the attnum array of referencing attributes.
10710 * pf/pp/ffeqoperators are OID array of operators between columns.
10711 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10712 * (...) clause
10713 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10714 * NULL/DEFAULT clause
10715 * old_check_ok signals that this constraint replaces an existing one that
10716 * was already validated (thus this one doesn't need validation).
10717 * lockmode is the lockmode to acquire on partitions when recursing.
10718 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10719 * a partition, are the OIDs of the parent check triggers for INSERT and
10720 * UPDATE respectively.
10722 static void
10723 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10724 Relation pkrel, Oid indexOid, Oid parentConstr,
10725 int numfks, int16 *pkattnum, int16 *fkattnum,
10726 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10727 int numfkdelsetcols, int16 *fkdelsetcols,
10728 bool old_check_ok, LOCKMODE lockmode,
10729 Oid parentInsTrigger, Oid parentUpdTrigger,
10730 bool with_period)
10732 Oid insertTriggerOid,
10733 updateTriggerOid;
10735 Assert(OidIsValid(parentConstr));
10737 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10738 ereport(ERROR,
10739 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10740 errmsg("foreign key constraints are not supported on foreign tables")));
10743 * Add the check triggers to it and, if necessary, schedule it to be
10744 * checked in Phase 3.
10746 * If the relation is partitioned, drill down to do it to its partitions.
10748 createForeignKeyCheckTriggers(RelationGetRelid(rel),
10749 RelationGetRelid(pkrel),
10750 fkconstraint,
10751 parentConstr,
10752 indexOid,
10753 parentInsTrigger, parentUpdTrigger,
10754 &insertTriggerOid, &updateTriggerOid);
10756 if (rel->rd_rel->relkind == RELKIND_RELATION)
10759 * Tell Phase 3 to check that the constraint is satisfied by existing
10760 * rows. We can skip this during table creation, when requested
10761 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10762 * and when we're recreating a constraint following a SET DATA TYPE
10763 * operation that did not impugn its validity.
10765 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10767 NewConstraint *newcon;
10768 AlteredTableInfo *tab;
10770 tab = ATGetQueueEntry(wqueue, rel);
10772 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10773 newcon->name = get_constraint_name(parentConstr);
10774 newcon->contype = CONSTR_FOREIGN;
10775 newcon->refrelid = RelationGetRelid(pkrel);
10776 newcon->refindid = indexOid;
10777 newcon->conid = parentConstr;
10778 newcon->conwithperiod = fkconstraint->fk_with_period;
10779 newcon->qual = (Node *) fkconstraint;
10781 tab->constraints = lappend(tab->constraints, newcon);
10784 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10786 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10787 Relation trigrel;
10790 * Triggers of the foreign keys will be manipulated a bunch of times
10791 * in the loop below. To avoid repeatedly opening/closing the trigger
10792 * catalog relation, we open it here and pass it to the subroutines
10793 * called below.
10795 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10798 * Recurse to take appropriate action on each partition; either we
10799 * find an existing constraint to reparent to ours, or we create a new
10800 * one.
10802 for (int i = 0; i < pd->nparts; i++)
10804 Oid partitionId = pd->oids[i];
10805 Relation partition = table_open(partitionId, lockmode);
10806 List *partFKs;
10807 AttrMap *attmap;
10808 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10809 bool attached;
10810 char *conname;
10811 Oid constrOid;
10812 ObjectAddress address,
10813 referenced;
10814 ListCell *cell;
10816 CheckTableNotInUse(partition, "ALTER TABLE");
10818 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10819 RelationGetDescr(rel),
10820 false);
10821 for (int j = 0; j < numfks; j++)
10822 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10824 /* Check whether an existing constraint can be repurposed */
10825 partFKs = copyObject(RelationGetFKeyList(partition));
10826 attached = false;
10827 foreach(cell, partFKs)
10829 ForeignKeyCacheInfo *fk;
10831 fk = lfirst_node(ForeignKeyCacheInfo, cell);
10832 if (tryAttachPartitionForeignKey(fk,
10833 partitionId,
10834 parentConstr,
10835 numfks,
10836 mapped_fkattnum,
10837 pkattnum,
10838 pfeqoperators,
10839 insertTriggerOid,
10840 updateTriggerOid,
10841 trigrel))
10843 attached = true;
10844 break;
10847 if (attached)
10849 table_close(partition, NoLock);
10850 continue;
10854 * No luck finding a good constraint to reuse; create our own.
10856 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10857 RelationGetRelid(partition),
10858 fkconstraint->conname))
10859 conname = ChooseConstraintName(RelationGetRelationName(partition),
10860 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10861 "fkey",
10862 RelationGetNamespace(partition), NIL);
10863 else
10864 conname = fkconstraint->conname;
10865 constrOid =
10866 CreateConstraintEntry(conname,
10867 RelationGetNamespace(partition),
10868 CONSTRAINT_FOREIGN,
10869 fkconstraint->deferrable,
10870 fkconstraint->initdeferred,
10871 fkconstraint->initially_valid,
10872 parentConstr,
10873 partitionId,
10874 mapped_fkattnum,
10875 numfks,
10876 numfks,
10877 InvalidOid,
10878 indexOid,
10879 RelationGetRelid(pkrel),
10880 pkattnum,
10881 pfeqoperators,
10882 ppeqoperators,
10883 ffeqoperators,
10884 numfks,
10885 fkconstraint->fk_upd_action,
10886 fkconstraint->fk_del_action,
10887 fkdelsetcols,
10888 numfkdelsetcols,
10889 fkconstraint->fk_matchtype,
10890 NULL,
10891 NULL,
10892 NULL,
10893 false,
10895 false,
10896 with_period, /* conPeriod */
10897 false);
10900 * Give this constraint partition-type dependencies on the parent
10901 * constraint as well as the table.
10903 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10904 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10905 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10906 ObjectAddressSet(referenced, RelationRelationId, partitionId);
10907 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10909 /* Make all this visible before recursing */
10910 CommandCounterIncrement();
10912 /* call ourselves to finalize the creation and we're done */
10913 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10914 indexOid,
10915 constrOid,
10916 numfks,
10917 pkattnum,
10918 mapped_fkattnum,
10919 pfeqoperators,
10920 ppeqoperators,
10921 ffeqoperators,
10922 numfkdelsetcols,
10923 fkdelsetcols,
10924 old_check_ok,
10925 lockmode,
10926 insertTriggerOid,
10927 updateTriggerOid,
10928 with_period);
10930 table_close(partition, NoLock);
10933 table_close(trigrel, RowExclusiveLock);
10938 * CloneForeignKeyConstraints
10939 * Clone foreign keys from a partitioned table to a newly acquired
10940 * partition.
10942 * partitionRel is a partition of parentRel, so we can be certain that it has
10943 * the same columns with the same datatypes. The columns may be in different
10944 * order, though.
10946 * wqueue must be passed to set up phase 3 constraint checking, unless the
10947 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10948 * PARTITION OF).
10950 static void
10951 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10952 Relation partitionRel)
10954 /* This only works for declarative partitioning */
10955 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10958 * Clone constraints for which the parent is on the referenced side.
10960 CloneFkReferenced(parentRel, partitionRel);
10963 * Now clone constraints where the parent is on the referencing side.
10965 CloneFkReferencing(wqueue, parentRel, partitionRel);
10969 * CloneFkReferenced
10970 * Subroutine for CloneForeignKeyConstraints
10972 * Find all the FKs that have the parent relation on the referenced side;
10973 * clone those constraints to the given partition. This is to be called
10974 * when the partition is being created or attached.
10976 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10978 * This recurses to partitions, if the relation being attached is partitioned.
10979 * Recursion is done by calling addFkRecurseReferenced.
10981 static void
10982 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10984 Relation pg_constraint;
10985 AttrMap *attmap;
10986 ListCell *cell;
10987 SysScanDesc scan;
10988 ScanKeyData key[2];
10989 HeapTuple tuple;
10990 List *clone = NIL;
10991 Relation trigrel;
10994 * Search for any constraints where this partition's parent is in the
10995 * referenced side. However, we must not clone any constraint whose
10996 * parent constraint is also going to be cloned, to avoid duplicates. So
10997 * do it in two steps: first construct the list of constraints to clone,
10998 * then go over that list cloning those whose parents are not in the list.
10999 * (We must not rely on the parent being seen first, since the catalog
11000 * scan could return children first.)
11002 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
11003 ScanKeyInit(&key[0],
11004 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
11005 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
11006 ScanKeyInit(&key[1],
11007 Anum_pg_constraint_contype, BTEqualStrategyNumber,
11008 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
11009 /* This is a seqscan, as we don't have a usable index ... */
11010 scan = systable_beginscan(pg_constraint, InvalidOid, true,
11011 NULL, 2, key);
11012 while ((tuple = systable_getnext(scan)) != NULL)
11014 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11016 clone = lappend_oid(clone, constrForm->oid);
11018 systable_endscan(scan);
11019 table_close(pg_constraint, RowShareLock);
11022 * Triggers of the foreign keys will be manipulated a bunch of times in
11023 * the loop below. To avoid repeatedly opening/closing the trigger
11024 * catalog relation, we open it here and pass it to the subroutines called
11025 * below.
11027 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11029 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
11030 RelationGetDescr(parentRel),
11031 false);
11032 foreach(cell, clone)
11034 Oid constrOid = lfirst_oid(cell);
11035 Form_pg_constraint constrForm;
11036 Relation fkRel;
11037 Oid indexOid;
11038 Oid partIndexId;
11039 int numfks;
11040 AttrNumber conkey[INDEX_MAX_KEYS];
11041 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
11042 AttrNumber confkey[INDEX_MAX_KEYS];
11043 Oid conpfeqop[INDEX_MAX_KEYS];
11044 Oid conppeqop[INDEX_MAX_KEYS];
11045 Oid conffeqop[INDEX_MAX_KEYS];
11046 int numfkdelsetcols;
11047 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11048 Constraint *fkconstraint;
11049 Oid deleteTriggerOid,
11050 updateTriggerOid;
11052 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
11053 if (!HeapTupleIsValid(tuple))
11054 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11055 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11058 * As explained above: don't try to clone a constraint for which we're
11059 * going to clone the parent.
11061 if (list_member_oid(clone, constrForm->conparentid))
11063 ReleaseSysCache(tuple);
11064 continue;
11068 * Don't clone self-referencing foreign keys, which can be in the
11069 * partitioned table or in the partition-to-be.
11071 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
11072 constrForm->conrelid == RelationGetRelid(partitionRel))
11074 ReleaseSysCache(tuple);
11075 continue;
11079 * Because we're only expanding the key space at the referenced side,
11080 * we don't need to prevent any operation in the referencing table, so
11081 * AccessShareLock suffices (assumes that dropping the constraint
11082 * acquires AEL).
11084 fkRel = table_open(constrForm->conrelid, AccessShareLock);
11086 indexOid = constrForm->conindid;
11087 DeconstructFkConstraintRow(tuple,
11088 &numfks,
11089 conkey,
11090 confkey,
11091 conpfeqop,
11092 conppeqop,
11093 conffeqop,
11094 &numfkdelsetcols,
11095 confdelsetcols);
11097 for (int i = 0; i < numfks; i++)
11098 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11100 fkconstraint = makeNode(Constraint);
11101 fkconstraint->contype = CONSTRAINT_FOREIGN;
11102 fkconstraint->conname = NameStr(constrForm->conname);
11103 fkconstraint->deferrable = constrForm->condeferrable;
11104 fkconstraint->initdeferred = constrForm->condeferred;
11105 fkconstraint->location = -1;
11106 fkconstraint->pktable = NULL;
11107 /* ->fk_attrs determined below */
11108 fkconstraint->pk_attrs = NIL;
11109 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11110 fkconstraint->fk_upd_action = constrForm->confupdtype;
11111 fkconstraint->fk_del_action = constrForm->confdeltype;
11112 fkconstraint->fk_del_set_cols = NIL;
11113 fkconstraint->old_conpfeqop = NIL;
11114 fkconstraint->old_pktable_oid = InvalidOid;
11115 fkconstraint->skip_validation = false;
11116 fkconstraint->initially_valid = true;
11118 /* set up colnames that are used to generate the constraint name */
11119 for (int i = 0; i < numfks; i++)
11121 Form_pg_attribute att;
11123 att = TupleDescAttr(RelationGetDescr(fkRel),
11124 conkey[i] - 1);
11125 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11126 makeString(NameStr(att->attname)));
11130 * Add the new foreign key constraint pointing to the new partition.
11131 * Because this new partition appears in the referenced side of the
11132 * constraint, we don't need to set up for Phase 3 check.
11134 partIndexId = index_get_partition(partitionRel, indexOid);
11135 if (!OidIsValid(partIndexId))
11136 elog(ERROR, "index for %u not found in partition %s",
11137 indexOid, RelationGetRelationName(partitionRel));
11140 * Get the "action" triggers belonging to the constraint to pass as
11141 * parent OIDs for similar triggers that will be created on the
11142 * partition in addFkRecurseReferenced().
11144 GetForeignKeyActionTriggers(trigrel, constrOid,
11145 constrForm->confrelid, constrForm->conrelid,
11146 &deleteTriggerOid, &updateTriggerOid);
11148 addFkRecurseReferenced(NULL,
11149 fkconstraint,
11150 fkRel,
11151 partitionRel,
11152 partIndexId,
11153 constrOid,
11154 numfks,
11155 mapped_confkey,
11156 conkey,
11157 conpfeqop,
11158 conppeqop,
11159 conffeqop,
11160 numfkdelsetcols,
11161 confdelsetcols,
11162 true,
11163 deleteTriggerOid,
11164 updateTriggerOid,
11165 constrForm->conperiod);
11167 table_close(fkRel, NoLock);
11168 ReleaseSysCache(tuple);
11171 table_close(trigrel, RowExclusiveLock);
11175 * CloneFkReferencing
11176 * Subroutine for CloneForeignKeyConstraints
11178 * For each FK constraint of the parent relation in the given list, find an
11179 * equivalent constraint in its partition relation that can be reparented;
11180 * if one cannot be found, create a new constraint in the partition as its
11181 * child.
11183 * If wqueue is given, it is used to set up phase-3 verification for each
11184 * cloned constraint; if omitted, we assume that such verification is not
11185 * needed (example: the partition is being created anew).
11187 static void
11188 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
11190 AttrMap *attmap;
11191 List *partFKs;
11192 List *clone = NIL;
11193 ListCell *cell;
11194 Relation trigrel;
11196 /* obtain a list of constraints that we need to clone */
11197 foreach(cell, RelationGetFKeyList(parentRel))
11199 ForeignKeyCacheInfo *fk = lfirst(cell);
11201 clone = lappend_oid(clone, fk->conoid);
11205 * Silently do nothing if there's nothing to do. In particular, this
11206 * avoids throwing a spurious error for foreign tables.
11208 if (clone == NIL)
11209 return;
11211 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11212 ereport(ERROR,
11213 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11214 errmsg("foreign key constraints are not supported on foreign tables")));
11217 * Triggers of the foreign keys will be manipulated a bunch of times in
11218 * the loop below. To avoid repeatedly opening/closing the trigger
11219 * catalog relation, we open it here and pass it to the subroutines called
11220 * below.
11222 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
11225 * The constraint key may differ, if the columns in the partition are
11226 * different. This map is used to convert them.
11228 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
11229 RelationGetDescr(parentRel),
11230 false);
11232 partFKs = copyObject(RelationGetFKeyList(partRel));
11234 foreach(cell, clone)
11236 Oid parentConstrOid = lfirst_oid(cell);
11237 Form_pg_constraint constrForm;
11238 Relation pkrel;
11239 HeapTuple tuple;
11240 int numfks;
11241 AttrNumber conkey[INDEX_MAX_KEYS];
11242 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
11243 AttrNumber confkey[INDEX_MAX_KEYS];
11244 Oid conpfeqop[INDEX_MAX_KEYS];
11245 Oid conppeqop[INDEX_MAX_KEYS];
11246 Oid conffeqop[INDEX_MAX_KEYS];
11247 int numfkdelsetcols;
11248 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11249 Constraint *fkconstraint;
11250 bool attached;
11251 Oid indexOid;
11252 Oid constrOid;
11253 ObjectAddress address,
11254 referenced;
11255 ListCell *lc;
11256 Oid insertTriggerOid,
11257 updateTriggerOid;
11258 bool with_period;
11260 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
11261 if (!HeapTupleIsValid(tuple))
11262 elog(ERROR, "cache lookup failed for constraint %u",
11263 parentConstrOid);
11264 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
11266 /* Don't clone constraints whose parents are being cloned */
11267 if (list_member_oid(clone, constrForm->conparentid))
11269 ReleaseSysCache(tuple);
11270 continue;
11274 * Need to prevent concurrent deletions. If pkrel is a partitioned
11275 * relation, that means to lock all partitions.
11277 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
11278 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11279 (void) find_all_inheritors(RelationGetRelid(pkrel),
11280 ShareRowExclusiveLock, NULL);
11282 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
11283 conpfeqop, conppeqop, conffeqop,
11284 &numfkdelsetcols, confdelsetcols);
11285 for (int i = 0; i < numfks; i++)
11286 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11289 * Get the "check" triggers belonging to the constraint to pass as
11290 * parent OIDs for similar triggers that will be created on the
11291 * partition in addFkRecurseReferencing(). They are also passed to
11292 * tryAttachPartitionForeignKey() below to simply assign as parents to
11293 * the partition's existing "check" triggers, that is, if the
11294 * corresponding constraints is deemed attachable to the parent
11295 * constraint.
11297 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
11298 constrForm->confrelid, constrForm->conrelid,
11299 &insertTriggerOid, &updateTriggerOid);
11302 * Before creating a new constraint, see whether any existing FKs are
11303 * fit for the purpose. If one is, attach the parent constraint to
11304 * it, and don't clone anything. This way we avoid the expensive
11305 * verification step and don't end up with a duplicate FK, and we
11306 * don't need to recurse to partitions for this constraint.
11308 attached = false;
11309 foreach(lc, partFKs)
11311 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
11313 if (tryAttachPartitionForeignKey(fk,
11314 RelationGetRelid(partRel),
11315 parentConstrOid,
11316 numfks,
11317 mapped_conkey,
11318 confkey,
11319 conpfeqop,
11320 insertTriggerOid,
11321 updateTriggerOid,
11322 trigrel))
11324 attached = true;
11325 table_close(pkrel, NoLock);
11326 break;
11329 if (attached)
11331 ReleaseSysCache(tuple);
11332 continue;
11335 /* No dice. Set up to create our own constraint */
11336 fkconstraint = makeNode(Constraint);
11337 fkconstraint->contype = CONSTRAINT_FOREIGN;
11338 /* ->conname determined below */
11339 fkconstraint->deferrable = constrForm->condeferrable;
11340 fkconstraint->initdeferred = constrForm->condeferred;
11341 fkconstraint->location = -1;
11342 fkconstraint->pktable = NULL;
11343 /* ->fk_attrs determined below */
11344 fkconstraint->pk_attrs = NIL;
11345 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11346 fkconstraint->fk_upd_action = constrForm->confupdtype;
11347 fkconstraint->fk_del_action = constrForm->confdeltype;
11348 fkconstraint->fk_del_set_cols = NIL;
11349 fkconstraint->old_conpfeqop = NIL;
11350 fkconstraint->old_pktable_oid = InvalidOid;
11351 fkconstraint->skip_validation = false;
11352 fkconstraint->initially_valid = true;
11353 for (int i = 0; i < numfks; i++)
11355 Form_pg_attribute att;
11357 att = TupleDescAttr(RelationGetDescr(partRel),
11358 mapped_conkey[i] - 1);
11359 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11360 makeString(NameStr(att->attname)));
11362 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
11363 RelationGetRelid(partRel),
11364 NameStr(constrForm->conname)))
11365 fkconstraint->conname =
11366 ChooseConstraintName(RelationGetRelationName(partRel),
11367 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
11368 "fkey",
11369 RelationGetNamespace(partRel), NIL);
11370 else
11371 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
11373 indexOid = constrForm->conindid;
11374 with_period = constrForm->conperiod;
11375 constrOid =
11376 CreateConstraintEntry(fkconstraint->conname,
11377 constrForm->connamespace,
11378 CONSTRAINT_FOREIGN,
11379 fkconstraint->deferrable,
11380 fkconstraint->initdeferred,
11381 constrForm->convalidated,
11382 parentConstrOid,
11383 RelationGetRelid(partRel),
11384 mapped_conkey,
11385 numfks,
11386 numfks,
11387 InvalidOid, /* not a domain constraint */
11388 indexOid,
11389 constrForm->confrelid, /* same foreign rel */
11390 confkey,
11391 conpfeqop,
11392 conppeqop,
11393 conffeqop,
11394 numfks,
11395 fkconstraint->fk_upd_action,
11396 fkconstraint->fk_del_action,
11397 confdelsetcols,
11398 numfkdelsetcols,
11399 fkconstraint->fk_matchtype,
11400 NULL,
11401 NULL,
11402 NULL,
11403 false, /* islocal */
11404 1, /* inhcount */
11405 false, /* conNoInherit */
11406 with_period, /* conPeriod */
11407 true);
11409 /* Set up partition dependencies for the new constraint */
11410 ObjectAddressSet(address, ConstraintRelationId, constrOid);
11411 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
11412 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
11413 ObjectAddressSet(referenced, RelationRelationId,
11414 RelationGetRelid(partRel));
11415 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
11417 /* Done with the cloned constraint's tuple */
11418 ReleaseSysCache(tuple);
11420 /* Make all this visible before recursing */
11421 CommandCounterIncrement();
11423 addFkRecurseReferencing(wqueue,
11424 fkconstraint,
11425 partRel,
11426 pkrel,
11427 indexOid,
11428 constrOid,
11429 numfks,
11430 confkey,
11431 mapped_conkey,
11432 conpfeqop,
11433 conppeqop,
11434 conffeqop,
11435 numfkdelsetcols,
11436 confdelsetcols,
11437 false, /* no old check exists */
11438 AccessExclusiveLock,
11439 insertTriggerOid,
11440 updateTriggerOid,
11441 with_period);
11442 table_close(pkrel, NoLock);
11445 table_close(trigrel, RowExclusiveLock);
11449 * When the parent of a partition receives [the referencing side of] a foreign
11450 * key, we must propagate that foreign key to the partition. However, the
11451 * partition might already have an equivalent foreign key; this routine
11452 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11453 * by the other parameters. If they are equivalent, create the link between
11454 * the two constraints and return true.
11456 * If the given FK does not match the one defined by rest of the params,
11457 * return false.
11459 static bool
11460 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11461 Oid partRelid,
11462 Oid parentConstrOid,
11463 int numfks,
11464 AttrNumber *mapped_conkey,
11465 AttrNumber *confkey,
11466 Oid *conpfeqop,
11467 Oid parentInsTrigger,
11468 Oid parentUpdTrigger,
11469 Relation trigrel)
11471 HeapTuple parentConstrTup;
11472 Form_pg_constraint parentConstr;
11473 HeapTuple partcontup;
11474 Form_pg_constraint partConstr;
11475 ScanKeyData key;
11476 SysScanDesc scan;
11477 HeapTuple trigtup;
11478 Oid insertTriggerOid,
11479 updateTriggerOid;
11481 parentConstrTup = SearchSysCache1(CONSTROID,
11482 ObjectIdGetDatum(parentConstrOid));
11483 if (!HeapTupleIsValid(parentConstrTup))
11484 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11485 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11488 * Do some quick & easy initial checks. If any of these fail, we cannot
11489 * use this constraint.
11491 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11493 ReleaseSysCache(parentConstrTup);
11494 return false;
11496 for (int i = 0; i < numfks; i++)
11498 if (fk->conkey[i] != mapped_conkey[i] ||
11499 fk->confkey[i] != confkey[i] ||
11500 fk->conpfeqop[i] != conpfeqop[i])
11502 ReleaseSysCache(parentConstrTup);
11503 return false;
11508 * Looks good so far; do some more extensive checks. Presumably the check
11509 * for 'convalidated' could be dropped, since we don't really care about
11510 * that, but let's be careful for now.
11512 partcontup = SearchSysCache1(CONSTROID,
11513 ObjectIdGetDatum(fk->conoid));
11514 if (!HeapTupleIsValid(partcontup))
11515 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11516 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11517 if (OidIsValid(partConstr->conparentid) ||
11518 !partConstr->convalidated ||
11519 partConstr->condeferrable != parentConstr->condeferrable ||
11520 partConstr->condeferred != parentConstr->condeferred ||
11521 partConstr->confupdtype != parentConstr->confupdtype ||
11522 partConstr->confdeltype != parentConstr->confdeltype ||
11523 partConstr->confmatchtype != parentConstr->confmatchtype)
11525 ReleaseSysCache(parentConstrTup);
11526 ReleaseSysCache(partcontup);
11527 return false;
11530 ReleaseSysCache(partcontup);
11531 ReleaseSysCache(parentConstrTup);
11534 * Looks good! Attach this constraint. The action triggers in the new
11535 * partition become redundant -- the parent table already has equivalent
11536 * ones, and those will be able to reach the partition. Remove the ones
11537 * in the partition. We identify them because they have our constraint
11538 * OID, as well as being on the referenced rel.
11540 ScanKeyInit(&key,
11541 Anum_pg_trigger_tgconstraint,
11542 BTEqualStrategyNumber, F_OIDEQ,
11543 ObjectIdGetDatum(fk->conoid));
11544 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11545 NULL, 1, &key);
11546 while ((trigtup = systable_getnext(scan)) != NULL)
11548 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11549 ObjectAddress trigger;
11551 if (trgform->tgconstrrelid != fk->conrelid)
11552 continue;
11553 if (trgform->tgrelid != fk->confrelid)
11554 continue;
11557 * The constraint is originally set up to contain this trigger as an
11558 * implementation object, so there's a dependency record that links
11559 * the two; however, since the trigger is no longer needed, we remove
11560 * the dependency link in order to be able to drop the trigger while
11561 * keeping the constraint intact.
11563 deleteDependencyRecordsFor(TriggerRelationId,
11564 trgform->oid,
11565 false);
11566 /* make dependency deletion visible to performDeletion */
11567 CommandCounterIncrement();
11568 ObjectAddressSet(trigger, TriggerRelationId,
11569 trgform->oid);
11570 performDeletion(&trigger, DROP_RESTRICT, 0);
11571 /* make trigger drop visible, in case the loop iterates */
11572 CommandCounterIncrement();
11575 systable_endscan(scan);
11577 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11580 * Like the constraint, attach partition's "check" triggers to the
11581 * corresponding parent triggers.
11583 GetForeignKeyCheckTriggers(trigrel,
11584 fk->conoid, fk->confrelid, fk->conrelid,
11585 &insertTriggerOid, &updateTriggerOid);
11586 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11587 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11588 partRelid);
11589 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11590 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11591 partRelid);
11593 CommandCounterIncrement();
11594 return true;
11598 * GetForeignKeyActionTriggers
11599 * Returns delete and update "action" triggers of the given relation
11600 * belonging to the given constraint
11602 static void
11603 GetForeignKeyActionTriggers(Relation trigrel,
11604 Oid conoid, Oid confrelid, Oid conrelid,
11605 Oid *deleteTriggerOid,
11606 Oid *updateTriggerOid)
11608 ScanKeyData key;
11609 SysScanDesc scan;
11610 HeapTuple trigtup;
11612 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11613 ScanKeyInit(&key,
11614 Anum_pg_trigger_tgconstraint,
11615 BTEqualStrategyNumber, F_OIDEQ,
11616 ObjectIdGetDatum(conoid));
11618 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11619 NULL, 1, &key);
11620 while ((trigtup = systable_getnext(scan)) != NULL)
11622 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11624 if (trgform->tgconstrrelid != conrelid)
11625 continue;
11626 if (trgform->tgrelid != confrelid)
11627 continue;
11628 /* Only ever look at "action" triggers on the PK side. */
11629 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11630 continue;
11631 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11633 Assert(*deleteTriggerOid == InvalidOid);
11634 *deleteTriggerOid = trgform->oid;
11636 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11638 Assert(*updateTriggerOid == InvalidOid);
11639 *updateTriggerOid = trgform->oid;
11641 #ifndef USE_ASSERT_CHECKING
11642 /* In an assert-enabled build, continue looking to find duplicates */
11643 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11644 break;
11645 #endif
11648 if (!OidIsValid(*deleteTriggerOid))
11649 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11650 conoid);
11651 if (!OidIsValid(*updateTriggerOid))
11652 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11653 conoid);
11655 systable_endscan(scan);
11659 * GetForeignKeyCheckTriggers
11660 * Returns insert and update "check" triggers of the given relation
11661 * belonging to the given constraint
11663 static void
11664 GetForeignKeyCheckTriggers(Relation trigrel,
11665 Oid conoid, Oid confrelid, Oid conrelid,
11666 Oid *insertTriggerOid,
11667 Oid *updateTriggerOid)
11669 ScanKeyData key;
11670 SysScanDesc scan;
11671 HeapTuple trigtup;
11673 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11674 ScanKeyInit(&key,
11675 Anum_pg_trigger_tgconstraint,
11676 BTEqualStrategyNumber, F_OIDEQ,
11677 ObjectIdGetDatum(conoid));
11679 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11680 NULL, 1, &key);
11681 while ((trigtup = systable_getnext(scan)) != NULL)
11683 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11685 if (trgform->tgconstrrelid != confrelid)
11686 continue;
11687 if (trgform->tgrelid != conrelid)
11688 continue;
11689 /* Only ever look at "check" triggers on the FK side. */
11690 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11691 continue;
11692 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11694 Assert(*insertTriggerOid == InvalidOid);
11695 *insertTriggerOid = trgform->oid;
11697 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11699 Assert(*updateTriggerOid == InvalidOid);
11700 *updateTriggerOid = trgform->oid;
11702 #ifndef USE_ASSERT_CHECKING
11703 /* In an assert-enabled build, continue looking to find duplicates. */
11704 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11705 break;
11706 #endif
11709 if (!OidIsValid(*insertTriggerOid))
11710 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11711 conoid);
11712 if (!OidIsValid(*updateTriggerOid))
11713 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11714 conoid);
11716 systable_endscan(scan);
11720 * ALTER TABLE ALTER CONSTRAINT
11722 * Update the attributes of a constraint.
11724 * Currently only works for Foreign Key constraints.
11726 * If the constraint is modified, returns its address; otherwise, return
11727 * InvalidObjectAddress.
11729 static ObjectAddress
11730 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11731 bool recursing, LOCKMODE lockmode)
11733 Constraint *cmdcon;
11734 Relation conrel;
11735 Relation tgrel;
11736 SysScanDesc scan;
11737 ScanKeyData skey[3];
11738 HeapTuple contuple;
11739 Form_pg_constraint currcon;
11740 ObjectAddress address;
11741 List *otherrelids = NIL;
11742 ListCell *lc;
11744 cmdcon = castNode(Constraint, cmd->def);
11746 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11747 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11750 * Find and check the target constraint
11752 ScanKeyInit(&skey[0],
11753 Anum_pg_constraint_conrelid,
11754 BTEqualStrategyNumber, F_OIDEQ,
11755 ObjectIdGetDatum(RelationGetRelid(rel)));
11756 ScanKeyInit(&skey[1],
11757 Anum_pg_constraint_contypid,
11758 BTEqualStrategyNumber, F_OIDEQ,
11759 ObjectIdGetDatum(InvalidOid));
11760 ScanKeyInit(&skey[2],
11761 Anum_pg_constraint_conname,
11762 BTEqualStrategyNumber, F_NAMEEQ,
11763 CStringGetDatum(cmdcon->conname));
11764 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11765 true, NULL, 3, skey);
11767 /* There can be at most one matching row */
11768 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11769 ereport(ERROR,
11770 (errcode(ERRCODE_UNDEFINED_OBJECT),
11771 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11772 cmdcon->conname, RelationGetRelationName(rel))));
11774 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11775 if (currcon->contype != CONSTRAINT_FOREIGN)
11776 ereport(ERROR,
11777 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11778 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11779 cmdcon->conname, RelationGetRelationName(rel))));
11782 * If it's not the topmost constraint, raise an error.
11784 * Altering a non-topmost constraint leaves some triggers untouched, since
11785 * they are not directly connected to this constraint; also, pg_dump would
11786 * ignore the deferrability status of the individual constraint, since it
11787 * only dumps topmost constraints. Avoid these problems by refusing this
11788 * operation and telling the user to alter the parent constraint instead.
11790 if (OidIsValid(currcon->conparentid))
11792 HeapTuple tp;
11793 Oid parent = currcon->conparentid;
11794 char *ancestorname = NULL;
11795 char *ancestortable = NULL;
11797 /* Loop to find the topmost constraint */
11798 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11800 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11802 /* If no parent, this is the constraint we want */
11803 if (!OidIsValid(contup->conparentid))
11805 ancestorname = pstrdup(NameStr(contup->conname));
11806 ancestortable = get_rel_name(contup->conrelid);
11807 ReleaseSysCache(tp);
11808 break;
11811 parent = contup->conparentid;
11812 ReleaseSysCache(tp);
11815 ereport(ERROR,
11816 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11817 cmdcon->conname, RelationGetRelationName(rel)),
11818 ancestorname && ancestortable ?
11819 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11820 cmdcon->conname, ancestorname, ancestortable) : 0,
11821 errhint("You may alter the constraint it derives from instead.")));
11825 * Do the actual catalog work. We can skip changing if already in the
11826 * desired state, but not if a partitioned table: partitions need to be
11827 * processed regardless, in case they had the constraint locally changed.
11829 address = InvalidObjectAddress;
11830 if (currcon->condeferrable != cmdcon->deferrable ||
11831 currcon->condeferred != cmdcon->initdeferred ||
11832 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11834 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11835 &otherrelids, lockmode))
11836 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11840 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11841 * having the constraint itself; here we also invalidate for relations
11842 * that have any triggers that are part of the constraint.
11844 foreach(lc, otherrelids)
11845 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11847 systable_endscan(scan);
11849 table_close(tgrel, RowExclusiveLock);
11850 table_close(conrel, RowExclusiveLock);
11852 return address;
11856 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11857 * constraint is altered.
11859 * *otherrelids is appended OIDs of relations containing affected triggers.
11861 * Note that we must recurse even when the values are correct, in case
11862 * indirect descendants have had their constraints altered locally.
11863 * (This could be avoided if we forbade altering constraints in partitions
11864 * but existing releases don't do that.)
11866 static bool
11867 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11868 Relation rel, HeapTuple contuple, List **otherrelids,
11869 LOCKMODE lockmode)
11871 Form_pg_constraint currcon;
11872 Oid conoid;
11873 Oid refrelid;
11874 bool changed = false;
11876 /* since this function recurses, it could be driven to stack overflow */
11877 check_stack_depth();
11879 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11880 conoid = currcon->oid;
11881 refrelid = currcon->confrelid;
11884 * Update pg_constraint with the flags from cmdcon.
11886 * If called to modify a constraint that's already in the desired state,
11887 * silently do nothing.
11889 if (currcon->condeferrable != cmdcon->deferrable ||
11890 currcon->condeferred != cmdcon->initdeferred)
11892 HeapTuple copyTuple;
11893 Form_pg_constraint copy_con;
11894 HeapTuple tgtuple;
11895 ScanKeyData tgkey;
11896 SysScanDesc tgscan;
11898 copyTuple = heap_copytuple(contuple);
11899 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11900 copy_con->condeferrable = cmdcon->deferrable;
11901 copy_con->condeferred = cmdcon->initdeferred;
11902 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11904 InvokeObjectPostAlterHook(ConstraintRelationId,
11905 conoid, 0);
11907 heap_freetuple(copyTuple);
11908 changed = true;
11910 /* Make new constraint flags visible to others */
11911 CacheInvalidateRelcache(rel);
11914 * Now we need to update the multiple entries in pg_trigger that
11915 * implement the constraint.
11917 ScanKeyInit(&tgkey,
11918 Anum_pg_trigger_tgconstraint,
11919 BTEqualStrategyNumber, F_OIDEQ,
11920 ObjectIdGetDatum(conoid));
11921 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11922 NULL, 1, &tgkey);
11923 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11925 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11926 Form_pg_trigger copy_tg;
11927 HeapTuple tgCopyTuple;
11930 * Remember OIDs of other relation(s) involved in FK constraint.
11931 * (Note: it's likely that we could skip forcing a relcache inval
11932 * for other rels that don't have a trigger whose properties
11933 * change, but let's be conservative.)
11935 if (tgform->tgrelid != RelationGetRelid(rel))
11936 *otherrelids = list_append_unique_oid(*otherrelids,
11937 tgform->tgrelid);
11940 * Update deferrability of RI_FKey_noaction_del,
11941 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11942 * triggers, but not others; see createForeignKeyActionTriggers
11943 * and CreateFKCheckTrigger.
11945 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11946 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11947 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11948 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11949 continue;
11951 tgCopyTuple = heap_copytuple(tgtuple);
11952 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11954 copy_tg->tgdeferrable = cmdcon->deferrable;
11955 copy_tg->tginitdeferred = cmdcon->initdeferred;
11956 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11958 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11960 heap_freetuple(tgCopyTuple);
11963 systable_endscan(tgscan);
11967 * If the table at either end of the constraint is partitioned, we need to
11968 * recurse and handle every constraint that is a child of this one.
11970 * (This assumes that the recurse flag is forcibly set for partitioned
11971 * tables, and not set for legacy inheritance, though we don't check for
11972 * that here.)
11974 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11975 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11977 ScanKeyData pkey;
11978 SysScanDesc pscan;
11979 HeapTuple childtup;
11981 ScanKeyInit(&pkey,
11982 Anum_pg_constraint_conparentid,
11983 BTEqualStrategyNumber, F_OIDEQ,
11984 ObjectIdGetDatum(conoid));
11986 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11987 true, NULL, 1, &pkey);
11989 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11991 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11992 Relation childrel;
11994 childrel = table_open(childcon->conrelid, lockmode);
11995 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11996 otherrelids, lockmode);
11997 table_close(childrel, NoLock);
12000 systable_endscan(pscan);
12003 return changed;
12007 * ALTER TABLE VALIDATE CONSTRAINT
12009 * XXX The reason we handle recursion here rather than at Phase 1 is because
12010 * there's no good way to skip recursing when handling foreign keys: there is
12011 * no need to lock children in that case, yet we wouldn't be able to avoid
12012 * doing so at that level.
12014 * Return value is the address of the validated constraint. If the constraint
12015 * was already validated, InvalidObjectAddress is returned.
12017 static ObjectAddress
12018 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
12019 bool recurse, bool recursing, LOCKMODE lockmode)
12021 Relation conrel;
12022 SysScanDesc scan;
12023 ScanKeyData skey[3];
12024 HeapTuple tuple;
12025 Form_pg_constraint con;
12026 ObjectAddress address;
12028 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12031 * Find and check the target constraint
12033 ScanKeyInit(&skey[0],
12034 Anum_pg_constraint_conrelid,
12035 BTEqualStrategyNumber, F_OIDEQ,
12036 ObjectIdGetDatum(RelationGetRelid(rel)));
12037 ScanKeyInit(&skey[1],
12038 Anum_pg_constraint_contypid,
12039 BTEqualStrategyNumber, F_OIDEQ,
12040 ObjectIdGetDatum(InvalidOid));
12041 ScanKeyInit(&skey[2],
12042 Anum_pg_constraint_conname,
12043 BTEqualStrategyNumber, F_NAMEEQ,
12044 CStringGetDatum(constrName));
12045 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12046 true, NULL, 3, skey);
12048 /* There can be at most one matching row */
12049 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12050 ereport(ERROR,
12051 (errcode(ERRCODE_UNDEFINED_OBJECT),
12052 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12053 constrName, RelationGetRelationName(rel))));
12055 con = (Form_pg_constraint) GETSTRUCT(tuple);
12056 if (con->contype != CONSTRAINT_FOREIGN &&
12057 con->contype != CONSTRAINT_CHECK)
12058 ereport(ERROR,
12059 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12060 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
12061 constrName, RelationGetRelationName(rel))));
12063 if (!con->convalidated)
12065 AlteredTableInfo *tab;
12066 HeapTuple copyTuple;
12067 Form_pg_constraint copy_con;
12069 if (con->contype == CONSTRAINT_FOREIGN)
12071 NewConstraint *newcon;
12072 Constraint *fkconstraint;
12074 /* Queue validation for phase 3 */
12075 fkconstraint = makeNode(Constraint);
12076 /* for now this is all we need */
12077 fkconstraint->conname = constrName;
12079 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12080 newcon->name = constrName;
12081 newcon->contype = CONSTR_FOREIGN;
12082 newcon->refrelid = con->confrelid;
12083 newcon->refindid = con->conindid;
12084 newcon->conid = con->oid;
12085 newcon->qual = (Node *) fkconstraint;
12087 /* Find or create work queue entry for this table */
12088 tab = ATGetQueueEntry(wqueue, rel);
12089 tab->constraints = lappend(tab->constraints, newcon);
12092 * We disallow creating invalid foreign keys to or from
12093 * partitioned tables, so ignoring the recursion bit is okay.
12096 else if (con->contype == CONSTRAINT_CHECK)
12098 List *children = NIL;
12099 ListCell *child;
12100 NewConstraint *newcon;
12101 Datum val;
12102 char *conbin;
12105 * If we're recursing, the parent has already done this, so skip
12106 * it. Also, if the constraint is a NO INHERIT constraint, we
12107 * shouldn't try to look for it in the children.
12109 if (!recursing && !con->connoinherit)
12110 children = find_all_inheritors(RelationGetRelid(rel),
12111 lockmode, NULL);
12114 * For CHECK constraints, we must ensure that we only mark the
12115 * constraint as validated on the parent if it's already validated
12116 * on the children.
12118 * We recurse before validating on the parent, to reduce risk of
12119 * deadlocks.
12121 foreach(child, children)
12123 Oid childoid = lfirst_oid(child);
12124 Relation childrel;
12126 if (childoid == RelationGetRelid(rel))
12127 continue;
12130 * If we are told not to recurse, there had better not be any
12131 * child tables, because we can't mark the constraint on the
12132 * parent valid unless it is valid for all child tables.
12134 if (!recurse)
12135 ereport(ERROR,
12136 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12137 errmsg("constraint must be validated on child tables too")));
12139 /* find_all_inheritors already got lock */
12140 childrel = table_open(childoid, NoLock);
12142 ATExecValidateConstraint(wqueue, childrel, constrName, false,
12143 true, lockmode);
12144 table_close(childrel, NoLock);
12147 /* Queue validation for phase 3 */
12148 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
12149 newcon->name = constrName;
12150 newcon->contype = CONSTR_CHECK;
12151 newcon->refrelid = InvalidOid;
12152 newcon->refindid = InvalidOid;
12153 newcon->conid = con->oid;
12155 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
12156 Anum_pg_constraint_conbin);
12157 conbin = TextDatumGetCString(val);
12158 newcon->qual = (Node *) stringToNode(conbin);
12160 /* Find or create work queue entry for this table */
12161 tab = ATGetQueueEntry(wqueue, rel);
12162 tab->constraints = lappend(tab->constraints, newcon);
12165 * Invalidate relcache so that others see the new validated
12166 * constraint.
12168 CacheInvalidateRelcache(rel);
12172 * Now update the catalog, while we have the door open.
12174 copyTuple = heap_copytuple(tuple);
12175 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
12176 copy_con->convalidated = true;
12177 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
12179 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
12181 heap_freetuple(copyTuple);
12183 ObjectAddressSet(address, ConstraintRelationId, con->oid);
12185 else
12186 address = InvalidObjectAddress; /* already validated */
12188 systable_endscan(scan);
12190 table_close(conrel, RowExclusiveLock);
12192 return address;
12197 * transformColumnNameList - transform list of column names
12199 * Lookup each name and return its attnum and, optionally, type OID
12201 * Note: the name of this function suggests that it's general-purpose,
12202 * but actually it's only used to look up names appearing in foreign-key
12203 * clauses. The error messages would need work to use it in other cases,
12204 * and perhaps the validity checks as well.
12206 static int
12207 transformColumnNameList(Oid relId, List *colList,
12208 int16 *attnums, Oid *atttypids)
12210 ListCell *l;
12211 int attnum;
12213 attnum = 0;
12214 foreach(l, colList)
12216 char *attname = strVal(lfirst(l));
12217 HeapTuple atttuple;
12218 Form_pg_attribute attform;
12220 atttuple = SearchSysCacheAttName(relId, attname);
12221 if (!HeapTupleIsValid(atttuple))
12222 ereport(ERROR,
12223 (errcode(ERRCODE_UNDEFINED_COLUMN),
12224 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
12225 attname)));
12226 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
12227 if (attform->attnum < 0)
12228 ereport(ERROR,
12229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12230 errmsg("system columns cannot be used in foreign keys")));
12231 if (attnum >= INDEX_MAX_KEYS)
12232 ereport(ERROR,
12233 (errcode(ERRCODE_TOO_MANY_COLUMNS),
12234 errmsg("cannot have more than %d keys in a foreign key",
12235 INDEX_MAX_KEYS)));
12236 attnums[attnum] = attform->attnum;
12237 if (atttypids != NULL)
12238 atttypids[attnum] = attform->atttypid;
12239 ReleaseSysCache(atttuple);
12240 attnum++;
12243 return attnum;
12247 * transformFkeyGetPrimaryKey -
12249 * Look up the names, attnums, and types of the primary key attributes
12250 * for the pkrel. Also return the index OID and index opclasses of the
12251 * index supporting the primary key. Also return whether the index has
12252 * WITHOUT OVERLAPS.
12254 * All parameters except pkrel are output parameters. Also, the function
12255 * return value is the number of attributes in the primary key.
12257 * Used when the column list in the REFERENCES specification is omitted.
12259 static int
12260 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
12261 List **attnamelist,
12262 int16 *attnums, Oid *atttypids,
12263 Oid *opclasses, bool *pk_has_without_overlaps)
12265 List *indexoidlist;
12266 ListCell *indexoidscan;
12267 HeapTuple indexTuple = NULL;
12268 Form_pg_index indexStruct = NULL;
12269 Datum indclassDatum;
12270 oidvector *indclass;
12271 int i;
12274 * Get the list of index OIDs for the table from the relcache, and look up
12275 * each one in the pg_index syscache until we find one marked primary key
12276 * (hopefully there isn't more than one such). Insist it's valid, too.
12278 *indexOid = InvalidOid;
12280 indexoidlist = RelationGetIndexList(pkrel);
12282 foreach(indexoidscan, indexoidlist)
12284 Oid indexoid = lfirst_oid(indexoidscan);
12286 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12287 if (!HeapTupleIsValid(indexTuple))
12288 elog(ERROR, "cache lookup failed for index %u", indexoid);
12289 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12290 if (indexStruct->indisprimary && indexStruct->indisvalid)
12293 * Refuse to use a deferrable primary key. This is per SQL spec,
12294 * and there would be a lot of interesting semantic problems if we
12295 * tried to allow it.
12297 if (!indexStruct->indimmediate)
12298 ereport(ERROR,
12299 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12300 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
12301 RelationGetRelationName(pkrel))));
12303 *indexOid = indexoid;
12304 break;
12306 ReleaseSysCache(indexTuple);
12309 list_free(indexoidlist);
12312 * Check that we found it
12314 if (!OidIsValid(*indexOid))
12315 ereport(ERROR,
12316 (errcode(ERRCODE_UNDEFINED_OBJECT),
12317 errmsg("there is no primary key for referenced table \"%s\"",
12318 RelationGetRelationName(pkrel))));
12320 /* Must get indclass the hard way */
12321 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12322 Anum_pg_index_indclass);
12323 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12326 * Now build the list of PK attributes from the indkey definition (we
12327 * assume a primary key cannot have expressional elements)
12329 *attnamelist = NIL;
12330 for (i = 0; i < indexStruct->indnkeyatts; i++)
12332 int pkattno = indexStruct->indkey.values[i];
12334 attnums[i] = pkattno;
12335 atttypids[i] = attnumTypeId(pkrel, pkattno);
12336 opclasses[i] = indclass->values[i];
12337 *attnamelist = lappend(*attnamelist,
12338 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
12341 *pk_has_without_overlaps = indexStruct->indisexclusion;
12343 ReleaseSysCache(indexTuple);
12345 return i;
12349 * transformFkeyCheckAttrs -
12351 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
12352 * reference as part of a foreign key constraint.
12354 * Returns the OID of the unique index supporting the constraint and
12355 * populates the caller-provided 'opclasses' array with the opclasses
12356 * associated with the index columns. Also sets whether the index
12357 * uses WITHOUT OVERLAPS.
12359 * Raises an ERROR on validation failure.
12361 static Oid
12362 transformFkeyCheckAttrs(Relation pkrel,
12363 int numattrs, int16 *attnums,
12364 bool with_period, Oid *opclasses,
12365 bool *pk_has_without_overlaps)
12367 Oid indexoid = InvalidOid;
12368 bool found = false;
12369 bool found_deferrable = false;
12370 List *indexoidlist;
12371 ListCell *indexoidscan;
12372 int i,
12376 * Reject duplicate appearances of columns in the referenced-columns list.
12377 * Such a case is forbidden by the SQL standard, and even if we thought it
12378 * useful to allow it, there would be ambiguity about how to match the
12379 * list to unique indexes (in particular, it'd be unclear which index
12380 * opclass goes with which FK column).
12382 for (i = 0; i < numattrs; i++)
12384 for (j = i + 1; j < numattrs; j++)
12386 if (attnums[i] == attnums[j])
12387 ereport(ERROR,
12388 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12389 errmsg("foreign key referenced-columns list must not contain duplicates")));
12394 * Get the list of index OIDs for the table from the relcache, and look up
12395 * each one in the pg_index syscache, and match unique indexes to the list
12396 * of attnums we are given.
12398 indexoidlist = RelationGetIndexList(pkrel);
12400 foreach(indexoidscan, indexoidlist)
12402 HeapTuple indexTuple;
12403 Form_pg_index indexStruct;
12405 indexoid = lfirst_oid(indexoidscan);
12406 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
12407 if (!HeapTupleIsValid(indexTuple))
12408 elog(ERROR, "cache lookup failed for index %u", indexoid);
12409 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
12412 * Must have the right number of columns; must be unique (or if
12413 * temporal then exclusion instead) and not a partial index; forget it
12414 * if there are any expressions, too. Invalid indexes are out as well.
12416 if (indexStruct->indnkeyatts == numattrs &&
12417 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
12418 indexStruct->indisvalid &&
12419 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
12420 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
12422 Datum indclassDatum;
12423 oidvector *indclass;
12425 /* Must get indclass the hard way */
12426 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
12427 Anum_pg_index_indclass);
12428 indclass = (oidvector *) DatumGetPointer(indclassDatum);
12431 * The given attnum list may match the index columns in any order.
12432 * Check for a match, and extract the appropriate opclasses while
12433 * we're at it.
12435 * We know that attnums[] is duplicate-free per the test at the
12436 * start of this function, and we checked above that the number of
12437 * index columns agrees, so if we find a match for each attnums[]
12438 * entry then we must have a one-to-one match in some order.
12440 for (i = 0; i < numattrs; i++)
12442 found = false;
12443 for (j = 0; j < numattrs; j++)
12445 if (attnums[i] == indexStruct->indkey.values[j])
12447 opclasses[i] = indclass->values[j];
12448 found = true;
12449 break;
12452 if (!found)
12453 break;
12455 /* The last attribute in the index must be the PERIOD FK part */
12456 if (found && with_period)
12458 int16 periodattnum = attnums[numattrs - 1];
12460 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
12464 * Refuse to use a deferrable unique/primary key. This is per SQL
12465 * spec, and there would be a lot of interesting semantic problems
12466 * if we tried to allow it.
12468 if (found && !indexStruct->indimmediate)
12471 * Remember that we found an otherwise matching index, so that
12472 * we can generate a more appropriate error message.
12474 found_deferrable = true;
12475 found = false;
12478 /* We need to know whether the index has WITHOUT OVERLAPS */
12479 if (found)
12480 *pk_has_without_overlaps = indexStruct->indisexclusion;
12482 ReleaseSysCache(indexTuple);
12483 if (found)
12484 break;
12487 if (!found)
12489 if (found_deferrable)
12490 ereport(ERROR,
12491 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12492 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12493 RelationGetRelationName(pkrel))));
12494 else
12495 ereport(ERROR,
12496 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12497 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12498 RelationGetRelationName(pkrel))));
12501 list_free(indexoidlist);
12503 return indexoid;
12507 * findFkeyCast -
12509 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12510 * Caller has equal regard for binary coercibility and for an exact match.
12512 static CoercionPathType
12513 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12515 CoercionPathType ret;
12517 if (targetTypeId == sourceTypeId)
12519 ret = COERCION_PATH_RELABELTYPE;
12520 *funcid = InvalidOid;
12522 else
12524 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12525 COERCION_IMPLICIT, funcid);
12526 if (ret == COERCION_PATH_NONE)
12527 /* A previously-relied-upon cast is now gone. */
12528 elog(ERROR, "could not find cast from %u to %u",
12529 sourceTypeId, targetTypeId);
12532 return ret;
12536 * Permissions checks on the referenced table for ADD FOREIGN KEY
12538 * Note: we have already checked that the user owns the referencing table,
12539 * else we'd have failed much earlier; no additional checks are needed for it.
12541 static void
12542 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12544 Oid roleid = GetUserId();
12545 AclResult aclresult;
12546 int i;
12548 /* Okay if we have relation-level REFERENCES permission */
12549 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12550 ACL_REFERENCES);
12551 if (aclresult == ACLCHECK_OK)
12552 return;
12553 /* Else we must have REFERENCES on each column */
12554 for (i = 0; i < natts; i++)
12556 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12557 roleid, ACL_REFERENCES);
12558 if (aclresult != ACLCHECK_OK)
12559 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12560 RelationGetRelationName(rel));
12565 * Scan the existing rows in a table to verify they meet a proposed FK
12566 * constraint.
12568 * Caller must have opened and locked both relations appropriately.
12570 static void
12571 validateForeignKeyConstraint(char *conname,
12572 Relation rel,
12573 Relation pkrel,
12574 Oid pkindOid,
12575 Oid constraintOid,
12576 bool hasperiod)
12578 TupleTableSlot *slot;
12579 TableScanDesc scan;
12580 Trigger trig = {0};
12581 Snapshot snapshot;
12582 MemoryContext oldcxt;
12583 MemoryContext perTupCxt;
12585 ereport(DEBUG1,
12586 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12589 * Build a trigger call structure; we'll need it either way.
12591 trig.tgoid = InvalidOid;
12592 trig.tgname = conname;
12593 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12594 trig.tgisinternal = true;
12595 trig.tgconstrrelid = RelationGetRelid(pkrel);
12596 trig.tgconstrindid = pkindOid;
12597 trig.tgconstraint = constraintOid;
12598 trig.tgdeferrable = false;
12599 trig.tginitdeferred = false;
12600 /* we needn't fill in remaining fields */
12603 * See if we can do it with a single LEFT JOIN query. A false result
12604 * indicates we must proceed with the fire-the-trigger method. We can't do
12605 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
12606 * left joins.
12608 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
12609 return;
12612 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12613 * if that tuple had just been inserted. If any of those fail, it should
12614 * ereport(ERROR) and that's that.
12616 snapshot = RegisterSnapshot(GetLatestSnapshot());
12617 slot = table_slot_create(rel, NULL);
12618 scan = table_beginscan(rel, snapshot, 0, NULL);
12620 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12621 "validateForeignKeyConstraint",
12622 ALLOCSET_SMALL_SIZES);
12623 oldcxt = MemoryContextSwitchTo(perTupCxt);
12625 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12627 LOCAL_FCINFO(fcinfo, 0);
12628 TriggerData trigdata = {0};
12630 CHECK_FOR_INTERRUPTS();
12633 * Make a call to the trigger function
12635 * No parameters are passed, but we do set a context
12637 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12640 * We assume RI_FKey_check_ins won't look at flinfo...
12642 trigdata.type = T_TriggerData;
12643 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12644 trigdata.tg_relation = rel;
12645 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12646 trigdata.tg_trigslot = slot;
12647 trigdata.tg_trigger = &trig;
12649 fcinfo->context = (Node *) &trigdata;
12651 RI_FKey_check_ins(fcinfo);
12653 MemoryContextReset(perTupCxt);
12656 MemoryContextSwitchTo(oldcxt);
12657 MemoryContextDelete(perTupCxt);
12658 table_endscan(scan);
12659 UnregisterSnapshot(snapshot);
12660 ExecDropSingleTupleTableSlot(slot);
12664 * CreateFKCheckTrigger
12665 * Creates the insert (on_insert=true) or update "check" trigger that
12666 * implements a given foreign key
12668 * Returns the OID of the so created trigger.
12670 static Oid
12671 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12672 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12673 bool on_insert)
12675 ObjectAddress trigAddress;
12676 CreateTrigStmt *fk_trigger;
12679 * Note: for a self-referential FK (referencing and referenced tables are
12680 * the same), it is important that the ON UPDATE action fires before the
12681 * CHECK action, since both triggers will fire on the same row during an
12682 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12683 * state of the row. Triggers fire in name order, so we ensure this by
12684 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12685 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12687 fk_trigger = makeNode(CreateTrigStmt);
12688 fk_trigger->replace = false;
12689 fk_trigger->isconstraint = true;
12690 fk_trigger->trigname = "RI_ConstraintTrigger_c";
12691 fk_trigger->relation = NULL;
12693 /* Either ON INSERT or ON UPDATE */
12694 if (on_insert)
12696 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12697 fk_trigger->events = TRIGGER_TYPE_INSERT;
12699 else
12701 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12702 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12705 fk_trigger->args = NIL;
12706 fk_trigger->row = true;
12707 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12708 fk_trigger->columns = NIL;
12709 fk_trigger->whenClause = NULL;
12710 fk_trigger->transitionRels = NIL;
12711 fk_trigger->deferrable = fkconstraint->deferrable;
12712 fk_trigger->initdeferred = fkconstraint->initdeferred;
12713 fk_trigger->constrrel = NULL;
12715 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12716 constraintOid, indexOid, InvalidOid,
12717 parentTrigOid, NULL, true, false);
12719 /* Make changes-so-far visible */
12720 CommandCounterIncrement();
12722 return trigAddress.objectId;
12726 * createForeignKeyActionTriggers
12727 * Create the referenced-side "action" triggers that implement a foreign
12728 * key.
12730 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12731 * *updateTrigOid.
12733 static void
12734 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12735 Oid constraintOid, Oid indexOid,
12736 Oid parentDelTrigger, Oid parentUpdTrigger,
12737 Oid *deleteTrigOid, Oid *updateTrigOid)
12739 CreateTrigStmt *fk_trigger;
12740 ObjectAddress trigAddress;
12743 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12744 * DELETE action on the referenced table.
12746 fk_trigger = makeNode(CreateTrigStmt);
12747 fk_trigger->replace = false;
12748 fk_trigger->isconstraint = true;
12749 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12750 fk_trigger->relation = NULL;
12751 fk_trigger->args = NIL;
12752 fk_trigger->row = true;
12753 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12754 fk_trigger->events = TRIGGER_TYPE_DELETE;
12755 fk_trigger->columns = NIL;
12756 fk_trigger->whenClause = NULL;
12757 fk_trigger->transitionRels = NIL;
12758 fk_trigger->constrrel = NULL;
12760 switch (fkconstraint->fk_del_action)
12762 case FKCONSTR_ACTION_NOACTION:
12763 fk_trigger->deferrable = fkconstraint->deferrable;
12764 fk_trigger->initdeferred = fkconstraint->initdeferred;
12765 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12766 break;
12767 case FKCONSTR_ACTION_RESTRICT:
12768 fk_trigger->deferrable = false;
12769 fk_trigger->initdeferred = false;
12770 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12771 break;
12772 case FKCONSTR_ACTION_CASCADE:
12773 fk_trigger->deferrable = false;
12774 fk_trigger->initdeferred = false;
12775 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12776 break;
12777 case FKCONSTR_ACTION_SETNULL:
12778 fk_trigger->deferrable = false;
12779 fk_trigger->initdeferred = false;
12780 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12781 break;
12782 case FKCONSTR_ACTION_SETDEFAULT:
12783 fk_trigger->deferrable = false;
12784 fk_trigger->initdeferred = false;
12785 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12786 break;
12787 default:
12788 elog(ERROR, "unrecognized FK action type: %d",
12789 (int) fkconstraint->fk_del_action);
12790 break;
12793 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12794 RelationGetRelid(rel),
12795 constraintOid, indexOid, InvalidOid,
12796 parentDelTrigger, NULL, true, false);
12797 if (deleteTrigOid)
12798 *deleteTrigOid = trigAddress.objectId;
12800 /* Make changes-so-far visible */
12801 CommandCounterIncrement();
12804 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12805 * UPDATE action on the referenced table.
12807 fk_trigger = makeNode(CreateTrigStmt);
12808 fk_trigger->replace = false;
12809 fk_trigger->isconstraint = true;
12810 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12811 fk_trigger->relation = NULL;
12812 fk_trigger->args = NIL;
12813 fk_trigger->row = true;
12814 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12815 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12816 fk_trigger->columns = NIL;
12817 fk_trigger->whenClause = NULL;
12818 fk_trigger->transitionRels = NIL;
12819 fk_trigger->constrrel = NULL;
12821 switch (fkconstraint->fk_upd_action)
12823 case FKCONSTR_ACTION_NOACTION:
12824 fk_trigger->deferrable = fkconstraint->deferrable;
12825 fk_trigger->initdeferred = fkconstraint->initdeferred;
12826 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12827 break;
12828 case FKCONSTR_ACTION_RESTRICT:
12829 fk_trigger->deferrable = false;
12830 fk_trigger->initdeferred = false;
12831 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12832 break;
12833 case FKCONSTR_ACTION_CASCADE:
12834 fk_trigger->deferrable = false;
12835 fk_trigger->initdeferred = false;
12836 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12837 break;
12838 case FKCONSTR_ACTION_SETNULL:
12839 fk_trigger->deferrable = false;
12840 fk_trigger->initdeferred = false;
12841 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12842 break;
12843 case FKCONSTR_ACTION_SETDEFAULT:
12844 fk_trigger->deferrable = false;
12845 fk_trigger->initdeferred = false;
12846 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12847 break;
12848 default:
12849 elog(ERROR, "unrecognized FK action type: %d",
12850 (int) fkconstraint->fk_upd_action);
12851 break;
12854 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12855 RelationGetRelid(rel),
12856 constraintOid, indexOid, InvalidOid,
12857 parentUpdTrigger, NULL, true, false);
12858 if (updateTrigOid)
12859 *updateTrigOid = trigAddress.objectId;
12863 * createForeignKeyCheckTriggers
12864 * Create the referencing-side "check" triggers that implement a foreign
12865 * key.
12867 * Returns the OIDs of the so created triggers in *insertTrigOid and
12868 * *updateTrigOid.
12870 static void
12871 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12872 Constraint *fkconstraint, Oid constraintOid,
12873 Oid indexOid,
12874 Oid parentInsTrigger, Oid parentUpdTrigger,
12875 Oid *insertTrigOid, Oid *updateTrigOid)
12877 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12878 constraintOid, indexOid,
12879 parentInsTrigger, true);
12880 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12881 constraintOid, indexOid,
12882 parentUpdTrigger, false);
12886 * ALTER TABLE DROP CONSTRAINT
12888 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12890 static void
12891 ATExecDropConstraint(Relation rel, const char *constrName,
12892 DropBehavior behavior, bool recurse,
12893 bool missing_ok, LOCKMODE lockmode)
12895 Relation conrel;
12896 SysScanDesc scan;
12897 ScanKeyData skey[3];
12898 HeapTuple tuple;
12899 bool found = false;
12901 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12904 * Find and drop the target constraint
12906 ScanKeyInit(&skey[0],
12907 Anum_pg_constraint_conrelid,
12908 BTEqualStrategyNumber, F_OIDEQ,
12909 ObjectIdGetDatum(RelationGetRelid(rel)));
12910 ScanKeyInit(&skey[1],
12911 Anum_pg_constraint_contypid,
12912 BTEqualStrategyNumber, F_OIDEQ,
12913 ObjectIdGetDatum(InvalidOid));
12914 ScanKeyInit(&skey[2],
12915 Anum_pg_constraint_conname,
12916 BTEqualStrategyNumber, F_NAMEEQ,
12917 CStringGetDatum(constrName));
12918 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12919 true, NULL, 3, skey);
12921 /* There can be at most one matching row */
12922 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12924 List *readyRels = NIL;
12926 dropconstraint_internal(rel, tuple, behavior, recurse, false,
12927 missing_ok, &readyRels, lockmode);
12928 found = true;
12931 systable_endscan(scan);
12933 if (!found)
12935 if (!missing_ok)
12936 ereport(ERROR,
12937 errcode(ERRCODE_UNDEFINED_OBJECT),
12938 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12939 constrName, RelationGetRelationName(rel)));
12940 else
12941 ereport(NOTICE,
12942 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12943 constrName, RelationGetRelationName(rel)));
12946 table_close(conrel, RowExclusiveLock);
12950 * Remove a constraint, using its pg_constraint tuple
12952 * Implementation for ALTER TABLE DROP CONSTRAINT and ALTER TABLE ALTER COLUMN
12953 * DROP NOT NULL.
12955 * Returns the address of the constraint being removed.
12957 static ObjectAddress
12958 dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior,
12959 bool recurse, bool recursing, bool missing_ok, List **readyRels,
12960 LOCKMODE lockmode)
12962 Relation conrel;
12963 Form_pg_constraint con;
12964 ObjectAddress conobj;
12965 List *children;
12966 bool is_no_inherit_constraint = false;
12967 char *constrName;
12968 List *unconstrained_cols = NIL;
12969 char *colname = NULL;
12970 bool dropping_pk = false;
12972 if (list_member_oid(*readyRels, RelationGetRelid(rel)))
12973 return InvalidObjectAddress;
12974 *readyRels = lappend_oid(*readyRels, RelationGetRelid(rel));
12976 /* Guard against stack overflow due to overly deep inheritance tree. */
12977 check_stack_depth();
12979 /* At top level, permission check was done in ATPrepCmd, else do it */
12980 if (recursing)
12981 ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12983 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12985 con = (Form_pg_constraint) GETSTRUCT(constraintTup);
12986 constrName = NameStr(con->conname);
12989 * If we're asked to drop a constraint which is both defined locally and
12990 * inherited, we can simply mark it as no longer having a local
12991 * definition, and no further changes are required.
12993 * XXX We do this for not-null constraints only, not CHECK, because the
12994 * latter have historically not behaved this way and it might be confusing
12995 * to change the behavior now.
12997 if (con->contype == CONSTRAINT_NOTNULL &&
12998 con->conislocal && con->coninhcount > 0)
13000 HeapTuple copytup;
13002 copytup = heap_copytuple(constraintTup);
13003 con = (Form_pg_constraint) GETSTRUCT(copytup);
13004 con->conislocal = false;
13005 CatalogTupleUpdate(conrel, &copytup->t_self, copytup);
13006 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13008 CommandCounterIncrement();
13009 table_close(conrel, RowExclusiveLock);
13010 return conobj;
13013 /* Don't allow drop of inherited constraints */
13014 if (con->coninhcount > 0 && !recursing)
13015 ereport(ERROR,
13016 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13017 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
13018 constrName, RelationGetRelationName(rel))));
13021 * See if we have a not-null constraint or a PRIMARY KEY. If so, we have
13022 * more checks and actions below, so obtain the list of columns that are
13023 * constrained by the constraint being dropped.
13025 if (con->contype == CONSTRAINT_NOTNULL)
13027 AttrNumber colnum;
13029 colnum = extractNotNullColumn(constraintTup);
13030 unconstrained_cols = list_make1_int(colnum);
13031 colname = NameStr(TupleDescAttr(RelationGetDescr(rel),
13032 colnum - 1)->attname);
13034 else if (con->contype == CONSTRAINT_PRIMARY)
13036 Datum adatum;
13037 ArrayType *arr;
13038 int numkeys;
13039 bool isNull;
13040 int16 *attnums;
13042 dropping_pk = true;
13044 adatum = heap_getattr(constraintTup, Anum_pg_constraint_conkey,
13045 RelationGetDescr(conrel), &isNull);
13046 if (isNull)
13047 elog(ERROR, "null conkey for constraint %u", con->oid);
13048 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13049 numkeys = ARR_DIMS(arr)[0];
13050 if (ARR_NDIM(arr) != 1 ||
13051 numkeys < 0 ||
13052 ARR_HASNULL(arr) ||
13053 ARR_ELEMTYPE(arr) != INT2OID)
13054 elog(ERROR, "conkey is not a 1-D smallint array");
13055 attnums = (int16 *) ARR_DATA_PTR(arr);
13057 for (int i = 0; i < numkeys; i++)
13058 unconstrained_cols = lappend_int(unconstrained_cols, attnums[i]);
13061 is_no_inherit_constraint = con->connoinherit;
13064 * If it's a foreign-key constraint, we'd better lock the referenced table
13065 * and check that that's not in use, just as we've already done for the
13066 * constrained table (else we might, eg, be dropping a trigger that has
13067 * unfired events). But we can/must skip that in the self-referential
13068 * case.
13070 if (con->contype == CONSTRAINT_FOREIGN &&
13071 con->confrelid != RelationGetRelid(rel))
13073 Relation frel;
13075 /* Must match lock taken by RemoveTriggerById: */
13076 frel = table_open(con->confrelid, AccessExclusiveLock);
13077 CheckTableNotInUse(frel, "ALTER TABLE");
13078 table_close(frel, NoLock);
13082 * Perform the actual constraint deletion
13084 ObjectAddressSet(conobj, ConstraintRelationId, con->oid);
13085 performDeletion(&conobj, behavior, 0);
13088 * If this was a NOT NULL or the primary key, verify that we still have
13089 * constraints to support GENERATED AS IDENTITY or the replica identity.
13091 if (unconstrained_cols != NIL)
13093 Relation attrel;
13094 Bitmapset *pkcols;
13095 Bitmapset *ircols;
13097 /* Make implicit attnotnull changes visible */
13098 CommandCounterIncrement();
13100 attrel = table_open(AttributeRelationId, RowExclusiveLock);
13103 * We want to test columns for their presence in the primary key, but
13104 * only if we're not dropping it.
13106 pkcols = dropping_pk ? NULL :
13107 RelationGetIndexAttrBitmap(rel,
13108 INDEX_ATTR_BITMAP_PRIMARY_KEY);
13109 ircols = RelationGetIndexAttrBitmap(rel, INDEX_ATTR_BITMAP_IDENTITY_KEY);
13111 foreach_int(attnum, unconstrained_cols)
13113 HeapTuple atttup;
13114 HeapTuple contup;
13115 Form_pg_attribute attForm;
13116 char attidentity;
13119 * Obtain pg_attribute tuple and verify conditions on it.
13121 atttup = SearchSysCacheAttNum(RelationGetRelid(rel), attnum);
13122 if (!HeapTupleIsValid(atttup))
13123 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
13124 attnum, RelationGetRelid(rel));
13125 attForm = (Form_pg_attribute) GETSTRUCT(atttup);
13126 attidentity = attForm->attidentity;
13127 ReleaseSysCache(atttup);
13130 * Since the above deletion has been made visible, we can now
13131 * search for any remaining constraints on this column (or these
13132 * columns, in the case we're dropping a multicol primary key.)
13133 * Then, verify whether any further NOT NULL or primary key
13134 * exists: if none and this is a generated identity column or the
13135 * replica identity, abort the whole thing.
13137 contup = findNotNullConstraintAttnum(RelationGetRelid(rel), attnum);
13138 if (contup ||
13139 bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber,
13140 pkcols))
13141 continue;
13144 * It's not valid to drop the not-null constraint for a GENERATED
13145 * AS IDENTITY column.
13147 if (attidentity != '\0')
13148 ereport(ERROR,
13149 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
13150 errmsg("column \"%s\" of relation \"%s\" is an identity column",
13151 get_attname(RelationGetRelid(rel), attnum,
13152 false),
13153 RelationGetRelationName(rel)));
13156 * It's not valid to drop the not-null constraint for a column in
13157 * the replica identity index, either. (FULL is not affected.)
13159 if (bms_is_member(attnum - FirstLowInvalidHeapAttributeNumber, ircols))
13160 ereport(ERROR,
13161 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13162 errmsg("column \"%s\" is in index used as replica identity",
13163 get_attname(RelationGetRelid(rel), attnum, false)));
13165 table_close(attrel, RowExclusiveLock);
13169 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
13170 * are dropped via the dependency mechanism, so we're done here.
13172 if (con->contype != CONSTRAINT_CHECK &&
13173 con->contype != CONSTRAINT_NOTNULL &&
13174 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
13176 table_close(conrel, RowExclusiveLock);
13177 return conobj;
13181 * Propagate to children as appropriate. Unlike most other ALTER
13182 * routines, we have to do this one level of recursion at a time; we can't
13183 * use find_all_inheritors to do it in one pass.
13185 if (!is_no_inherit_constraint)
13186 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13187 else
13188 children = NIL;
13191 * For a partitioned table, if partitions exist and we are told not to
13192 * recurse, it's a user error. It doesn't make sense to have a constraint
13193 * be defined only on the parent, especially if it's a partitioned table.
13195 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
13196 children != NIL && !recurse)
13197 ereport(ERROR,
13198 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13199 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
13200 errhint("Do not specify the ONLY keyword.")));
13202 foreach_oid(childrelid, children)
13204 Relation childrel;
13205 HeapTuple tuple;
13206 Form_pg_constraint childcon;
13208 if (list_member_oid(*readyRels, childrelid))
13209 continue; /* child already processed */
13211 /* find_inheritance_children already got lock */
13212 childrel = table_open(childrelid, NoLock);
13213 CheckTableNotInUse(childrel, "ALTER TABLE");
13216 * We search for not-null constraints by column name, and others by
13217 * constraint name.
13219 if (con->contype == CONSTRAINT_NOTNULL)
13221 tuple = findNotNullConstraint(childrelid, colname);
13222 if (!HeapTupleIsValid(tuple))
13223 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13224 colname, RelationGetRelid(childrel));
13226 else
13228 SysScanDesc scan;
13229 ScanKeyData skey[3];
13231 ScanKeyInit(&skey[0],
13232 Anum_pg_constraint_conrelid,
13233 BTEqualStrategyNumber, F_OIDEQ,
13234 ObjectIdGetDatum(childrelid));
13235 ScanKeyInit(&skey[1],
13236 Anum_pg_constraint_contypid,
13237 BTEqualStrategyNumber, F_OIDEQ,
13238 ObjectIdGetDatum(InvalidOid));
13239 ScanKeyInit(&skey[2],
13240 Anum_pg_constraint_conname,
13241 BTEqualStrategyNumber, F_NAMEEQ,
13242 CStringGetDatum(constrName));
13243 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
13244 true, NULL, 3, skey);
13245 /* There can only be one, so no need to loop */
13246 tuple = systable_getnext(scan);
13247 if (!HeapTupleIsValid(tuple))
13248 ereport(ERROR,
13249 (errcode(ERRCODE_UNDEFINED_OBJECT),
13250 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13251 constrName,
13252 RelationGetRelationName(childrel))));
13253 tuple = heap_copytuple(tuple);
13254 systable_endscan(scan);
13257 childcon = (Form_pg_constraint) GETSTRUCT(tuple);
13259 /* Right now only CHECK and not-null constraints can be inherited */
13260 if (childcon->contype != CONSTRAINT_CHECK &&
13261 childcon->contype != CONSTRAINT_NOTNULL)
13262 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
13264 if (childcon->coninhcount <= 0) /* shouldn't happen */
13265 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
13266 childrelid, NameStr(childcon->conname));
13268 if (recurse)
13271 * If the child constraint has other definition sources, just
13272 * decrement its inheritance count; if not, recurse to delete it.
13274 if (childcon->coninhcount == 1 && !childcon->conislocal)
13276 /* Time to delete this child constraint, too */
13277 dropconstraint_internal(childrel, tuple, behavior,
13278 recurse, true, missing_ok, readyRels,
13279 lockmode);
13281 else
13283 /* Child constraint must survive my deletion */
13284 childcon->coninhcount--;
13285 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13287 /* Make update visible */
13288 CommandCounterIncrement();
13291 else
13294 * If we were told to drop ONLY in this table (no recursion) and
13295 * there are no further parents for this constraint, we need to
13296 * mark the inheritors' constraints as locally defined rather than
13297 * inherited.
13299 childcon->coninhcount--;
13300 if (childcon->coninhcount == 0)
13301 childcon->conislocal = true;
13303 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
13305 /* Make update visible */
13306 CommandCounterIncrement();
13309 heap_freetuple(tuple);
13311 table_close(childrel, NoLock);
13315 * In addition, when dropping a primary key from a legacy-inheritance
13316 * parent table, we must recurse to children to mark the corresponding NOT
13317 * NULL constraint as no longer inherited, or drop it if this its last
13318 * reference.
13320 if (con->contype == CONSTRAINT_PRIMARY &&
13321 rel->rd_rel->relkind == RELKIND_RELATION &&
13322 rel->rd_rel->relhassubclass)
13324 List *colnames = NIL;
13325 List *pkready = NIL;
13328 * Because primary keys are always marked as NO INHERIT, we don't have
13329 * a list of children yet, so obtain one now.
13331 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
13334 * Find out the list of column names to process. Fortunately, we
13335 * already have the list of column numbers.
13337 foreach_int(attnum, unconstrained_cols)
13339 colnames = lappend(colnames, get_attname(RelationGetRelid(rel),
13340 attnum, false));
13343 foreach_oid(childrelid, children)
13345 Relation childrel;
13347 if (list_member_oid(pkready, childrelid))
13348 continue; /* child already processed */
13350 /* find_inheritance_children already got lock */
13351 childrel = table_open(childrelid, NoLock);
13352 CheckTableNotInUse(childrel, "ALTER TABLE");
13354 foreach_ptr(char, colName, colnames)
13356 HeapTuple contup;
13358 contup = findNotNullConstraint(childrelid, colName);
13359 if (contup == NULL)
13360 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\", relation \"%s\"",
13361 colName, RelationGetRelationName(childrel));
13363 dropconstraint_internal(childrel, contup,
13364 DROP_RESTRICT, true, true,
13365 false, &pkready,
13366 lockmode);
13367 pkready = NIL;
13370 table_close(childrel, NoLock);
13372 pkready = lappend_oid(pkready, childrelid);
13376 table_close(conrel, RowExclusiveLock);
13378 return conobj;
13382 * ALTER COLUMN TYPE
13384 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
13385 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
13386 * transformed (and must be, because we rely on some transformed fields).
13388 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
13389 * table will be done "in parallel" during phase 3, so all the USING
13390 * expressions should be parsed assuming the original column types. Also,
13391 * this allows a USING expression to refer to a field that will be dropped.
13393 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
13394 * the first two execution steps in phase 2; they must not see the effects
13395 * of any other subcommand types, since the USING expressions are parsed
13396 * against the unmodified table's state.
13398 static void
13399 ATPrepAlterColumnType(List **wqueue,
13400 AlteredTableInfo *tab, Relation rel,
13401 bool recurse, bool recursing,
13402 AlterTableCmd *cmd, LOCKMODE lockmode,
13403 AlterTableUtilityContext *context)
13405 char *colName = cmd->name;
13406 ColumnDef *def = (ColumnDef *) cmd->def;
13407 TypeName *typeName = def->typeName;
13408 Node *transform = def->cooked_default;
13409 HeapTuple tuple;
13410 Form_pg_attribute attTup;
13411 AttrNumber attnum;
13412 Oid targettype;
13413 int32 targettypmod;
13414 Oid targetcollid;
13415 NewColumnValue *newval;
13416 ParseState *pstate = make_parsestate(NULL);
13417 AclResult aclresult;
13418 bool is_expr;
13420 if (rel->rd_rel->reloftype && !recursing)
13421 ereport(ERROR,
13422 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13423 errmsg("cannot alter column type of typed table")));
13425 /* lookup the attribute so we can check inheritance status */
13426 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13427 if (!HeapTupleIsValid(tuple))
13428 ereport(ERROR,
13429 (errcode(ERRCODE_UNDEFINED_COLUMN),
13430 errmsg("column \"%s\" of relation \"%s\" does not exist",
13431 colName, RelationGetRelationName(rel))));
13432 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
13433 attnum = attTup->attnum;
13435 /* Can't alter a system attribute */
13436 if (attnum <= 0)
13437 ereport(ERROR,
13438 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13439 errmsg("cannot alter system column \"%s\"",
13440 colName)));
13443 * Don't alter inherited columns. At outer level, there had better not be
13444 * any inherited definition; when recursing, we assume this was checked at
13445 * the parent level (see below).
13447 if (attTup->attinhcount > 0 && !recursing)
13448 ereport(ERROR,
13449 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13450 errmsg("cannot alter inherited column \"%s\"",
13451 colName)));
13453 /* Don't alter columns used in the partition key */
13454 if (has_partition_attrs(rel,
13455 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
13456 &is_expr))
13457 ereport(ERROR,
13458 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13459 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
13460 colName, RelationGetRelationName(rel))));
13462 /* Look up the target type */
13463 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
13465 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
13466 if (aclresult != ACLCHECK_OK)
13467 aclcheck_error_type(aclresult, targettype);
13469 /* And the collation */
13470 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13472 /* make sure datatype is legal for a column */
13473 CheckAttributeType(colName, targettype, targetcollid,
13474 list_make1_oid(rel->rd_rel->reltype),
13477 if (tab->relkind == RELKIND_RELATION ||
13478 tab->relkind == RELKIND_PARTITIONED_TABLE)
13481 * Set up an expression to transform the old data value to the new
13482 * type. If a USING option was given, use the expression as
13483 * transformed by transformAlterTableStmt, else just take the old
13484 * value and try to coerce it. We do this first so that type
13485 * incompatibility can be detected before we waste effort, and because
13486 * we need the expression to be parsed against the original table row
13487 * type.
13489 if (!transform)
13491 transform = (Node *) makeVar(1, attnum,
13492 attTup->atttypid, attTup->atttypmod,
13493 attTup->attcollation,
13497 transform = coerce_to_target_type(pstate,
13498 transform, exprType(transform),
13499 targettype, targettypmod,
13500 COERCION_ASSIGNMENT,
13501 COERCE_IMPLICIT_CAST,
13502 -1);
13503 if (transform == NULL)
13505 /* error text depends on whether USING was specified or not */
13506 if (def->cooked_default != NULL)
13507 ereport(ERROR,
13508 (errcode(ERRCODE_DATATYPE_MISMATCH),
13509 errmsg("result of USING clause for column \"%s\""
13510 " cannot be cast automatically to type %s",
13511 colName, format_type_be(targettype)),
13512 errhint("You might need to add an explicit cast.")));
13513 else
13514 ereport(ERROR,
13515 (errcode(ERRCODE_DATATYPE_MISMATCH),
13516 errmsg("column \"%s\" cannot be cast automatically to type %s",
13517 colName, format_type_be(targettype)),
13518 /* translator: USING is SQL, don't translate it */
13519 errhint("You might need to specify \"USING %s::%s\".",
13520 quote_identifier(colName),
13521 format_type_with_typemod(targettype,
13522 targettypmod))));
13525 /* Fix collations after all else */
13526 assign_expr_collations(pstate, transform);
13528 /* Plan the expr now so we can accurately assess the need to rewrite. */
13529 transform = (Node *) expression_planner((Expr *) transform);
13532 * Add a work queue item to make ATRewriteTable update the column
13533 * contents.
13535 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
13536 newval->attnum = attnum;
13537 newval->expr = (Expr *) transform;
13538 newval->is_generated = false;
13540 tab->newvals = lappend(tab->newvals, newval);
13541 if (ATColumnChangeRequiresRewrite(transform, attnum))
13542 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
13544 else if (transform)
13545 ereport(ERROR,
13546 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13547 errmsg("\"%s\" is not a table",
13548 RelationGetRelationName(rel))));
13550 if (!RELKIND_HAS_STORAGE(tab->relkind))
13553 * For relations without storage, do this check now. Regular tables
13554 * will check it later when the table is being rewritten.
13556 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
13559 ReleaseSysCache(tuple);
13562 * Recurse manually by queueing a new command for each child, if
13563 * necessary. We cannot apply ATSimpleRecursion here because we need to
13564 * remap attribute numbers in the USING expression, if any.
13566 * If we are told not to recurse, there had better not be any child
13567 * tables; else the alter would put them out of step.
13569 if (recurse)
13571 Oid relid = RelationGetRelid(rel);
13572 List *child_oids,
13573 *child_numparents;
13574 ListCell *lo,
13575 *li;
13577 child_oids = find_all_inheritors(relid, lockmode,
13578 &child_numparents);
13581 * find_all_inheritors does the recursive search of the inheritance
13582 * hierarchy, so all we have to do is process all of the relids in the
13583 * list that it returns.
13585 forboth(lo, child_oids, li, child_numparents)
13587 Oid childrelid = lfirst_oid(lo);
13588 int numparents = lfirst_int(li);
13589 Relation childrel;
13590 HeapTuple childtuple;
13591 Form_pg_attribute childattTup;
13593 if (childrelid == relid)
13594 continue;
13596 /* find_all_inheritors already got lock */
13597 childrel = relation_open(childrelid, NoLock);
13598 CheckTableNotInUse(childrel, "ALTER TABLE");
13601 * Verify that the child doesn't have any inherited definitions of
13602 * this column that came from outside this inheritance hierarchy.
13603 * (renameatt makes a similar test, though in a different way
13604 * because of its different recursion mechanism.)
13606 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
13607 colName);
13608 if (!HeapTupleIsValid(childtuple))
13609 ereport(ERROR,
13610 (errcode(ERRCODE_UNDEFINED_COLUMN),
13611 errmsg("column \"%s\" of relation \"%s\" does not exist",
13612 colName, RelationGetRelationName(childrel))));
13613 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
13615 if (childattTup->attinhcount > numparents)
13616 ereport(ERROR,
13617 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13618 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
13619 colName, RelationGetRelationName(childrel))));
13621 ReleaseSysCache(childtuple);
13624 * Remap the attribute numbers. If no USING expression was
13625 * specified, there is no need for this step.
13627 if (def->cooked_default)
13629 AttrMap *attmap;
13630 bool found_whole_row;
13632 /* create a copy to scribble on */
13633 cmd = copyObject(cmd);
13635 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
13636 RelationGetDescr(rel),
13637 false);
13638 ((ColumnDef *) cmd->def)->cooked_default =
13639 map_variable_attnos(def->cooked_default,
13640 1, 0,
13641 attmap,
13642 InvalidOid, &found_whole_row);
13643 if (found_whole_row)
13644 ereport(ERROR,
13645 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13646 errmsg("cannot convert whole-row table reference"),
13647 errdetail("USING expression contains a whole-row table reference.")));
13648 pfree(attmap);
13650 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
13651 relation_close(childrel, NoLock);
13654 else if (!recursing &&
13655 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
13656 ereport(ERROR,
13657 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
13658 errmsg("type of inherited column \"%s\" must be changed in child tables too",
13659 colName)));
13661 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
13662 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
13666 * When the data type of a column is changed, a rewrite might not be required
13667 * if the new type is sufficiently identical to the old one, and the USING
13668 * clause isn't trying to insert some other value. It's safe to skip the
13669 * rewrite in these cases:
13671 * - the old type is binary coercible to the new type
13672 * - the new type is an unconstrained domain over the old type
13673 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
13675 * In the case of a constrained domain, we could get by with scanning the
13676 * table and checking the constraint rather than actually rewriting it, but we
13677 * don't currently try to do that.
13679 static bool
13680 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
13682 Assert(expr != NULL);
13684 for (;;)
13686 /* only one varno, so no need to check that */
13687 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
13688 return false;
13689 else if (IsA(expr, RelabelType))
13690 expr = (Node *) ((RelabelType *) expr)->arg;
13691 else if (IsA(expr, CoerceToDomain))
13693 CoerceToDomain *d = (CoerceToDomain *) expr;
13695 if (DomainHasConstraints(d->resulttype))
13696 return true;
13697 expr = (Node *) d->arg;
13699 else if (IsA(expr, FuncExpr))
13701 FuncExpr *f = (FuncExpr *) expr;
13703 switch (f->funcid)
13705 case F_TIMESTAMPTZ_TIMESTAMP:
13706 case F_TIMESTAMP_TIMESTAMPTZ:
13707 if (TimestampTimestampTzRequiresRewrite())
13708 return true;
13709 else
13710 expr = linitial(f->args);
13711 break;
13712 default:
13713 return true;
13716 else
13717 return true;
13722 * ALTER COLUMN .. SET DATA TYPE
13724 * Return the address of the modified column.
13726 static ObjectAddress
13727 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13728 AlterTableCmd *cmd, LOCKMODE lockmode)
13730 char *colName = cmd->name;
13731 ColumnDef *def = (ColumnDef *) cmd->def;
13732 TypeName *typeName = def->typeName;
13733 HeapTuple heapTup;
13734 Form_pg_attribute attTup,
13735 attOldTup;
13736 AttrNumber attnum;
13737 HeapTuple typeTuple;
13738 Form_pg_type tform;
13739 Oid targettype;
13740 int32 targettypmod;
13741 Oid targetcollid;
13742 Node *defaultexpr;
13743 Relation attrelation;
13744 Relation depRel;
13745 ScanKeyData key[3];
13746 SysScanDesc scan;
13747 HeapTuple depTup;
13748 ObjectAddress address;
13751 * Clear all the missing values if we're rewriting the table, since this
13752 * renders them pointless.
13754 if (tab->rewrite)
13756 Relation newrel;
13758 newrel = table_open(RelationGetRelid(rel), NoLock);
13759 RelationClearMissing(newrel);
13760 relation_close(newrel, NoLock);
13761 /* make sure we don't conflict with later attribute modifications */
13762 CommandCounterIncrement();
13765 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13767 /* Look up the target column */
13768 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13769 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13770 ereport(ERROR,
13771 (errcode(ERRCODE_UNDEFINED_COLUMN),
13772 errmsg("column \"%s\" of relation \"%s\" does not exist",
13773 colName, RelationGetRelationName(rel))));
13774 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13775 attnum = attTup->attnum;
13776 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13778 /* Check for multiple ALTER TYPE on same column --- can't cope */
13779 if (attTup->atttypid != attOldTup->atttypid ||
13780 attTup->atttypmod != attOldTup->atttypmod)
13781 ereport(ERROR,
13782 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13783 errmsg("cannot alter type of column \"%s\" twice",
13784 colName)));
13786 /* Look up the target type (should not fail, since prep found it) */
13787 typeTuple = typenameType(NULL, typeName, &targettypmod);
13788 tform = (Form_pg_type) GETSTRUCT(typeTuple);
13789 targettype = tform->oid;
13790 /* And the collation */
13791 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13794 * If there is a default expression for the column, get it and ensure we
13795 * can coerce it to the new datatype. (We must do this before changing
13796 * the column type, because build_column_default itself will try to
13797 * coerce, and will not issue the error message we want if it fails.)
13799 * We remove any implicit coercion steps at the top level of the old
13800 * default expression; this has been agreed to satisfy the principle of
13801 * least surprise. (The conversion to the new column type should act like
13802 * it started from what the user sees as the stored expression, and the
13803 * implicit coercions aren't going to be shown.)
13805 if (attTup->atthasdef)
13807 defaultexpr = build_column_default(rel, attnum);
13808 Assert(defaultexpr);
13809 defaultexpr = strip_implicit_coercions(defaultexpr);
13810 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13811 defaultexpr, exprType(defaultexpr),
13812 targettype, targettypmod,
13813 COERCION_ASSIGNMENT,
13814 COERCE_IMPLICIT_CAST,
13815 -1);
13816 if (defaultexpr == NULL)
13818 if (attTup->attgenerated)
13819 ereport(ERROR,
13820 (errcode(ERRCODE_DATATYPE_MISMATCH),
13821 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13822 colName, format_type_be(targettype))));
13823 else
13824 ereport(ERROR,
13825 (errcode(ERRCODE_DATATYPE_MISMATCH),
13826 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13827 colName, format_type_be(targettype))));
13830 else
13831 defaultexpr = NULL;
13834 * Find everything that depends on the column (constraints, indexes, etc),
13835 * and record enough information to let us recreate the objects.
13837 * The actual recreation does not happen here, but only after we have
13838 * performed all the individual ALTER TYPE operations. We have to save
13839 * the info before executing ALTER TYPE, though, else the deparser will
13840 * get confused.
13842 RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13845 * Now scan for dependencies of this column on other things. The only
13846 * things we should find are the dependency on the column datatype and
13847 * possibly a collation dependency. Those can be removed.
13849 depRel = table_open(DependRelationId, RowExclusiveLock);
13851 ScanKeyInit(&key[0],
13852 Anum_pg_depend_classid,
13853 BTEqualStrategyNumber, F_OIDEQ,
13854 ObjectIdGetDatum(RelationRelationId));
13855 ScanKeyInit(&key[1],
13856 Anum_pg_depend_objid,
13857 BTEqualStrategyNumber, F_OIDEQ,
13858 ObjectIdGetDatum(RelationGetRelid(rel)));
13859 ScanKeyInit(&key[2],
13860 Anum_pg_depend_objsubid,
13861 BTEqualStrategyNumber, F_INT4EQ,
13862 Int32GetDatum((int32) attnum));
13864 scan = systable_beginscan(depRel, DependDependerIndexId, true,
13865 NULL, 3, key);
13867 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13869 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13870 ObjectAddress foundObject;
13872 foundObject.classId = foundDep->refclassid;
13873 foundObject.objectId = foundDep->refobjid;
13874 foundObject.objectSubId = foundDep->refobjsubid;
13876 if (foundDep->deptype != DEPENDENCY_NORMAL)
13877 elog(ERROR, "found unexpected dependency type '%c'",
13878 foundDep->deptype);
13879 if (!(foundDep->refclassid == TypeRelationId &&
13880 foundDep->refobjid == attTup->atttypid) &&
13881 !(foundDep->refclassid == CollationRelationId &&
13882 foundDep->refobjid == attTup->attcollation))
13883 elog(ERROR, "found unexpected dependency for column: %s",
13884 getObjectDescription(&foundObject, false));
13886 CatalogTupleDelete(depRel, &depTup->t_self);
13889 systable_endscan(scan);
13891 table_close(depRel, RowExclusiveLock);
13894 * Here we go --- change the recorded column type and collation. (Note
13895 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13896 * fix up the missing value if any.
13898 if (attTup->atthasmissing)
13900 Datum missingval;
13901 bool missingNull;
13903 /* if rewrite is true the missing value should already be cleared */
13904 Assert(tab->rewrite == 0);
13906 /* Get the missing value datum */
13907 missingval = heap_getattr(heapTup,
13908 Anum_pg_attribute_attmissingval,
13909 attrelation->rd_att,
13910 &missingNull);
13912 /* if it's a null array there is nothing to do */
13914 if (!missingNull)
13917 * Get the datum out of the array and repack it in a new array
13918 * built with the new type data. We assume that since the table
13919 * doesn't need rewriting, the actual Datum doesn't need to be
13920 * changed, only the array metadata.
13923 int one = 1;
13924 bool isNull;
13925 Datum valuesAtt[Natts_pg_attribute] = {0};
13926 bool nullsAtt[Natts_pg_attribute] = {0};
13927 bool replacesAtt[Natts_pg_attribute] = {0};
13928 HeapTuple newTup;
13930 missingval = array_get_element(missingval,
13932 &one,
13934 attTup->attlen,
13935 attTup->attbyval,
13936 attTup->attalign,
13937 &isNull);
13938 missingval = PointerGetDatum(construct_array(&missingval,
13940 targettype,
13941 tform->typlen,
13942 tform->typbyval,
13943 tform->typalign));
13945 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13946 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13947 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13949 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13950 valuesAtt, nullsAtt, replacesAtt);
13951 heap_freetuple(heapTup);
13952 heapTup = newTup;
13953 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13957 attTup->atttypid = targettype;
13958 attTup->atttypmod = targettypmod;
13959 attTup->attcollation = targetcollid;
13960 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13961 ereport(ERROR,
13962 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13963 errmsg("too many array dimensions"));
13964 attTup->attndims = list_length(typeName->arrayBounds);
13965 attTup->attlen = tform->typlen;
13966 attTup->attbyval = tform->typbyval;
13967 attTup->attalign = tform->typalign;
13968 attTup->attstorage = tform->typstorage;
13969 attTup->attcompression = InvalidCompressionMethod;
13971 ReleaseSysCache(typeTuple);
13973 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13975 table_close(attrelation, RowExclusiveLock);
13977 /* Install dependencies on new datatype and collation */
13978 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13979 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13982 * Drop any pg_statistic entry for the column, since it's now wrong type
13984 RemoveStatistics(RelationGetRelid(rel), attnum);
13986 InvokeObjectPostAlterHook(RelationRelationId,
13987 RelationGetRelid(rel), attnum);
13990 * Update the default, if present, by brute force --- remove and re-add
13991 * the default. Probably unsafe to take shortcuts, since the new version
13992 * may well have additional dependencies. (It's okay to do this now,
13993 * rather than after other ALTER TYPE commands, since the default won't
13994 * depend on other column types.)
13996 if (defaultexpr)
13999 * If it's a GENERATED default, drop its dependency records, in
14000 * particular its INTERNAL dependency on the column, which would
14001 * otherwise cause dependency.c to refuse to perform the deletion.
14003 if (attTup->attgenerated)
14005 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
14007 if (!OidIsValid(attrdefoid))
14008 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
14009 RelationGetRelid(rel), attnum);
14010 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
14014 * Make updates-so-far visible, particularly the new pg_attribute row
14015 * which will be updated again.
14017 CommandCounterIncrement();
14020 * We use RESTRICT here for safety, but at present we do not expect
14021 * anything to depend on the default.
14023 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
14024 true);
14026 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
14029 ObjectAddressSubSet(address, RelationRelationId,
14030 RelationGetRelid(rel), attnum);
14032 /* Cleanup */
14033 heap_freetuple(heapTup);
14035 return address;
14039 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
14040 * that depends on the column (constraints, indexes, etc), and record enough
14041 * information to let us recreate the objects.
14043 static void
14044 RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
14045 Relation rel, AttrNumber attnum, const char *colName)
14047 Relation depRel;
14048 ScanKeyData key[3];
14049 SysScanDesc scan;
14050 HeapTuple depTup;
14052 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
14054 depRel = table_open(DependRelationId, RowExclusiveLock);
14056 ScanKeyInit(&key[0],
14057 Anum_pg_depend_refclassid,
14058 BTEqualStrategyNumber, F_OIDEQ,
14059 ObjectIdGetDatum(RelationRelationId));
14060 ScanKeyInit(&key[1],
14061 Anum_pg_depend_refobjid,
14062 BTEqualStrategyNumber, F_OIDEQ,
14063 ObjectIdGetDatum(RelationGetRelid(rel)));
14064 ScanKeyInit(&key[2],
14065 Anum_pg_depend_refobjsubid,
14066 BTEqualStrategyNumber, F_INT4EQ,
14067 Int32GetDatum((int32) attnum));
14069 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14070 NULL, 3, key);
14072 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
14074 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
14075 ObjectAddress foundObject;
14077 foundObject.classId = foundDep->classid;
14078 foundObject.objectId = foundDep->objid;
14079 foundObject.objectSubId = foundDep->objsubid;
14081 switch (foundObject.classId)
14083 case RelationRelationId:
14085 char relKind = get_rel_relkind(foundObject.objectId);
14087 if (relKind == RELKIND_INDEX ||
14088 relKind == RELKIND_PARTITIONED_INDEX)
14090 Assert(foundObject.objectSubId == 0);
14091 RememberIndexForRebuilding(foundObject.objectId, tab);
14093 else if (relKind == RELKIND_SEQUENCE)
14096 * This must be a SERIAL column's sequence. We need
14097 * not do anything to it.
14099 Assert(foundObject.objectSubId == 0);
14101 else
14103 /* Not expecting any other direct dependencies... */
14104 elog(ERROR, "unexpected object depending on column: %s",
14105 getObjectDescription(&foundObject, false));
14107 break;
14110 case ConstraintRelationId:
14111 Assert(foundObject.objectSubId == 0);
14112 RememberConstraintForRebuilding(foundObject.objectId, tab);
14113 break;
14115 case ProcedureRelationId:
14118 * A new-style SQL function can depend on a column, if that
14119 * column is referenced in the parsed function body. Ideally
14120 * we'd automatically update the function by deparsing and
14121 * reparsing it, but that's risky and might well fail anyhow.
14122 * FIXME someday.
14124 * This is only a problem for AT_AlterColumnType, not
14125 * AT_SetExpression.
14127 if (subtype == AT_AlterColumnType)
14128 ereport(ERROR,
14129 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14130 errmsg("cannot alter type of a column used by a function or procedure"),
14131 errdetail("%s depends on column \"%s\"",
14132 getObjectDescription(&foundObject, false),
14133 colName)));
14134 break;
14136 case RewriteRelationId:
14139 * View/rule bodies have pretty much the same issues as
14140 * function bodies. FIXME someday.
14142 if (subtype == AT_AlterColumnType)
14143 ereport(ERROR,
14144 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14145 errmsg("cannot alter type of a column used by a view or rule"),
14146 errdetail("%s depends on column \"%s\"",
14147 getObjectDescription(&foundObject, false),
14148 colName)));
14149 break;
14151 case TriggerRelationId:
14154 * A trigger can depend on a column because the column is
14155 * specified as an update target, or because the column is
14156 * used in the trigger's WHEN condition. The first case would
14157 * not require any extra work, but the second case would
14158 * require updating the WHEN expression, which has the same
14159 * issues as above. Since we can't easily tell which case
14160 * applies, we punt for both. FIXME someday.
14162 if (subtype == AT_AlterColumnType)
14163 ereport(ERROR,
14164 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14165 errmsg("cannot alter type of a column used in a trigger definition"),
14166 errdetail("%s depends on column \"%s\"",
14167 getObjectDescription(&foundObject, false),
14168 colName)));
14169 break;
14171 case PolicyRelationId:
14174 * A policy can depend on a column because the column is
14175 * specified in the policy's USING or WITH CHECK qual
14176 * expressions. It might be possible to rewrite and recheck
14177 * the policy expression, but punt for now. It's certainly
14178 * easy enough to remove and recreate the policy; still, FIXME
14179 * someday.
14181 if (subtype == AT_AlterColumnType)
14182 ereport(ERROR,
14183 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14184 errmsg("cannot alter type of a column used in a policy definition"),
14185 errdetail("%s depends on column \"%s\"",
14186 getObjectDescription(&foundObject, false),
14187 colName)));
14188 break;
14190 case AttrDefaultRelationId:
14192 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
14194 if (col.objectId == RelationGetRelid(rel) &&
14195 col.objectSubId == attnum)
14198 * Ignore the column's own default expression. The
14199 * caller deals with it.
14202 else
14205 * This must be a reference from the expression of a
14206 * generated column elsewhere in the same table.
14207 * Changing the type/generated expression of a column
14208 * that is used by a generated column is not allowed
14209 * by SQL standard, so just punt for now. It might be
14210 * doable with some thinking and effort.
14212 if (subtype == AT_AlterColumnType)
14213 ereport(ERROR,
14214 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14215 errmsg("cannot alter type of a column used by a generated column"),
14216 errdetail("Column \"%s\" is used by generated column \"%s\".",
14217 colName,
14218 get_attname(col.objectId,
14219 col.objectSubId,
14220 false))));
14222 break;
14225 case StatisticExtRelationId:
14228 * Give the extended-stats machinery a chance to fix anything
14229 * that this column type change would break.
14231 RememberStatisticsForRebuilding(foundObject.objectId, tab);
14232 break;
14234 case PublicationRelRelationId:
14237 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
14238 * clause. Same issues as above. FIXME someday.
14240 if (subtype == AT_AlterColumnType)
14241 ereport(ERROR,
14242 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14243 errmsg("cannot alter type of a column used by a publication WHERE clause"),
14244 errdetail("%s depends on column \"%s\"",
14245 getObjectDescription(&foundObject, false),
14246 colName)));
14247 break;
14249 default:
14252 * We don't expect any other sorts of objects to depend on a
14253 * column.
14255 elog(ERROR, "unexpected object depending on column: %s",
14256 getObjectDescription(&foundObject, false));
14257 break;
14261 systable_endscan(scan);
14262 table_close(depRel, NoLock);
14266 * Subroutine for ATExecAlterColumnType: remember that a replica identity
14267 * needs to be reset.
14269 static void
14270 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
14272 if (!get_index_isreplident(indoid))
14273 return;
14275 if (tab->replicaIdentityIndex)
14276 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
14278 tab->replicaIdentityIndex = get_rel_name(indoid);
14282 * Subroutine for ATExecAlterColumnType: remember any clustered index.
14284 static void
14285 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
14287 if (!get_index_isclustered(indoid))
14288 return;
14290 if (tab->clusterOnIndex)
14291 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
14293 tab->clusterOnIndex = get_rel_name(indoid);
14297 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
14298 * to be rebuilt (which we might already know).
14300 static void
14301 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
14304 * This de-duplication check is critical for two independent reasons: we
14305 * mustn't try to recreate the same constraint twice, and if a constraint
14306 * depends on more than one column whose type is to be altered, we must
14307 * capture its definition string before applying any of the column type
14308 * changes. ruleutils.c will get confused if we ask again later.
14310 if (!list_member_oid(tab->changedConstraintOids, conoid))
14312 /* OK, capture the constraint's existing definition string */
14313 char *defstring = pg_get_constraintdef_command(conoid);
14314 Oid indoid;
14316 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
14317 conoid);
14318 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
14319 defstring);
14322 * For the index of a constraint, if any, remember if it is used for
14323 * the table's replica identity or if it is a clustered index, so that
14324 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
14325 * those properties.
14327 indoid = get_constraint_index(conoid);
14328 if (OidIsValid(indoid))
14330 RememberReplicaIdentityForRebuilding(indoid, tab);
14331 RememberClusterOnForRebuilding(indoid, tab);
14337 * Subroutine for ATExecAlterColumnType: remember that an index needs
14338 * to be rebuilt (which we might already know).
14340 static void
14341 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
14344 * This de-duplication check is critical for two independent reasons: we
14345 * mustn't try to recreate the same index twice, and if an index depends
14346 * on more than one column whose type is to be altered, we must capture
14347 * its definition string before applying any of the column type changes.
14348 * ruleutils.c will get confused if we ask again later.
14350 if (!list_member_oid(tab->changedIndexOids, indoid))
14353 * Before adding it as an index-to-rebuild, we'd better see if it
14354 * belongs to a constraint, and if so rebuild the constraint instead.
14355 * Typically this check fails, because constraint indexes normally
14356 * have only dependencies on their constraint. But it's possible for
14357 * such an index to also have direct dependencies on table columns,
14358 * for example with a partial exclusion constraint.
14360 Oid conoid = get_index_constraint(indoid);
14362 if (OidIsValid(conoid))
14364 RememberConstraintForRebuilding(conoid, tab);
14366 else
14368 /* OK, capture the index's existing definition string */
14369 char *defstring = pg_get_indexdef_string(indoid);
14371 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
14372 indoid);
14373 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
14374 defstring);
14377 * Remember if this index is used for the table's replica identity
14378 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
14379 * can queue up commands necessary to restore those properties.
14381 RememberReplicaIdentityForRebuilding(indoid, tab);
14382 RememberClusterOnForRebuilding(indoid, tab);
14388 * Subroutine for ATExecAlterColumnType: remember that a statistics object
14389 * needs to be rebuilt (which we might already know).
14391 static void
14392 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
14395 * This de-duplication check is critical for two independent reasons: we
14396 * mustn't try to recreate the same statistics object twice, and if the
14397 * statistics object depends on more than one column whose type is to be
14398 * altered, we must capture its definition string before applying any of
14399 * the type changes. ruleutils.c will get confused if we ask again later.
14401 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
14403 /* OK, capture the statistics object's existing definition string */
14404 char *defstring = pg_get_statisticsobjdef_string(stxoid);
14406 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
14407 stxoid);
14408 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
14409 defstring);
14414 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
14415 * operations for a particular relation. We have to drop and recreate all the
14416 * indexes and constraints that depend on the altered columns. We do the
14417 * actual dropping here, but re-creation is managed by adding work queue
14418 * entries to do those steps later.
14420 static void
14421 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
14423 ObjectAddress obj;
14424 ObjectAddresses *objects;
14425 ListCell *def_item;
14426 ListCell *oid_item;
14429 * Collect all the constraints and indexes to drop so we can process them
14430 * in a single call. That way we don't have to worry about dependencies
14431 * among them.
14433 objects = new_object_addresses();
14436 * Re-parse the index and constraint definitions, and attach them to the
14437 * appropriate work queue entries. We do this before dropping because in
14438 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
14439 * lock on the table the constraint is attached to, and we need to get
14440 * that before reparsing/dropping.
14442 * We can't rely on the output of deparsing to tell us which relation to
14443 * operate on, because concurrent activity might have made the name
14444 * resolve differently. Instead, we've got to use the OID of the
14445 * constraint or index we're processing to figure out which relation to
14446 * operate on.
14448 forboth(oid_item, tab->changedConstraintOids,
14449 def_item, tab->changedConstraintDefs)
14451 Oid oldId = lfirst_oid(oid_item);
14452 HeapTuple tup;
14453 Form_pg_constraint con;
14454 Oid relid;
14455 Oid confrelid;
14456 char contype;
14457 bool conislocal;
14459 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14460 if (!HeapTupleIsValid(tup)) /* should not happen */
14461 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14462 con = (Form_pg_constraint) GETSTRUCT(tup);
14463 if (OidIsValid(con->conrelid))
14464 relid = con->conrelid;
14465 else
14467 /* must be a domain constraint */
14468 relid = get_typ_typrelid(getBaseType(con->contypid));
14469 if (!OidIsValid(relid))
14470 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
14472 confrelid = con->confrelid;
14473 contype = con->contype;
14474 conislocal = con->conislocal;
14475 ReleaseSysCache(tup);
14477 ObjectAddressSet(obj, ConstraintRelationId, oldId);
14478 add_exact_object_address(&obj, objects);
14481 * If the constraint is inherited (only), we don't want to inject a
14482 * new definition here; it'll get recreated when
14483 * ATAddCheckNNConstraint recurses from adding the parent table's
14484 * constraint. But we had to carry the info this far so that we can
14485 * drop the constraint below.
14487 if (!conislocal)
14488 continue;
14491 * When rebuilding an FK constraint that references the table we're
14492 * modifying, we might not yet have any lock on the FK's table, so get
14493 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
14494 * step, so there's no value in asking for anything weaker.
14496 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
14497 LockRelationOid(relid, AccessExclusiveLock);
14499 ATPostAlterTypeParse(oldId, relid, confrelid,
14500 (char *) lfirst(def_item),
14501 wqueue, lockmode, tab->rewrite);
14503 forboth(oid_item, tab->changedIndexOids,
14504 def_item, tab->changedIndexDefs)
14506 Oid oldId = lfirst_oid(oid_item);
14507 Oid relid;
14509 relid = IndexGetRelation(oldId, false);
14510 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14511 (char *) lfirst(def_item),
14512 wqueue, lockmode, tab->rewrite);
14514 ObjectAddressSet(obj, RelationRelationId, oldId);
14515 add_exact_object_address(&obj, objects);
14518 /* add dependencies for new statistics */
14519 forboth(oid_item, tab->changedStatisticsOids,
14520 def_item, tab->changedStatisticsDefs)
14522 Oid oldId = lfirst_oid(oid_item);
14523 Oid relid;
14525 relid = StatisticsGetRelation(oldId, false);
14526 ATPostAlterTypeParse(oldId, relid, InvalidOid,
14527 (char *) lfirst(def_item),
14528 wqueue, lockmode, tab->rewrite);
14530 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
14531 add_exact_object_address(&obj, objects);
14535 * Queue up command to restore replica identity index marking
14537 if (tab->replicaIdentityIndex)
14539 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14540 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
14542 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
14543 subcmd->name = tab->replicaIdentityIndex;
14544 cmd->subtype = AT_ReplicaIdentity;
14545 cmd->def = (Node *) subcmd;
14547 /* do it after indexes and constraints */
14548 tab->subcmds[AT_PASS_OLD_CONSTR] =
14549 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14553 * Queue up command to restore marking of index used for cluster.
14555 if (tab->clusterOnIndex)
14557 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14559 cmd->subtype = AT_ClusterOn;
14560 cmd->name = tab->clusterOnIndex;
14562 /* do it after indexes and constraints */
14563 tab->subcmds[AT_PASS_OLD_CONSTR] =
14564 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14568 * It should be okay to use DROP_RESTRICT here, since nothing else should
14569 * be depending on these objects.
14571 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
14573 free_object_addresses(objects);
14576 * The objects will get recreated during subsequent passes over the work
14577 * queue.
14582 * Parse the previously-saved definition string for a constraint, index or
14583 * statistics object against the newly-established column data type(s), and
14584 * queue up the resulting command parsetrees for execution.
14586 * This might fail if, for example, you have a WHERE clause that uses an
14587 * operator that's not available for the new column type.
14589 static void
14590 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
14591 List **wqueue, LOCKMODE lockmode, bool rewrite)
14593 List *raw_parsetree_list;
14594 List *querytree_list;
14595 ListCell *list_item;
14596 Relation rel;
14599 * We expect that we will get only ALTER TABLE and CREATE INDEX
14600 * statements. Hence, there is no need to pass them through
14601 * parse_analyze_*() or the rewriter, but instead we need to pass them
14602 * through parse_utilcmd.c to make them ready for execution.
14604 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
14605 querytree_list = NIL;
14606 foreach(list_item, raw_parsetree_list)
14608 RawStmt *rs = lfirst_node(RawStmt, list_item);
14609 Node *stmt = rs->stmt;
14611 if (IsA(stmt, IndexStmt))
14612 querytree_list = lappend(querytree_list,
14613 transformIndexStmt(oldRelId,
14614 (IndexStmt *) stmt,
14615 cmd));
14616 else if (IsA(stmt, AlterTableStmt))
14618 List *beforeStmts;
14619 List *afterStmts;
14621 stmt = (Node *) transformAlterTableStmt(oldRelId,
14622 (AlterTableStmt *) stmt,
14623 cmd,
14624 &beforeStmts,
14625 &afterStmts);
14626 querytree_list = list_concat(querytree_list, beforeStmts);
14627 querytree_list = lappend(querytree_list, stmt);
14628 querytree_list = list_concat(querytree_list, afterStmts);
14630 else if (IsA(stmt, CreateStatsStmt))
14631 querytree_list = lappend(querytree_list,
14632 transformStatsStmt(oldRelId,
14633 (CreateStatsStmt *) stmt,
14634 cmd));
14635 else
14636 querytree_list = lappend(querytree_list, stmt);
14639 /* Caller should already have acquired whatever lock we need. */
14640 rel = relation_open(oldRelId, NoLock);
14643 * Attach each generated command to the proper place in the work queue.
14644 * Note this could result in creation of entirely new work-queue entries.
14646 * Also note that we have to tweak the command subtypes, because it turns
14647 * out that re-creation of indexes and constraints has to act a bit
14648 * differently from initial creation.
14650 foreach(list_item, querytree_list)
14652 Node *stm = (Node *) lfirst(list_item);
14653 AlteredTableInfo *tab;
14655 tab = ATGetQueueEntry(wqueue, rel);
14657 if (IsA(stm, IndexStmt))
14659 IndexStmt *stmt = (IndexStmt *) stm;
14660 AlterTableCmd *newcmd;
14662 if (!rewrite)
14663 TryReuseIndex(oldId, stmt);
14664 stmt->reset_default_tblspc = true;
14665 /* keep the index's comment */
14666 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
14668 newcmd = makeNode(AlterTableCmd);
14669 newcmd->subtype = AT_ReAddIndex;
14670 newcmd->def = (Node *) stmt;
14671 tab->subcmds[AT_PASS_OLD_INDEX] =
14672 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
14674 else if (IsA(stm, AlterTableStmt))
14676 AlterTableStmt *stmt = (AlterTableStmt *) stm;
14677 ListCell *lcmd;
14679 foreach(lcmd, stmt->cmds)
14681 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
14683 if (cmd->subtype == AT_AddIndex)
14685 IndexStmt *indstmt;
14686 Oid indoid;
14688 indstmt = castNode(IndexStmt, cmd->def);
14689 indoid = get_constraint_index(oldId);
14691 if (!rewrite)
14692 TryReuseIndex(indoid, indstmt);
14693 /* keep any comment on the index */
14694 indstmt->idxcomment = GetComment(indoid,
14695 RelationRelationId, 0);
14696 indstmt->reset_default_tblspc = true;
14698 cmd->subtype = AT_ReAddIndex;
14699 tab->subcmds[AT_PASS_OLD_INDEX] =
14700 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
14702 /* recreate any comment on the constraint */
14703 RebuildConstraintComment(tab,
14704 AT_PASS_OLD_INDEX,
14705 oldId,
14706 rel,
14707 NIL,
14708 indstmt->idxname);
14710 else if (cmd->subtype == AT_AddConstraint)
14712 Constraint *con = castNode(Constraint, cmd->def);
14714 con->old_pktable_oid = refRelId;
14715 /* rewriting neither side of a FK */
14716 if (con->contype == CONSTR_FOREIGN &&
14717 !rewrite && tab->rewrite == 0)
14718 TryReuseForeignKey(oldId, con);
14719 con->reset_default_tblspc = true;
14720 cmd->subtype = AT_ReAddConstraint;
14721 tab->subcmds[AT_PASS_OLD_CONSTR] =
14722 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14724 /* recreate any comment on the constraint */
14725 RebuildConstraintComment(tab,
14726 AT_PASS_OLD_CONSTR,
14727 oldId,
14728 rel,
14729 NIL,
14730 con->conname);
14732 else if (cmd->subtype == AT_SetAttNotNull)
14735 * We see this subtype when a primary key is created
14736 * internally, for example when it is replaced with a new
14737 * constraint (say because one of the columns changes
14738 * type); in this case we need to reinstate attnotnull,
14739 * because it was removed because of the drop of the old
14740 * PK. Schedule this subcommand to an upcoming AT pass.
14742 cmd->subtype = AT_SetAttNotNull;
14743 tab->subcmds[AT_PASS_OLD_COL_ATTRS] =
14744 lappend(tab->subcmds[AT_PASS_OLD_COL_ATTRS], cmd);
14746 else
14747 elog(ERROR, "unexpected statement subtype: %d",
14748 (int) cmd->subtype);
14751 else if (IsA(stm, AlterDomainStmt))
14753 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14755 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14757 Constraint *con = castNode(Constraint, stmt->def);
14758 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14760 cmd->subtype = AT_ReAddDomainConstraint;
14761 cmd->def = (Node *) stmt;
14762 tab->subcmds[AT_PASS_OLD_CONSTR] =
14763 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14765 /* recreate any comment on the constraint */
14766 RebuildConstraintComment(tab,
14767 AT_PASS_OLD_CONSTR,
14768 oldId,
14769 NULL,
14770 stmt->typeName,
14771 con->conname);
14773 else
14774 elog(ERROR, "unexpected statement subtype: %d",
14775 (int) stmt->subtype);
14777 else if (IsA(stm, CreateStatsStmt))
14779 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14780 AlterTableCmd *newcmd;
14782 /* keep the statistics object's comment */
14783 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14785 newcmd = makeNode(AlterTableCmd);
14786 newcmd->subtype = AT_ReAddStatistics;
14787 newcmd->def = (Node *) stmt;
14788 tab->subcmds[AT_PASS_MISC] =
14789 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14791 else
14792 elog(ERROR, "unexpected statement type: %d",
14793 (int) nodeTag(stm));
14796 relation_close(rel, NoLock);
14800 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14801 * for a table or domain constraint that is being rebuilt.
14803 * objid is the OID of the constraint.
14804 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14805 * as a string list) for a domain constraint.
14806 * (We could dig that info, as well as the conname, out of the pg_constraint
14807 * entry; but callers already have them so might as well pass them.)
14809 static void
14810 RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14811 Relation rel, List *domname,
14812 const char *conname)
14814 CommentStmt *cmd;
14815 char *comment_str;
14816 AlterTableCmd *newcmd;
14818 /* Look for comment for object wanted, and leave if none */
14819 comment_str = GetComment(objid, ConstraintRelationId, 0);
14820 if (comment_str == NULL)
14821 return;
14823 /* Build CommentStmt node, copying all input data for safety */
14824 cmd = makeNode(CommentStmt);
14825 if (rel)
14827 cmd->objtype = OBJECT_TABCONSTRAINT;
14828 cmd->object = (Node *)
14829 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14830 makeString(pstrdup(RelationGetRelationName(rel))),
14831 makeString(pstrdup(conname)));
14833 else
14835 cmd->objtype = OBJECT_DOMCONSTRAINT;
14836 cmd->object = (Node *)
14837 list_make2(makeTypeNameFromNameList(copyObject(domname)),
14838 makeString(pstrdup(conname)));
14840 cmd->comment = comment_str;
14842 /* Append it to list of commands */
14843 newcmd = makeNode(AlterTableCmd);
14844 newcmd->subtype = AT_ReAddComment;
14845 newcmd->def = (Node *) cmd;
14846 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14850 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14851 * for the real analysis, then mutates the IndexStmt based on that verdict.
14853 static void
14854 TryReuseIndex(Oid oldId, IndexStmt *stmt)
14856 if (CheckIndexCompatible(oldId,
14857 stmt->accessMethod,
14858 stmt->indexParams,
14859 stmt->excludeOpNames,
14860 stmt->iswithoutoverlaps))
14862 Relation irel = index_open(oldId, NoLock);
14864 /* If it's a partitioned index, there is no storage to share. */
14865 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14867 stmt->oldNumber = irel->rd_locator.relNumber;
14868 stmt->oldCreateSubid = irel->rd_createSubid;
14869 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14871 index_close(irel, NoLock);
14876 * Subroutine for ATPostAlterTypeParse().
14878 * Stash the old P-F equality operator into the Constraint node, for possible
14879 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14880 * this constraint can be skipped.
14882 static void
14883 TryReuseForeignKey(Oid oldId, Constraint *con)
14885 HeapTuple tup;
14886 Datum adatum;
14887 ArrayType *arr;
14888 Oid *rawarr;
14889 int numkeys;
14890 int i;
14892 Assert(con->contype == CONSTR_FOREIGN);
14893 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14895 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14896 if (!HeapTupleIsValid(tup)) /* should not happen */
14897 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14899 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14900 Anum_pg_constraint_conpfeqop);
14901 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14902 numkeys = ARR_DIMS(arr)[0];
14903 /* test follows the one in ri_FetchConstraintInfo() */
14904 if (ARR_NDIM(arr) != 1 ||
14905 ARR_HASNULL(arr) ||
14906 ARR_ELEMTYPE(arr) != OIDOID)
14907 elog(ERROR, "conpfeqop is not a 1-D Oid array");
14908 rawarr = (Oid *) ARR_DATA_PTR(arr);
14910 /* stash a List of the operator Oids in our Constraint node */
14911 for (i = 0; i < numkeys; i++)
14912 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14914 ReleaseSysCache(tup);
14918 * ALTER COLUMN .. OPTIONS ( ... )
14920 * Returns the address of the modified column
14922 static ObjectAddress
14923 ATExecAlterColumnGenericOptions(Relation rel,
14924 const char *colName,
14925 List *options,
14926 LOCKMODE lockmode)
14928 Relation ftrel;
14929 Relation attrel;
14930 ForeignServer *server;
14931 ForeignDataWrapper *fdw;
14932 HeapTuple tuple;
14933 HeapTuple newtuple;
14934 bool isnull;
14935 Datum repl_val[Natts_pg_attribute];
14936 bool repl_null[Natts_pg_attribute];
14937 bool repl_repl[Natts_pg_attribute];
14938 Datum datum;
14939 Form_pg_foreign_table fttableform;
14940 Form_pg_attribute atttableform;
14941 AttrNumber attnum;
14942 ObjectAddress address;
14944 if (options == NIL)
14945 return InvalidObjectAddress;
14947 /* First, determine FDW validator associated to the foreign table. */
14948 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14949 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14950 if (!HeapTupleIsValid(tuple))
14951 ereport(ERROR,
14952 (errcode(ERRCODE_UNDEFINED_OBJECT),
14953 errmsg("foreign table \"%s\" does not exist",
14954 RelationGetRelationName(rel))));
14955 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14956 server = GetForeignServer(fttableform->ftserver);
14957 fdw = GetForeignDataWrapper(server->fdwid);
14959 table_close(ftrel, AccessShareLock);
14960 ReleaseSysCache(tuple);
14962 attrel = table_open(AttributeRelationId, RowExclusiveLock);
14963 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14964 if (!HeapTupleIsValid(tuple))
14965 ereport(ERROR,
14966 (errcode(ERRCODE_UNDEFINED_COLUMN),
14967 errmsg("column \"%s\" of relation \"%s\" does not exist",
14968 colName, RelationGetRelationName(rel))));
14970 /* Prevent them from altering a system attribute */
14971 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14972 attnum = atttableform->attnum;
14973 if (attnum <= 0)
14974 ereport(ERROR,
14975 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14976 errmsg("cannot alter system column \"%s\"", colName)));
14979 /* Initialize buffers for new tuple values */
14980 memset(repl_val, 0, sizeof(repl_val));
14981 memset(repl_null, false, sizeof(repl_null));
14982 memset(repl_repl, false, sizeof(repl_repl));
14984 /* Extract the current options */
14985 datum = SysCacheGetAttr(ATTNAME,
14986 tuple,
14987 Anum_pg_attribute_attfdwoptions,
14988 &isnull);
14989 if (isnull)
14990 datum = PointerGetDatum(NULL);
14992 /* Transform the options */
14993 datum = transformGenericOptions(AttributeRelationId,
14994 datum,
14995 options,
14996 fdw->fdwvalidator);
14998 if (PointerIsValid(DatumGetPointer(datum)))
14999 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
15000 else
15001 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
15003 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
15005 /* Everything looks good - update the tuple */
15007 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
15008 repl_val, repl_null, repl_repl);
15010 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
15012 InvokeObjectPostAlterHook(RelationRelationId,
15013 RelationGetRelid(rel),
15014 atttableform->attnum);
15015 ObjectAddressSubSet(address, RelationRelationId,
15016 RelationGetRelid(rel), attnum);
15018 ReleaseSysCache(tuple);
15020 table_close(attrel, RowExclusiveLock);
15022 heap_freetuple(newtuple);
15024 return address;
15028 * ALTER TABLE OWNER
15030 * recursing is true if we are recursing from a table to its indexes,
15031 * sequences, or toast table. We don't allow the ownership of those things to
15032 * be changed separately from the parent table. Also, we can skip permission
15033 * checks (this is necessary not just an optimization, else we'd fail to
15034 * handle toast tables properly).
15036 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
15037 * free-standing composite type.
15039 void
15040 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
15042 Relation target_rel;
15043 Relation class_rel;
15044 HeapTuple tuple;
15045 Form_pg_class tuple_class;
15048 * Get exclusive lock till end of transaction on the target table. Use
15049 * relation_open so that we can work on indexes and sequences.
15051 target_rel = relation_open(relationOid, lockmode);
15053 /* Get its pg_class tuple, too */
15054 class_rel = table_open(RelationRelationId, RowExclusiveLock);
15056 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
15057 if (!HeapTupleIsValid(tuple))
15058 elog(ERROR, "cache lookup failed for relation %u", relationOid);
15059 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
15061 /* Can we change the ownership of this tuple? */
15062 switch (tuple_class->relkind)
15064 case RELKIND_RELATION:
15065 case RELKIND_VIEW:
15066 case RELKIND_MATVIEW:
15067 case RELKIND_FOREIGN_TABLE:
15068 case RELKIND_PARTITIONED_TABLE:
15069 /* ok to change owner */
15070 break;
15071 case RELKIND_INDEX:
15072 if (!recursing)
15075 * Because ALTER INDEX OWNER used to be allowed, and in fact
15076 * is generated by old versions of pg_dump, we give a warning
15077 * and do nothing rather than erroring out. Also, to avoid
15078 * unnecessary chatter while restoring those old dumps, say
15079 * nothing at all if the command would be a no-op anyway.
15081 if (tuple_class->relowner != newOwnerId)
15082 ereport(WARNING,
15083 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15084 errmsg("cannot change owner of index \"%s\"",
15085 NameStr(tuple_class->relname)),
15086 errhint("Change the ownership of the index's table instead.")));
15087 /* quick hack to exit via the no-op path */
15088 newOwnerId = tuple_class->relowner;
15090 break;
15091 case RELKIND_PARTITIONED_INDEX:
15092 if (recursing)
15093 break;
15094 ereport(ERROR,
15095 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15096 errmsg("cannot change owner of index \"%s\"",
15097 NameStr(tuple_class->relname)),
15098 errhint("Change the ownership of the index's table instead.")));
15099 break;
15100 case RELKIND_SEQUENCE:
15101 if (!recursing &&
15102 tuple_class->relowner != newOwnerId)
15104 /* if it's an owned sequence, disallow changing it by itself */
15105 Oid tableId;
15106 int32 colId;
15108 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
15109 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
15110 ereport(ERROR,
15111 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15112 errmsg("cannot change owner of sequence \"%s\"",
15113 NameStr(tuple_class->relname)),
15114 errdetail("Sequence \"%s\" is linked to table \"%s\".",
15115 NameStr(tuple_class->relname),
15116 get_rel_name(tableId))));
15118 break;
15119 case RELKIND_COMPOSITE_TYPE:
15120 if (recursing)
15121 break;
15122 ereport(ERROR,
15123 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15124 errmsg("\"%s\" is a composite type",
15125 NameStr(tuple_class->relname)),
15126 /* translator: %s is an SQL ALTER command */
15127 errhint("Use %s instead.",
15128 "ALTER TYPE")));
15129 break;
15130 case RELKIND_TOASTVALUE:
15131 if (recursing)
15132 break;
15133 /* FALL THRU */
15134 default:
15135 ereport(ERROR,
15136 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15137 errmsg("cannot change owner of relation \"%s\"",
15138 NameStr(tuple_class->relname)),
15139 errdetail_relkind_not_supported(tuple_class->relkind)));
15143 * If the new owner is the same as the existing owner, consider the
15144 * command to have succeeded. This is for dump restoration purposes.
15146 if (tuple_class->relowner != newOwnerId)
15148 Datum repl_val[Natts_pg_class];
15149 bool repl_null[Natts_pg_class];
15150 bool repl_repl[Natts_pg_class];
15151 Acl *newAcl;
15152 Datum aclDatum;
15153 bool isNull;
15154 HeapTuple newtuple;
15156 /* skip permission checks when recursing to index or toast table */
15157 if (!recursing)
15159 /* Superusers can always do it */
15160 if (!superuser())
15162 Oid namespaceOid = tuple_class->relnamespace;
15163 AclResult aclresult;
15165 /* Otherwise, must be owner of the existing object */
15166 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
15167 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
15168 RelationGetRelationName(target_rel));
15170 /* Must be able to become new owner */
15171 check_can_set_role(GetUserId(), newOwnerId);
15173 /* New owner must have CREATE privilege on namespace */
15174 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
15175 ACL_CREATE);
15176 if (aclresult != ACLCHECK_OK)
15177 aclcheck_error(aclresult, OBJECT_SCHEMA,
15178 get_namespace_name(namespaceOid));
15182 memset(repl_null, false, sizeof(repl_null));
15183 memset(repl_repl, false, sizeof(repl_repl));
15185 repl_repl[Anum_pg_class_relowner - 1] = true;
15186 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
15189 * Determine the modified ACL for the new owner. This is only
15190 * necessary when the ACL is non-null.
15192 aclDatum = SysCacheGetAttr(RELOID, tuple,
15193 Anum_pg_class_relacl,
15194 &isNull);
15195 if (!isNull)
15197 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15198 tuple_class->relowner, newOwnerId);
15199 repl_repl[Anum_pg_class_relacl - 1] = true;
15200 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
15203 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
15205 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
15207 heap_freetuple(newtuple);
15210 * We must similarly update any per-column ACLs to reflect the new
15211 * owner; for neatness reasons that's split out as a subroutine.
15213 change_owner_fix_column_acls(relationOid,
15214 tuple_class->relowner,
15215 newOwnerId);
15218 * Update owner dependency reference, if any. A composite type has
15219 * none, because it's tracked for the pg_type entry instead of here;
15220 * indexes and TOAST tables don't have their own entries either.
15222 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
15223 tuple_class->relkind != RELKIND_INDEX &&
15224 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
15225 tuple_class->relkind != RELKIND_TOASTVALUE)
15226 changeDependencyOnOwner(RelationRelationId, relationOid,
15227 newOwnerId);
15230 * Also change the ownership of the table's row type, if it has one
15232 if (OidIsValid(tuple_class->reltype))
15233 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
15236 * If we are operating on a table or materialized view, also change
15237 * the ownership of any indexes and sequences that belong to the
15238 * relation, as well as its toast table (if it has one).
15240 if (tuple_class->relkind == RELKIND_RELATION ||
15241 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
15242 tuple_class->relkind == RELKIND_MATVIEW ||
15243 tuple_class->relkind == RELKIND_TOASTVALUE)
15245 List *index_oid_list;
15246 ListCell *i;
15248 /* Find all the indexes belonging to this relation */
15249 index_oid_list = RelationGetIndexList(target_rel);
15251 /* For each index, recursively change its ownership */
15252 foreach(i, index_oid_list)
15253 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
15255 list_free(index_oid_list);
15258 /* If it has a toast table, recurse to change its ownership */
15259 if (tuple_class->reltoastrelid != InvalidOid)
15260 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
15261 true, lockmode);
15263 /* If it has dependent sequences, recurse to change them too */
15264 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
15267 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
15269 ReleaseSysCache(tuple);
15270 table_close(class_rel, RowExclusiveLock);
15271 relation_close(target_rel, NoLock);
15275 * change_owner_fix_column_acls
15277 * Helper function for ATExecChangeOwner. Scan the columns of the table
15278 * and fix any non-null column ACLs to reflect the new owner.
15280 static void
15281 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
15283 Relation attRelation;
15284 SysScanDesc scan;
15285 ScanKeyData key[1];
15286 HeapTuple attributeTuple;
15288 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
15289 ScanKeyInit(&key[0],
15290 Anum_pg_attribute_attrelid,
15291 BTEqualStrategyNumber, F_OIDEQ,
15292 ObjectIdGetDatum(relationOid));
15293 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
15294 true, NULL, 1, key);
15295 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15297 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15298 Datum repl_val[Natts_pg_attribute];
15299 bool repl_null[Natts_pg_attribute];
15300 bool repl_repl[Natts_pg_attribute];
15301 Acl *newAcl;
15302 Datum aclDatum;
15303 bool isNull;
15304 HeapTuple newtuple;
15306 /* Ignore dropped columns */
15307 if (att->attisdropped)
15308 continue;
15310 aclDatum = heap_getattr(attributeTuple,
15311 Anum_pg_attribute_attacl,
15312 RelationGetDescr(attRelation),
15313 &isNull);
15314 /* Null ACLs do not require changes */
15315 if (isNull)
15316 continue;
15318 memset(repl_null, false, sizeof(repl_null));
15319 memset(repl_repl, false, sizeof(repl_repl));
15321 newAcl = aclnewowner(DatumGetAclP(aclDatum),
15322 oldOwnerId, newOwnerId);
15323 repl_repl[Anum_pg_attribute_attacl - 1] = true;
15324 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
15326 newtuple = heap_modify_tuple(attributeTuple,
15327 RelationGetDescr(attRelation),
15328 repl_val, repl_null, repl_repl);
15330 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
15332 heap_freetuple(newtuple);
15334 systable_endscan(scan);
15335 table_close(attRelation, RowExclusiveLock);
15339 * change_owner_recurse_to_sequences
15341 * Helper function for ATExecChangeOwner. Examines pg_depend searching
15342 * for sequences that are dependent on serial columns, and changes their
15343 * ownership.
15345 static void
15346 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
15348 Relation depRel;
15349 SysScanDesc scan;
15350 ScanKeyData key[2];
15351 HeapTuple tup;
15354 * SERIAL sequences are those having an auto dependency on one of the
15355 * table's columns (we don't care *which* column, exactly).
15357 depRel = table_open(DependRelationId, AccessShareLock);
15359 ScanKeyInit(&key[0],
15360 Anum_pg_depend_refclassid,
15361 BTEqualStrategyNumber, F_OIDEQ,
15362 ObjectIdGetDatum(RelationRelationId));
15363 ScanKeyInit(&key[1],
15364 Anum_pg_depend_refobjid,
15365 BTEqualStrategyNumber, F_OIDEQ,
15366 ObjectIdGetDatum(relationOid));
15367 /* we leave refobjsubid unspecified */
15369 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
15370 NULL, 2, key);
15372 while (HeapTupleIsValid(tup = systable_getnext(scan)))
15374 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
15375 Relation seqRel;
15377 /* skip dependencies other than auto dependencies on columns */
15378 if (depForm->refobjsubid == 0 ||
15379 depForm->classid != RelationRelationId ||
15380 depForm->objsubid != 0 ||
15381 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
15382 continue;
15384 /* Use relation_open just in case it's an index */
15385 seqRel = relation_open(depForm->objid, lockmode);
15387 /* skip non-sequence relations */
15388 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
15390 /* No need to keep the lock */
15391 relation_close(seqRel, lockmode);
15392 continue;
15395 /* We don't need to close the sequence while we alter it. */
15396 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
15398 /* Now we can close it. Keep the lock till end of transaction. */
15399 relation_close(seqRel, NoLock);
15402 systable_endscan(scan);
15404 relation_close(depRel, AccessShareLock);
15408 * ALTER TABLE CLUSTER ON
15410 * The only thing we have to do is to change the indisclustered bits.
15412 * Return the address of the new clustering index.
15414 static ObjectAddress
15415 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
15417 Oid indexOid;
15418 ObjectAddress address;
15420 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
15422 if (!OidIsValid(indexOid))
15423 ereport(ERROR,
15424 (errcode(ERRCODE_UNDEFINED_OBJECT),
15425 errmsg("index \"%s\" for table \"%s\" does not exist",
15426 indexName, RelationGetRelationName(rel))));
15428 /* Check index is valid to cluster on */
15429 check_index_is_clusterable(rel, indexOid, lockmode);
15431 /* And do the work */
15432 mark_index_clustered(rel, indexOid, false);
15434 ObjectAddressSet(address,
15435 RelationRelationId, indexOid);
15437 return address;
15441 * ALTER TABLE SET WITHOUT CLUSTER
15443 * We have to find any indexes on the table that have indisclustered bit
15444 * set and turn it off.
15446 static void
15447 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
15449 mark_index_clustered(rel, InvalidOid, false);
15453 * Preparation phase for SET ACCESS METHOD
15455 * Check that the access method exists and determine whether a change is
15456 * actually needed.
15458 static void
15459 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
15461 Oid amoid;
15464 * Look up the access method name and check that it differs from the
15465 * table's current AM. If DEFAULT was specified for a partitioned table
15466 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
15468 if (amname != NULL)
15469 amoid = get_table_am_oid(amname, false);
15470 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15471 amoid = InvalidOid;
15472 else
15473 amoid = get_table_am_oid(default_table_access_method, false);
15475 /* if it's a match, phase 3 doesn't need to do anything */
15476 if (rel->rd_rel->relam == amoid)
15477 return;
15479 /* Save info for Phase 3 to do the real work */
15480 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
15481 tab->newAccessMethod = amoid;
15482 tab->chgAccessMethod = true;
15486 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
15487 * storage that have an interest in preserving AM.
15489 * Since these have no storage, setting the access method is a catalog only
15490 * operation.
15492 static void
15493 ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
15495 Relation pg_class;
15496 Oid oldAccessMethodId;
15497 HeapTuple tuple;
15498 Form_pg_class rd_rel;
15499 Oid reloid = RelationGetRelid(rel);
15502 * Shouldn't be called on relations having storage; these are processed in
15503 * phase 3.
15505 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15507 /* Get a modifiable copy of the relation's pg_class row. */
15508 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15510 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
15511 if (!HeapTupleIsValid(tuple))
15512 elog(ERROR, "cache lookup failed for relation %u", reloid);
15513 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
15515 /* Update the pg_class row. */
15516 oldAccessMethodId = rd_rel->relam;
15517 rd_rel->relam = newAccessMethodId;
15519 /* Leave if no update required */
15520 if (rd_rel->relam == oldAccessMethodId)
15522 heap_freetuple(tuple);
15523 table_close(pg_class, RowExclusiveLock);
15524 return;
15527 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
15530 * Update the dependency on the new access method. No dependency is added
15531 * if the new access method is InvalidOid (default case). Be very careful
15532 * that this has to compare the previous value stored in pg_class with the
15533 * new one.
15535 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
15537 ObjectAddress relobj,
15538 referenced;
15541 * New access method is defined and there was no dependency
15542 * previously, so record a new one.
15544 ObjectAddressSet(relobj, RelationRelationId, reloid);
15545 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
15546 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
15548 else if (OidIsValid(oldAccessMethodId) &&
15549 !OidIsValid(rd_rel->relam))
15552 * There was an access method defined, and no new one, so just remove
15553 * the existing dependency.
15555 deleteDependencyRecordsForClass(RelationRelationId, reloid,
15556 AccessMethodRelationId,
15557 DEPENDENCY_NORMAL);
15559 else
15561 Assert(OidIsValid(oldAccessMethodId) &&
15562 OidIsValid(rd_rel->relam));
15564 /* Both are valid, so update the dependency */
15565 changeDependencyFor(RelationRelationId, reloid,
15566 AccessMethodRelationId,
15567 oldAccessMethodId, rd_rel->relam);
15570 /* make the relam and dependency changes visible */
15571 CommandCounterIncrement();
15573 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15575 heap_freetuple(tuple);
15576 table_close(pg_class, RowExclusiveLock);
15580 * ALTER TABLE SET TABLESPACE
15582 static void
15583 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
15585 Oid tablespaceId;
15587 /* Check that the tablespace exists */
15588 tablespaceId = get_tablespace_oid(tablespacename, false);
15590 /* Check permissions except when moving to database's default */
15591 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
15593 AclResult aclresult;
15595 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
15596 if (aclresult != ACLCHECK_OK)
15597 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
15600 /* Save info for Phase 3 to do the real work */
15601 if (OidIsValid(tab->newTableSpace))
15602 ereport(ERROR,
15603 (errcode(ERRCODE_SYNTAX_ERROR),
15604 errmsg("cannot have multiple SET TABLESPACE subcommands")));
15606 tab->newTableSpace = tablespaceId;
15610 * Set, reset, or replace reloptions.
15612 static void
15613 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
15614 LOCKMODE lockmode)
15616 Oid relid;
15617 Relation pgclass;
15618 HeapTuple tuple;
15619 HeapTuple newtuple;
15620 Datum datum;
15621 bool isnull;
15622 Datum newOptions;
15623 Datum repl_val[Natts_pg_class];
15624 bool repl_null[Natts_pg_class];
15625 bool repl_repl[Natts_pg_class];
15626 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
15628 if (defList == NIL && operation != AT_ReplaceRelOptions)
15629 return; /* nothing to do */
15631 pgclass = table_open(RelationRelationId, RowExclusiveLock);
15633 /* Fetch heap tuple */
15634 relid = RelationGetRelid(rel);
15635 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
15636 if (!HeapTupleIsValid(tuple))
15637 elog(ERROR, "cache lookup failed for relation %u", relid);
15639 if (operation == AT_ReplaceRelOptions)
15642 * If we're supposed to replace the reloptions list, we just pretend
15643 * there were none before.
15645 datum = (Datum) 0;
15646 isnull = true;
15648 else
15650 /* Get the old reloptions */
15651 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15652 &isnull);
15655 /* Generate new proposed reloptions (text array) */
15656 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15657 defList, NULL, validnsps, false,
15658 operation == AT_ResetRelOptions);
15660 /* Validate */
15661 switch (rel->rd_rel->relkind)
15663 case RELKIND_RELATION:
15664 case RELKIND_TOASTVALUE:
15665 case RELKIND_MATVIEW:
15666 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
15667 break;
15668 case RELKIND_PARTITIONED_TABLE:
15669 (void) partitioned_table_reloptions(newOptions, true);
15670 break;
15671 case RELKIND_VIEW:
15672 (void) view_reloptions(newOptions, true);
15673 break;
15674 case RELKIND_INDEX:
15675 case RELKIND_PARTITIONED_INDEX:
15676 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
15677 break;
15678 default:
15679 ereport(ERROR,
15680 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15681 errmsg("cannot set options for relation \"%s\"",
15682 RelationGetRelationName(rel)),
15683 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
15684 break;
15687 /* Special-case validation of view options */
15688 if (rel->rd_rel->relkind == RELKIND_VIEW)
15690 Query *view_query = get_view_query(rel);
15691 List *view_options = untransformRelOptions(newOptions);
15692 ListCell *cell;
15693 bool check_option = false;
15695 foreach(cell, view_options)
15697 DefElem *defel = (DefElem *) lfirst(cell);
15699 if (strcmp(defel->defname, "check_option") == 0)
15700 check_option = true;
15704 * If the check option is specified, look to see if the view is
15705 * actually auto-updatable or not.
15707 if (check_option)
15709 const char *view_updatable_error =
15710 view_query_is_auto_updatable(view_query, true);
15712 if (view_updatable_error)
15713 ereport(ERROR,
15714 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15715 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
15716 errhint("%s", _(view_updatable_error))));
15721 * All we need do here is update the pg_class row; the new options will be
15722 * propagated into relcaches during post-commit cache inval.
15724 memset(repl_val, 0, sizeof(repl_val));
15725 memset(repl_null, false, sizeof(repl_null));
15726 memset(repl_repl, false, sizeof(repl_repl));
15728 if (newOptions != (Datum) 0)
15729 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15730 else
15731 repl_null[Anum_pg_class_reloptions - 1] = true;
15733 repl_repl[Anum_pg_class_reloptions - 1] = true;
15735 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15736 repl_val, repl_null, repl_repl);
15738 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15740 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15742 heap_freetuple(newtuple);
15744 ReleaseSysCache(tuple);
15746 /* repeat the whole exercise for the toast table, if there's one */
15747 if (OidIsValid(rel->rd_rel->reltoastrelid))
15749 Relation toastrel;
15750 Oid toastid = rel->rd_rel->reltoastrelid;
15752 toastrel = table_open(toastid, lockmode);
15754 /* Fetch heap tuple */
15755 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15756 if (!HeapTupleIsValid(tuple))
15757 elog(ERROR, "cache lookup failed for relation %u", toastid);
15759 if (operation == AT_ReplaceRelOptions)
15762 * If we're supposed to replace the reloptions list, we just
15763 * pretend there were none before.
15765 datum = (Datum) 0;
15766 isnull = true;
15768 else
15770 /* Get the old reloptions */
15771 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15772 &isnull);
15775 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15776 defList, "toast", validnsps, false,
15777 operation == AT_ResetRelOptions);
15779 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15781 memset(repl_val, 0, sizeof(repl_val));
15782 memset(repl_null, false, sizeof(repl_null));
15783 memset(repl_repl, false, sizeof(repl_repl));
15785 if (newOptions != (Datum) 0)
15786 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15787 else
15788 repl_null[Anum_pg_class_reloptions - 1] = true;
15790 repl_repl[Anum_pg_class_reloptions - 1] = true;
15792 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15793 repl_val, repl_null, repl_repl);
15795 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15797 InvokeObjectPostAlterHookArg(RelationRelationId,
15798 RelationGetRelid(toastrel), 0,
15799 InvalidOid, true);
15801 heap_freetuple(newtuple);
15803 ReleaseSysCache(tuple);
15805 table_close(toastrel, NoLock);
15808 table_close(pgclass, RowExclusiveLock);
15812 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15813 * rewriting to be done, so we just want to copy the data as fast as possible.
15815 static void
15816 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15818 Relation rel;
15819 Oid reltoastrelid;
15820 RelFileNumber newrelfilenumber;
15821 RelFileLocator newrlocator;
15822 List *reltoastidxids = NIL;
15823 ListCell *lc;
15826 * Need lock here in case we are recursing to toast table or index
15828 rel = relation_open(tableOid, lockmode);
15830 /* Check first if relation can be moved to new tablespace */
15831 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15833 InvokeObjectPostAlterHook(RelationRelationId,
15834 RelationGetRelid(rel), 0);
15835 relation_close(rel, NoLock);
15836 return;
15839 reltoastrelid = rel->rd_rel->reltoastrelid;
15840 /* Fetch the list of indexes on toast relation if necessary */
15841 if (OidIsValid(reltoastrelid))
15843 Relation toastRel = relation_open(reltoastrelid, lockmode);
15845 reltoastidxids = RelationGetIndexList(toastRel);
15846 relation_close(toastRel, lockmode);
15850 * Relfilenumbers are not unique in databases across tablespaces, so we
15851 * need to allocate a new one in the new tablespace.
15853 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15854 rel->rd_rel->relpersistence);
15856 /* Open old and new relation */
15857 newrlocator = rel->rd_locator;
15858 newrlocator.relNumber = newrelfilenumber;
15859 newrlocator.spcOid = newTableSpace;
15861 /* hand off to AM to actually create new rel storage and copy the data */
15862 if (rel->rd_rel->relkind == RELKIND_INDEX)
15864 index_copy_data(rel, newrlocator);
15866 else
15868 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15869 table_relation_copy_data(rel, &newrlocator);
15873 * Update the pg_class row.
15875 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15876 * executed on pg_class or its indexes (the above copy wouldn't contain
15877 * the updated pg_class entry), but that's forbidden with
15878 * CheckRelationTableSpaceMove().
15880 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15882 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15884 RelationAssumeNewRelfilelocator(rel);
15886 relation_close(rel, NoLock);
15888 /* Make sure the reltablespace change is visible */
15889 CommandCounterIncrement();
15891 /* Move associated toast relation and/or indexes, too */
15892 if (OidIsValid(reltoastrelid))
15893 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15894 foreach(lc, reltoastidxids)
15895 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15897 /* Clean up */
15898 list_free(reltoastidxids);
15902 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15903 * storage that have an interest in preserving tablespace.
15905 * Since these have no storage the tablespace can be updated with a simple
15906 * metadata only operation to update the tablespace.
15908 static void
15909 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15912 * Shouldn't be called on relations having storage; these are processed in
15913 * phase 3.
15915 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15917 /* check if relation can be moved to its new tablespace */
15918 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15920 InvokeObjectPostAlterHook(RelationRelationId,
15921 RelationGetRelid(rel),
15923 return;
15926 /* Update can be done, so change reltablespace */
15927 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15929 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15931 /* Make sure the reltablespace change is visible */
15932 CommandCounterIncrement();
15936 * Alter Table ALL ... SET TABLESPACE
15938 * Allows a user to move all objects of some type in a given tablespace in the
15939 * current database to another tablespace. Objects can be chosen based on the
15940 * owner of the object also, to allow users to move only their objects.
15941 * The user must have CREATE rights on the new tablespace, as usual. The main
15942 * permissions handling is done by the lower-level table move function.
15944 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15945 * lock can't be acquired then we ereport(ERROR).
15948 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15950 List *relations = NIL;
15951 ListCell *l;
15952 ScanKeyData key[1];
15953 Relation rel;
15954 TableScanDesc scan;
15955 HeapTuple tuple;
15956 Oid orig_tablespaceoid;
15957 Oid new_tablespaceoid;
15958 List *role_oids = roleSpecsToIds(stmt->roles);
15960 /* Ensure we were not asked to move something we can't */
15961 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15962 stmt->objtype != OBJECT_MATVIEW)
15963 ereport(ERROR,
15964 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15965 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15967 /* Get the orig and new tablespace OIDs */
15968 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15969 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15971 /* Can't move shared relations in to or out of pg_global */
15972 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15973 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15974 new_tablespaceoid == GLOBALTABLESPACE_OID)
15975 ereport(ERROR,
15976 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15977 errmsg("cannot move relations in to or out of pg_global tablespace")));
15980 * Must have CREATE rights on the new tablespace, unless it is the
15981 * database default tablespace (which all users implicitly have CREATE
15982 * rights on).
15984 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15986 AclResult aclresult;
15988 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15989 ACL_CREATE);
15990 if (aclresult != ACLCHECK_OK)
15991 aclcheck_error(aclresult, OBJECT_TABLESPACE,
15992 get_tablespace_name(new_tablespaceoid));
15996 * Now that the checks are done, check if we should set either to
15997 * InvalidOid because it is our database's default tablespace.
15999 if (orig_tablespaceoid == MyDatabaseTableSpace)
16000 orig_tablespaceoid = InvalidOid;
16002 if (new_tablespaceoid == MyDatabaseTableSpace)
16003 new_tablespaceoid = InvalidOid;
16005 /* no-op */
16006 if (orig_tablespaceoid == new_tablespaceoid)
16007 return new_tablespaceoid;
16010 * Walk the list of objects in the tablespace and move them. This will
16011 * only find objects in our database, of course.
16013 ScanKeyInit(&key[0],
16014 Anum_pg_class_reltablespace,
16015 BTEqualStrategyNumber, F_OIDEQ,
16016 ObjectIdGetDatum(orig_tablespaceoid));
16018 rel = table_open(RelationRelationId, AccessShareLock);
16019 scan = table_beginscan_catalog(rel, 1, key);
16020 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
16022 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
16023 Oid relOid = relForm->oid;
16026 * Do not move objects in pg_catalog as part of this, if an admin
16027 * really wishes to do so, they can issue the individual ALTER
16028 * commands directly.
16030 * Also, explicitly avoid any shared tables, temp tables, or TOAST
16031 * (TOAST will be moved with the main table).
16033 if (IsCatalogNamespace(relForm->relnamespace) ||
16034 relForm->relisshared ||
16035 isAnyTempNamespace(relForm->relnamespace) ||
16036 IsToastNamespace(relForm->relnamespace))
16037 continue;
16039 /* Only move the object type requested */
16040 if ((stmt->objtype == OBJECT_TABLE &&
16041 relForm->relkind != RELKIND_RELATION &&
16042 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
16043 (stmt->objtype == OBJECT_INDEX &&
16044 relForm->relkind != RELKIND_INDEX &&
16045 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
16046 (stmt->objtype == OBJECT_MATVIEW &&
16047 relForm->relkind != RELKIND_MATVIEW))
16048 continue;
16050 /* Check if we are only moving objects owned by certain roles */
16051 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
16052 continue;
16055 * Handle permissions-checking here since we are locking the tables
16056 * and also to avoid doing a bunch of work only to fail part-way. Note
16057 * that permissions will also be checked by AlterTableInternal().
16059 * Caller must be considered an owner on the table to move it.
16061 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
16062 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
16063 NameStr(relForm->relname));
16065 if (stmt->nowait &&
16066 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
16067 ereport(ERROR,
16068 (errcode(ERRCODE_OBJECT_IN_USE),
16069 errmsg("aborting because lock on relation \"%s.%s\" is not available",
16070 get_namespace_name(relForm->relnamespace),
16071 NameStr(relForm->relname))));
16072 else
16073 LockRelationOid(relOid, AccessExclusiveLock);
16075 /* Add to our list of objects to move */
16076 relations = lappend_oid(relations, relOid);
16079 table_endscan(scan);
16080 table_close(rel, AccessShareLock);
16082 if (relations == NIL)
16083 ereport(NOTICE,
16084 (errcode(ERRCODE_NO_DATA_FOUND),
16085 errmsg("no matching relations in tablespace \"%s\" found",
16086 orig_tablespaceoid == InvalidOid ? "(database default)" :
16087 get_tablespace_name(orig_tablespaceoid))));
16089 /* Everything is locked, loop through and move all of the relations. */
16090 foreach(l, relations)
16092 List *cmds = NIL;
16093 AlterTableCmd *cmd = makeNode(AlterTableCmd);
16095 cmd->subtype = AT_SetTableSpace;
16096 cmd->name = stmt->new_tablespacename;
16098 cmds = lappend(cmds, cmd);
16100 EventTriggerAlterTableStart((Node *) stmt);
16101 /* OID is set by AlterTableInternal */
16102 AlterTableInternal(lfirst_oid(l), cmds, false);
16103 EventTriggerAlterTableEnd();
16106 return new_tablespaceoid;
16109 static void
16110 index_copy_data(Relation rel, RelFileLocator newrlocator)
16112 SMgrRelation dstrel;
16115 * Since we copy the file directly without looking at the shared buffers,
16116 * we'd better first flush out any pages of the source relation that are
16117 * in shared buffers. We assume no new changes will be made while we are
16118 * holding exclusive lock on the rel.
16120 FlushRelationBuffers(rel);
16123 * Create and copy all forks of the relation, and schedule unlinking of
16124 * old physical files.
16126 * NOTE: any conflict in relfilenumber value will be caught in
16127 * RelationCreateStorage().
16129 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
16131 /* copy main fork */
16132 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
16133 rel->rd_rel->relpersistence);
16135 /* copy those extra forks that exist */
16136 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
16137 forkNum <= MAX_FORKNUM; forkNum++)
16139 if (smgrexists(RelationGetSmgr(rel), forkNum))
16141 smgrcreate(dstrel, forkNum, false);
16144 * WAL log creation if the relation is persistent, or this is the
16145 * init fork of an unlogged relation.
16147 if (RelationIsPermanent(rel) ||
16148 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
16149 forkNum == INIT_FORKNUM))
16150 log_smgrcreate(&newrlocator, forkNum);
16151 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
16152 rel->rd_rel->relpersistence);
16156 /* drop old relation, and close new one */
16157 RelationDropStorage(rel);
16158 smgrclose(dstrel);
16162 * ALTER TABLE ENABLE/DISABLE TRIGGER
16164 * We just pass this off to trigger.c.
16166 static void
16167 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
16168 char fires_when, bool skip_system, bool recurse,
16169 LOCKMODE lockmode)
16171 EnableDisableTrigger(rel, trigname, InvalidOid,
16172 fires_when, skip_system, recurse,
16173 lockmode);
16175 InvokeObjectPostAlterHook(RelationRelationId,
16176 RelationGetRelid(rel), 0);
16180 * ALTER TABLE ENABLE/DISABLE RULE
16182 * We just pass this off to rewriteDefine.c.
16184 static void
16185 ATExecEnableDisableRule(Relation rel, const char *rulename,
16186 char fires_when, LOCKMODE lockmode)
16188 EnableDisableRule(rel, rulename, fires_when);
16190 InvokeObjectPostAlterHook(RelationRelationId,
16191 RelationGetRelid(rel), 0);
16195 * ALTER TABLE INHERIT
16197 * Add a parent to the child's parents. This verifies that all the columns and
16198 * check constraints of the parent appear in the child and that they have the
16199 * same data types and expressions.
16201 static void
16202 ATPrepAddInherit(Relation child_rel)
16204 if (child_rel->rd_rel->reloftype)
16205 ereport(ERROR,
16206 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16207 errmsg("cannot change inheritance of typed table")));
16209 if (child_rel->rd_rel->relispartition)
16210 ereport(ERROR,
16211 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16212 errmsg("cannot change inheritance of a partition")));
16214 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16215 ereport(ERROR,
16216 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16217 errmsg("cannot change inheritance of partitioned table")));
16221 * Return the address of the new parent relation.
16223 static ObjectAddress
16224 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
16226 Relation parent_rel;
16227 List *children;
16228 ObjectAddress address;
16229 const char *trigger_name;
16232 * A self-exclusive lock is needed here. See the similar case in
16233 * MergeAttributes() for a full explanation.
16235 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
16238 * Must be owner of both parent and child -- child was checked by
16239 * ATSimplePermissions call in ATPrepCmd
16241 ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
16243 /* Permanent rels cannot inherit from temporary ones */
16244 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16245 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
16246 ereport(ERROR,
16247 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16248 errmsg("cannot inherit from temporary relation \"%s\"",
16249 RelationGetRelationName(parent_rel))));
16251 /* If parent rel is temp, it must belong to this session */
16252 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16253 !parent_rel->rd_islocaltemp)
16254 ereport(ERROR,
16255 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16256 errmsg("cannot inherit from temporary relation of another session")));
16258 /* Ditto for the child */
16259 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
16260 !child_rel->rd_islocaltemp)
16261 ereport(ERROR,
16262 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16263 errmsg("cannot inherit to temporary relation of another session")));
16265 /* Prevent partitioned tables from becoming inheritance parents */
16266 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16267 ereport(ERROR,
16268 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16269 errmsg("cannot inherit from partitioned table \"%s\"",
16270 parent->relname)));
16272 /* Likewise for partitions */
16273 if (parent_rel->rd_rel->relispartition)
16274 ereport(ERROR,
16275 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16276 errmsg("cannot inherit from a partition")));
16279 * Prevent circularity by seeing if proposed parent inherits from child.
16280 * (In particular, this disallows making a rel inherit from itself.)
16282 * This is not completely bulletproof because of race conditions: in
16283 * multi-level inheritance trees, someone else could concurrently be
16284 * making another inheritance link that closes the loop but does not join
16285 * either of the rels we have locked. Preventing that seems to require
16286 * exclusive locks on the entire inheritance tree, which is a cure worse
16287 * than the disease. find_all_inheritors() will cope with circularity
16288 * anyway, so don't sweat it too much.
16290 * We use weakest lock we can on child's children, namely AccessShareLock.
16292 children = find_all_inheritors(RelationGetRelid(child_rel),
16293 AccessShareLock, NULL);
16295 if (list_member_oid(children, RelationGetRelid(parent_rel)))
16296 ereport(ERROR,
16297 (errcode(ERRCODE_DUPLICATE_TABLE),
16298 errmsg("circular inheritance not allowed"),
16299 errdetail("\"%s\" is already a child of \"%s\".",
16300 parent->relname,
16301 RelationGetRelationName(child_rel))));
16304 * If child_rel has row-level triggers with transition tables, we
16305 * currently don't allow it to become an inheritance child. See also
16306 * prohibitions in ATExecAttachPartition() and CreateTrigger().
16308 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
16309 if (trigger_name != NULL)
16310 ereport(ERROR,
16311 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16312 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
16313 trigger_name, RelationGetRelationName(child_rel)),
16314 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
16316 /* OK to create inheritance */
16317 CreateInheritance(child_rel, parent_rel, false);
16320 * If parent_rel has a primary key, then child_rel has not-null
16321 * constraints that make these columns as non nullable. Make those
16322 * constraints as inherited.
16324 ATInheritAdjustNotNulls(parent_rel, child_rel, 1);
16326 ObjectAddressSet(address, RelationRelationId,
16327 RelationGetRelid(parent_rel));
16329 /* keep our lock on the parent relation until commit */
16330 table_close(parent_rel, NoLock);
16332 return address;
16336 * CreateInheritance
16337 * Catalog manipulation portion of creating inheritance between a child
16338 * table and a parent table.
16340 * Common to ATExecAddInherit() and ATExecAttachPartition().
16342 static void
16343 CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
16345 Relation catalogRelation;
16346 SysScanDesc scan;
16347 ScanKeyData key;
16348 HeapTuple inheritsTuple;
16349 int32 inhseqno;
16351 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
16352 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16355 * Check for duplicates in the list of parents, and determine the highest
16356 * inhseqno already present; we'll use the next one for the new parent.
16357 * Also, if proposed child is a partition, it cannot already be
16358 * inheriting.
16360 * Note: we do not reject the case where the child already inherits from
16361 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
16363 ScanKeyInit(&key,
16364 Anum_pg_inherits_inhrelid,
16365 BTEqualStrategyNumber, F_OIDEQ,
16366 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16367 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
16368 true, NULL, 1, &key);
16370 /* inhseqno sequences start at 1 */
16371 inhseqno = 0;
16372 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16374 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16376 if (inh->inhparent == RelationGetRelid(parent_rel))
16377 ereport(ERROR,
16378 (errcode(ERRCODE_DUPLICATE_TABLE),
16379 errmsg("relation \"%s\" would be inherited from more than once",
16380 RelationGetRelationName(parent_rel))));
16382 if (inh->inhseqno > inhseqno)
16383 inhseqno = inh->inhseqno;
16385 systable_endscan(scan);
16387 /* Match up the columns and bump attinhcount as needed */
16388 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
16390 /* Match up the constraints and bump coninhcount as needed */
16391 MergeConstraintsIntoExisting(child_rel, parent_rel);
16394 * OK, it looks valid. Make the catalog entries that show inheritance.
16396 StoreCatalogInheritance1(RelationGetRelid(child_rel),
16397 RelationGetRelid(parent_rel),
16398 inhseqno + 1,
16399 catalogRelation,
16400 parent_rel->rd_rel->relkind ==
16401 RELKIND_PARTITIONED_TABLE);
16403 /* Now we're done with pg_inherits */
16404 table_close(catalogRelation, RowExclusiveLock);
16408 * Obtain the source-text form of the constraint expression for a check
16409 * constraint, given its pg_constraint tuple
16411 static char *
16412 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
16414 Form_pg_constraint con;
16415 bool isnull;
16416 Datum attr;
16417 Datum expr;
16419 con = (Form_pg_constraint) GETSTRUCT(contup);
16420 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
16421 if (isnull)
16422 elog(ERROR, "null conbin for constraint %u", con->oid);
16424 expr = DirectFunctionCall2(pg_get_expr, attr,
16425 ObjectIdGetDatum(con->conrelid));
16426 return TextDatumGetCString(expr);
16430 * Determine whether two check constraints are functionally equivalent
16432 * The test we apply is to see whether they reverse-compile to the same
16433 * source string. This insulates us from issues like whether attributes
16434 * have the same physical column numbers in parent and child relations.
16436 static bool
16437 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
16439 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
16440 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
16442 if (acon->condeferrable != bcon->condeferrable ||
16443 acon->condeferred != bcon->condeferred ||
16444 strcmp(decompile_conbin(a, tupleDesc),
16445 decompile_conbin(b, tupleDesc)) != 0)
16446 return false;
16447 else
16448 return true;
16452 * Check columns in child table match up with columns in parent, and increment
16453 * their attinhcount.
16455 * Called by CreateInheritance
16457 * Currently all parent columns must be found in child. Missing columns are an
16458 * error. One day we might consider creating new columns like CREATE TABLE
16459 * does. However, that is widely unpopular --- in the common use case of
16460 * partitioned tables it's a foot-gun.
16462 * The data type must match exactly. If the parent column is NOT NULL then
16463 * the child must be as well. Defaults are not compared, however.
16465 static void
16466 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
16468 Relation attrrel;
16469 TupleDesc parent_desc;
16471 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
16472 parent_desc = RelationGetDescr(parent_rel);
16474 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
16476 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
16477 char *parent_attname = NameStr(parent_att->attname);
16478 HeapTuple tuple;
16480 /* Ignore dropped columns in the parent. */
16481 if (parent_att->attisdropped)
16482 continue;
16484 /* Find same column in child (matching on column name). */
16485 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
16486 if (HeapTupleIsValid(tuple))
16488 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
16490 if (parent_att->atttypid != child_att->atttypid ||
16491 parent_att->atttypmod != child_att->atttypmod)
16492 ereport(ERROR,
16493 (errcode(ERRCODE_DATATYPE_MISMATCH),
16494 errmsg("child table \"%s\" has different type for column \"%s\"",
16495 RelationGetRelationName(child_rel), parent_attname)));
16497 if (parent_att->attcollation != child_att->attcollation)
16498 ereport(ERROR,
16499 (errcode(ERRCODE_COLLATION_MISMATCH),
16500 errmsg("child table \"%s\" has different collation for column \"%s\"",
16501 RelationGetRelationName(child_rel), parent_attname)));
16504 * If the parent has a not-null constraint that's not NO INHERIT,
16505 * make sure the child has one too.
16507 * Other constraints are checked elsewhere.
16509 if (parent_att->attnotnull && !child_att->attnotnull)
16511 HeapTuple contup;
16513 contup = findNotNullConstraintAttnum(RelationGetRelid(parent_rel),
16514 parent_att->attnum);
16515 if (HeapTupleIsValid(contup) &&
16516 !((Form_pg_constraint) GETSTRUCT(contup))->connoinherit)
16517 ereport(ERROR,
16518 errcode(ERRCODE_DATATYPE_MISMATCH),
16519 errmsg("column \"%s\" in child table must be marked NOT NULL",
16520 parent_attname));
16524 * Child column must be generated if and only if parent column is.
16526 if (parent_att->attgenerated && !child_att->attgenerated)
16527 ereport(ERROR,
16528 (errcode(ERRCODE_DATATYPE_MISMATCH),
16529 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
16530 if (child_att->attgenerated && !parent_att->attgenerated)
16531 ereport(ERROR,
16532 (errcode(ERRCODE_DATATYPE_MISMATCH),
16533 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
16536 * Regular inheritance children are independent enough not to
16537 * inherit identity columns. But partitions are integral part of
16538 * a partitioned table and inherit identity column.
16540 if (ispartition)
16541 child_att->attidentity = parent_att->attidentity;
16544 * OK, bump the child column's inheritance count. (If we fail
16545 * later on, this change will just roll back.)
16547 child_att->attinhcount++;
16548 if (child_att->attinhcount < 0)
16549 ereport(ERROR,
16550 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16551 errmsg("too many inheritance parents"));
16554 * In case of partitions, we must enforce that value of attislocal
16555 * is same in all partitions. (Note: there are only inherited
16556 * attributes in partitions)
16558 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16560 Assert(child_att->attinhcount == 1);
16561 child_att->attislocal = false;
16564 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
16565 heap_freetuple(tuple);
16567 else
16569 ereport(ERROR,
16570 (errcode(ERRCODE_DATATYPE_MISMATCH),
16571 errmsg("child table is missing column \"%s\"", parent_attname)));
16575 table_close(attrrel, RowExclusiveLock);
16579 * Check constraints in child table match up with constraints in parent,
16580 * and increment their coninhcount.
16582 * Constraints that are marked ONLY in the parent are ignored.
16584 * Called by CreateInheritance
16586 * Currently all constraints in parent must be present in the child. One day we
16587 * may consider adding new constraints like CREATE TABLE does.
16589 * XXX This is O(N^2) which may be an issue with tables with hundreds of
16590 * constraints. As long as tables have more like 10 constraints it shouldn't be
16591 * a problem though. Even 100 constraints ought not be the end of the world.
16593 * XXX See MergeWithExistingConstraint too if you change this code.
16595 static void
16596 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
16598 Relation constraintrel;
16599 SysScanDesc parent_scan;
16600 ScanKeyData parent_key;
16601 HeapTuple parent_tuple;
16602 Oid parent_relid = RelationGetRelid(parent_rel);
16604 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
16606 /* Outer loop scans through the parent's constraint definitions */
16607 ScanKeyInit(&parent_key,
16608 Anum_pg_constraint_conrelid,
16609 BTEqualStrategyNumber, F_OIDEQ,
16610 ObjectIdGetDatum(parent_relid));
16611 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16612 true, NULL, 1, &parent_key);
16614 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
16616 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
16617 SysScanDesc child_scan;
16618 ScanKeyData child_key;
16619 HeapTuple child_tuple;
16620 bool found = false;
16622 if (parent_con->contype != CONSTRAINT_CHECK &&
16623 parent_con->contype != CONSTRAINT_NOTNULL)
16624 continue;
16626 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
16627 if (parent_con->connoinherit)
16628 continue;
16630 /* Search for a child constraint matching this one */
16631 ScanKeyInit(&child_key,
16632 Anum_pg_constraint_conrelid,
16633 BTEqualStrategyNumber, F_OIDEQ,
16634 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16635 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
16636 true, NULL, 1, &child_key);
16638 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
16640 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
16641 HeapTuple child_copy;
16643 if (child_con->contype != parent_con->contype)
16644 continue;
16647 * CHECK constraint are matched by name, NOT NULL ones by
16648 * attribute number
16650 if (child_con->contype == CONSTRAINT_CHECK)
16652 if (strcmp(NameStr(parent_con->conname),
16653 NameStr(child_con->conname)) != 0)
16654 continue;
16656 else if (child_con->contype == CONSTRAINT_NOTNULL)
16658 AttrNumber parent_attno = extractNotNullColumn(parent_tuple);
16659 AttrNumber child_attno = extractNotNullColumn(child_tuple);
16661 if (strcmp(get_attname(parent_relid, parent_attno, false),
16662 get_attname(RelationGetRelid(child_rel), child_attno,
16663 false)) != 0)
16664 continue;
16667 if (child_con->contype == CONSTRAINT_CHECK &&
16668 !constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
16669 ereport(ERROR,
16670 (errcode(ERRCODE_DATATYPE_MISMATCH),
16671 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
16672 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
16675 * If the CHECK child constraint is "no inherit" then cannot
16676 * merge.
16678 * This is not desirable for not-null constraints, mostly because
16679 * it breaks our pg_upgrade strategy, but it also makes sense on
16680 * its own: if a child has its own not-null constraint and then
16681 * acquires a parent with the same constraint, then we start to
16682 * enforce that constraint for all the descendants of that child
16683 * too, if any.
16685 if (child_con->contype == CONSTRAINT_CHECK &&
16686 child_con->connoinherit)
16687 ereport(ERROR,
16688 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16689 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
16690 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16693 * If the child constraint is "not valid" then cannot merge with a
16694 * valid parent constraint
16696 if (parent_con->convalidated && !child_con->convalidated)
16697 ereport(ERROR,
16698 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
16699 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
16700 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
16703 * OK, bump the child constraint's inheritance count. (If we fail
16704 * later on, this change will just roll back.)
16706 child_copy = heap_copytuple(child_tuple);
16707 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
16708 child_con->coninhcount++;
16709 if (child_con->coninhcount < 0)
16710 ereport(ERROR,
16711 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
16712 errmsg("too many inheritance parents"));
16713 if (child_con->contype == CONSTRAINT_NOTNULL &&
16714 child_con->connoinherit)
16717 * If the child has children, it's not possible to turn a NO
16718 * INHERIT constraint into an inheritable one: we would need
16719 * to recurse to create constraints in those children, but
16720 * this is not a good place to do that.
16722 if (child_rel->rd_rel->relhassubclass)
16723 ereport(ERROR,
16724 errmsg("cannot add NOT NULL constraint to column \"%s\" of relation \"%s\" with inheritance children",
16725 get_attname(RelationGetRelid(child_rel),
16726 extractNotNullColumn(child_tuple),
16727 false),
16728 RelationGetRelationName(child_rel)),
16729 errdetail("Existing constraint \"%s\" is marked NO INHERIT.",
16730 NameStr(child_con->conname)));
16732 child_con->connoinherit = false;
16736 * In case of partitions, an inherited constraint must be
16737 * inherited only once since it cannot have multiple parents and
16738 * it is never considered local.
16740 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16742 Assert(child_con->coninhcount == 1);
16743 child_con->conislocal = false;
16746 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
16747 heap_freetuple(child_copy);
16749 found = true;
16750 break;
16753 systable_endscan(child_scan);
16755 if (!found)
16757 if (parent_con->contype == CONSTRAINT_NOTNULL)
16758 ereport(ERROR,
16759 errcode(ERRCODE_DATATYPE_MISMATCH),
16760 errmsg("column \"%s\" in child table must be marked NOT NULL",
16761 get_attname(parent_relid,
16762 extractNotNullColumn(parent_tuple),
16763 false)));
16765 ereport(ERROR,
16766 (errcode(ERRCODE_DATATYPE_MISMATCH),
16767 errmsg("child table is missing constraint \"%s\"",
16768 NameStr(parent_con->conname))));
16772 systable_endscan(parent_scan);
16773 table_close(constraintrel, RowExclusiveLock);
16777 * ALTER TABLE NO INHERIT
16779 * Return value is the address of the relation that is no longer parent.
16781 static ObjectAddress
16782 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
16784 ObjectAddress address;
16785 Relation parent_rel;
16787 if (rel->rd_rel->relispartition)
16788 ereport(ERROR,
16789 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16790 errmsg("cannot change inheritance of a partition")));
16793 * AccessShareLock on the parent is probably enough, seeing that DROP
16794 * TABLE doesn't lock parent tables at all. We need some lock since we'll
16795 * be inspecting the parent's schema.
16797 parent_rel = table_openrv(parent, AccessShareLock);
16800 * We don't bother to check ownership of the parent table --- ownership of
16801 * the child is presumed enough rights.
16804 /* Off to RemoveInheritance() where most of the work happens */
16805 RemoveInheritance(rel, parent_rel, false);
16808 * If parent_rel has a primary key, then child_rel has not-null
16809 * constraints that make these columns as non nullable. Mark those
16810 * constraints as no longer inherited by this parent.
16812 ATInheritAdjustNotNulls(parent_rel, rel, -1);
16815 * If the parent has a primary key, then we decrement counts for all NOT
16816 * NULL constraints
16819 ObjectAddressSet(address, RelationRelationId,
16820 RelationGetRelid(parent_rel));
16822 /* keep our lock on the parent relation until commit */
16823 table_close(parent_rel, NoLock);
16825 return address;
16829 * MarkInheritDetached
16831 * Set inhdetachpending for a partition, for ATExecDetachPartition
16832 * in concurrent mode. While at it, verify that no other partition is
16833 * already pending detach.
16835 static void
16836 MarkInheritDetached(Relation child_rel, Relation parent_rel)
16838 Relation catalogRelation;
16839 SysScanDesc scan;
16840 ScanKeyData key;
16841 HeapTuple inheritsTuple;
16842 bool found = false;
16844 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16847 * Find pg_inherits entries by inhparent. (We need to scan them all in
16848 * order to verify that no other partition is pending detach.)
16850 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16851 ScanKeyInit(&key,
16852 Anum_pg_inherits_inhparent,
16853 BTEqualStrategyNumber, F_OIDEQ,
16854 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16855 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16856 true, NULL, 1, &key);
16858 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16860 Form_pg_inherits inhForm;
16862 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16863 if (inhForm->inhdetachpending)
16864 ereport(ERROR,
16865 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16866 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16867 get_rel_name(inhForm->inhrelid),
16868 get_namespace_name(parent_rel->rd_rel->relnamespace),
16869 RelationGetRelationName(parent_rel)),
16870 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16872 if (inhForm->inhrelid == RelationGetRelid(child_rel))
16874 HeapTuple newtup;
16876 newtup = heap_copytuple(inheritsTuple);
16877 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16879 CatalogTupleUpdate(catalogRelation,
16880 &inheritsTuple->t_self,
16881 newtup);
16882 found = true;
16883 heap_freetuple(newtup);
16884 /* keep looking, to ensure we catch others pending detach */
16888 /* Done */
16889 systable_endscan(scan);
16890 table_close(catalogRelation, RowExclusiveLock);
16892 if (!found)
16893 ereport(ERROR,
16894 (errcode(ERRCODE_UNDEFINED_TABLE),
16895 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16896 RelationGetRelationName(child_rel),
16897 RelationGetRelationName(parent_rel))));
16901 * RemoveInheritance
16903 * Drop a parent from the child's parents. This just adjusts the attinhcount
16904 * and attislocal of the columns and removes the pg_inherit and pg_depend
16905 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16907 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16908 * up attislocal stays true, which means if a child is ever removed from a
16909 * parent then its columns will never be automatically dropped which may
16910 * surprise. But at least we'll never surprise by dropping columns someone
16911 * isn't expecting to be dropped which would actually mean data loss.
16913 * coninhcount and conislocal for inherited constraints are adjusted in
16914 * exactly the same way.
16916 * Common to ATExecDropInherit() and ATExecDetachPartition().
16918 static void
16919 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16921 Relation catalogRelation;
16922 SysScanDesc scan;
16923 ScanKeyData key[3];
16924 HeapTuple attributeTuple,
16925 constraintTuple;
16926 List *connames;
16927 List *nncolumns;
16928 bool found;
16929 bool is_partitioning;
16931 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16933 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16934 RelationGetRelid(parent_rel),
16935 expect_detached,
16936 RelationGetRelationName(child_rel));
16937 if (!found)
16939 if (is_partitioning)
16940 ereport(ERROR,
16941 (errcode(ERRCODE_UNDEFINED_TABLE),
16942 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16943 RelationGetRelationName(child_rel),
16944 RelationGetRelationName(parent_rel))));
16945 else
16946 ereport(ERROR,
16947 (errcode(ERRCODE_UNDEFINED_TABLE),
16948 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16949 RelationGetRelationName(parent_rel),
16950 RelationGetRelationName(child_rel))));
16954 * Search through child columns looking for ones matching parent rel
16956 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16957 ScanKeyInit(&key[0],
16958 Anum_pg_attribute_attrelid,
16959 BTEqualStrategyNumber, F_OIDEQ,
16960 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16961 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16962 true, NULL, 1, key);
16963 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16965 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16967 /* Ignore if dropped or not inherited */
16968 if (att->attisdropped)
16969 continue;
16970 if (att->attinhcount <= 0)
16971 continue;
16973 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16974 NameStr(att->attname)))
16976 /* Decrement inhcount and possibly set islocal to true */
16977 HeapTuple copyTuple = heap_copytuple(attributeTuple);
16978 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16980 copy_att->attinhcount--;
16981 if (copy_att->attinhcount == 0)
16982 copy_att->attislocal = true;
16984 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16985 heap_freetuple(copyTuple);
16988 systable_endscan(scan);
16989 table_close(catalogRelation, RowExclusiveLock);
16992 * Likewise, find inherited check constraints and disinherit them. To do
16993 * this, we first need a list of the names of the parent's check
16994 * constraints. (We cheat a bit by only checking for name matches,
16995 * assuming that the expressions will match.)
16997 * For NOT NULL columns, we store column numbers to match.
16999 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
17000 ScanKeyInit(&key[0],
17001 Anum_pg_constraint_conrelid,
17002 BTEqualStrategyNumber, F_OIDEQ,
17003 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
17004 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17005 true, NULL, 1, key);
17007 connames = NIL;
17008 nncolumns = NIL;
17010 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17012 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17014 if (con->contype == CONSTRAINT_CHECK)
17015 connames = lappend(connames, pstrdup(NameStr(con->conname)));
17016 if (con->contype == CONSTRAINT_NOTNULL)
17017 nncolumns = lappend_int(nncolumns, extractNotNullColumn(constraintTuple));
17020 systable_endscan(scan);
17022 /* Now scan the child's constraints */
17023 ScanKeyInit(&key[0],
17024 Anum_pg_constraint_conrelid,
17025 BTEqualStrategyNumber, F_OIDEQ,
17026 ObjectIdGetDatum(RelationGetRelid(child_rel)));
17027 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
17028 true, NULL, 1, key);
17030 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
17032 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
17033 bool match = false;
17034 ListCell *lc;
17037 * Match CHECK constraints by name, not-null constraints by column
17038 * number, and ignore all others.
17040 if (con->contype == CONSTRAINT_CHECK)
17042 foreach(lc, connames)
17044 if (con->contype == CONSTRAINT_CHECK &&
17045 strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
17047 match = true;
17048 break;
17052 else if (con->contype == CONSTRAINT_NOTNULL)
17054 AttrNumber child_attno = extractNotNullColumn(constraintTuple);
17056 foreach(lc, nncolumns)
17058 if (lfirst_int(lc) == child_attno)
17060 match = true;
17061 break;
17065 else
17066 continue;
17068 if (match)
17070 /* Decrement inhcount and possibly set islocal to true */
17071 HeapTuple copyTuple = heap_copytuple(constraintTuple);
17072 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
17074 if (copy_con->coninhcount <= 0) /* shouldn't happen */
17075 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
17076 RelationGetRelid(child_rel), NameStr(copy_con->conname));
17078 copy_con->coninhcount--;
17079 if (copy_con->coninhcount == 0)
17080 copy_con->conislocal = true;
17082 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
17083 heap_freetuple(copyTuple);
17087 systable_endscan(scan);
17088 table_close(catalogRelation, RowExclusiveLock);
17090 drop_parent_dependency(RelationGetRelid(child_rel),
17091 RelationRelationId,
17092 RelationGetRelid(parent_rel),
17093 child_dependency_type(is_partitioning));
17096 * Post alter hook of this inherits. Since object_access_hook doesn't take
17097 * multiple object identifiers, we relay oid of parent relation using
17098 * auxiliary_id argument.
17100 InvokeObjectPostAlterHookArg(InheritsRelationId,
17101 RelationGetRelid(child_rel), 0,
17102 RelationGetRelid(parent_rel), false);
17106 * Adjust coninhcount of not-null constraints upwards or downwards when a
17107 * table is marked as inheriting or no longer doing so a table with a primary
17108 * key.
17110 * Note: these constraints are not dropped, even if their inhcount goes to zero
17111 * and conislocal is false. Instead we mark the constraints as locally defined.
17112 * This is seen as more useful behavior, with no downsides. The user can always
17113 * drop them afterwards.
17115 static void
17116 ATInheritAdjustNotNulls(Relation parent_rel, Relation child_rel, int inhcount)
17118 Bitmapset *pkattnos;
17120 /* Quick exit when parent has no PK */
17121 if (!parent_rel->rd_rel->relhasindex)
17122 return;
17124 pkattnos = RelationGetIndexAttrBitmap(parent_rel,
17125 INDEX_ATTR_BITMAP_PRIMARY_KEY);
17126 if (pkattnos != NULL)
17128 Bitmapset *childattnums = NULL;
17129 AttrMap *attmap;
17130 int i;
17132 attmap = build_attrmap_by_name(RelationGetDescr(parent_rel),
17133 RelationGetDescr(child_rel), true);
17135 i = -1;
17136 while ((i = bms_next_member(pkattnos, i)) >= 0)
17138 childattnums = bms_add_member(childattnums,
17139 attmap->attnums[i + FirstLowInvalidHeapAttributeNumber - 1]);
17143 * CCI is needed in case there's a NOT NULL PRIMARY KEY column in the
17144 * parent: the relevant not-null constraint in the child already had
17145 * its inhcount modified earlier.
17147 CommandCounterIncrement();
17148 AdjustNotNullInheritance(RelationGetRelid(child_rel), childattnums,
17149 inhcount);
17154 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
17155 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
17156 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
17157 * be TypeRelationId). There's no convenient way to do this, so go trawling
17158 * through pg_depend.
17160 static void
17161 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
17162 DependencyType deptype)
17164 Relation catalogRelation;
17165 SysScanDesc scan;
17166 ScanKeyData key[3];
17167 HeapTuple depTuple;
17169 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
17171 ScanKeyInit(&key[0],
17172 Anum_pg_depend_classid,
17173 BTEqualStrategyNumber, F_OIDEQ,
17174 ObjectIdGetDatum(RelationRelationId));
17175 ScanKeyInit(&key[1],
17176 Anum_pg_depend_objid,
17177 BTEqualStrategyNumber, F_OIDEQ,
17178 ObjectIdGetDatum(relid));
17179 ScanKeyInit(&key[2],
17180 Anum_pg_depend_objsubid,
17181 BTEqualStrategyNumber, F_INT4EQ,
17182 Int32GetDatum(0));
17184 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
17185 NULL, 3, key);
17187 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
17189 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
17191 if (dep->refclassid == refclassid &&
17192 dep->refobjid == refobjid &&
17193 dep->refobjsubid == 0 &&
17194 dep->deptype == deptype)
17195 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
17198 systable_endscan(scan);
17199 table_close(catalogRelation, RowExclusiveLock);
17203 * ALTER TABLE OF
17205 * Attach a table to a composite type, as though it had been created with CREATE
17206 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
17207 * subject table must not have inheritance parents. These restrictions ensure
17208 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
17210 * The address of the type is returned.
17212 static ObjectAddress
17213 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
17215 Oid relid = RelationGetRelid(rel);
17216 Type typetuple;
17217 Form_pg_type typeform;
17218 Oid typeid;
17219 Relation inheritsRelation,
17220 relationRelation;
17221 SysScanDesc scan;
17222 ScanKeyData key;
17223 AttrNumber table_attno,
17224 type_attno;
17225 TupleDesc typeTupleDesc,
17226 tableTupleDesc;
17227 ObjectAddress tableobj,
17228 typeobj;
17229 HeapTuple classtuple;
17231 /* Validate the type. */
17232 typetuple = typenameType(NULL, ofTypename, NULL);
17233 check_of_type(typetuple);
17234 typeform = (Form_pg_type) GETSTRUCT(typetuple);
17235 typeid = typeform->oid;
17237 /* Fail if the table has any inheritance parents. */
17238 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
17239 ScanKeyInit(&key,
17240 Anum_pg_inherits_inhrelid,
17241 BTEqualStrategyNumber, F_OIDEQ,
17242 ObjectIdGetDatum(relid));
17243 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
17244 true, NULL, 1, &key);
17245 if (HeapTupleIsValid(systable_getnext(scan)))
17246 ereport(ERROR,
17247 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17248 errmsg("typed tables cannot inherit")));
17249 systable_endscan(scan);
17250 table_close(inheritsRelation, AccessShareLock);
17253 * Check the tuple descriptors for compatibility. Unlike inheritance, we
17254 * require that the order also match. However, attnotnull need not match.
17256 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
17257 tableTupleDesc = RelationGetDescr(rel);
17258 table_attno = 1;
17259 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
17261 Form_pg_attribute type_attr,
17262 table_attr;
17263 const char *type_attname,
17264 *table_attname;
17266 /* Get the next non-dropped type attribute. */
17267 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
17268 if (type_attr->attisdropped)
17269 continue;
17270 type_attname = NameStr(type_attr->attname);
17272 /* Get the next non-dropped table attribute. */
17275 if (table_attno > tableTupleDesc->natts)
17276 ereport(ERROR,
17277 (errcode(ERRCODE_DATATYPE_MISMATCH),
17278 errmsg("table is missing column \"%s\"",
17279 type_attname)));
17280 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
17281 table_attno++;
17282 } while (table_attr->attisdropped);
17283 table_attname = NameStr(table_attr->attname);
17285 /* Compare name. */
17286 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
17287 ereport(ERROR,
17288 (errcode(ERRCODE_DATATYPE_MISMATCH),
17289 errmsg("table has column \"%s\" where type requires \"%s\"",
17290 table_attname, type_attname)));
17292 /* Compare type. */
17293 if (table_attr->atttypid != type_attr->atttypid ||
17294 table_attr->atttypmod != type_attr->atttypmod ||
17295 table_attr->attcollation != type_attr->attcollation)
17296 ereport(ERROR,
17297 (errcode(ERRCODE_DATATYPE_MISMATCH),
17298 errmsg("table \"%s\" has different type for column \"%s\"",
17299 RelationGetRelationName(rel), type_attname)));
17301 ReleaseTupleDesc(typeTupleDesc);
17303 /* Any remaining columns at the end of the table had better be dropped. */
17304 for (; table_attno <= tableTupleDesc->natts; table_attno++)
17306 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
17307 table_attno - 1);
17309 if (!table_attr->attisdropped)
17310 ereport(ERROR,
17311 (errcode(ERRCODE_DATATYPE_MISMATCH),
17312 errmsg("table has extra column \"%s\"",
17313 NameStr(table_attr->attname))));
17316 /* If the table was already typed, drop the existing dependency. */
17317 if (rel->rd_rel->reloftype)
17318 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17319 DEPENDENCY_NORMAL);
17321 /* Record a dependency on the new type. */
17322 tableobj.classId = RelationRelationId;
17323 tableobj.objectId = relid;
17324 tableobj.objectSubId = 0;
17325 typeobj.classId = TypeRelationId;
17326 typeobj.objectId = typeid;
17327 typeobj.objectSubId = 0;
17328 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
17330 /* Update pg_class.reloftype */
17331 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17332 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17333 if (!HeapTupleIsValid(classtuple))
17334 elog(ERROR, "cache lookup failed for relation %u", relid);
17335 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
17336 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
17338 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17340 heap_freetuple(classtuple);
17341 table_close(relationRelation, RowExclusiveLock);
17343 ReleaseSysCache(typetuple);
17345 return typeobj;
17349 * ALTER TABLE NOT OF
17351 * Detach a typed table from its originating type. Just clear reloftype and
17352 * remove the dependency.
17354 static void
17355 ATExecDropOf(Relation rel, LOCKMODE lockmode)
17357 Oid relid = RelationGetRelid(rel);
17358 Relation relationRelation;
17359 HeapTuple tuple;
17361 if (!OidIsValid(rel->rd_rel->reloftype))
17362 ereport(ERROR,
17363 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17364 errmsg("\"%s\" is not a typed table",
17365 RelationGetRelationName(rel))));
17368 * We don't bother to check ownership of the type --- ownership of the
17369 * table is presumed enough rights. No lock required on the type, either.
17372 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
17373 DEPENDENCY_NORMAL);
17375 /* Clear pg_class.reloftype */
17376 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
17377 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17378 if (!HeapTupleIsValid(tuple))
17379 elog(ERROR, "cache lookup failed for relation %u", relid);
17380 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
17381 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
17383 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
17385 heap_freetuple(tuple);
17386 table_close(relationRelation, RowExclusiveLock);
17390 * relation_mark_replica_identity: Update a table's replica identity
17392 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
17393 * index. Otherwise, it must be InvalidOid.
17395 * Caller had better hold an exclusive lock on the relation, as the results
17396 * of running two of these concurrently wouldn't be pretty.
17398 static void
17399 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
17400 bool is_internal)
17402 Relation pg_index;
17403 Relation pg_class;
17404 HeapTuple pg_class_tuple;
17405 HeapTuple pg_index_tuple;
17406 Form_pg_class pg_class_form;
17407 Form_pg_index pg_index_form;
17408 ListCell *index;
17411 * Check whether relreplident has changed, and update it if so.
17413 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17414 pg_class_tuple = SearchSysCacheCopy1(RELOID,
17415 ObjectIdGetDatum(RelationGetRelid(rel)));
17416 if (!HeapTupleIsValid(pg_class_tuple))
17417 elog(ERROR, "cache lookup failed for relation \"%s\"",
17418 RelationGetRelationName(rel));
17419 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
17420 if (pg_class_form->relreplident != ri_type)
17422 pg_class_form->relreplident = ri_type;
17423 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
17425 table_close(pg_class, RowExclusiveLock);
17426 heap_freetuple(pg_class_tuple);
17429 * Update the per-index indisreplident flags correctly.
17431 pg_index = table_open(IndexRelationId, RowExclusiveLock);
17432 foreach(index, RelationGetIndexList(rel))
17434 Oid thisIndexOid = lfirst_oid(index);
17435 bool dirty = false;
17437 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
17438 ObjectIdGetDatum(thisIndexOid));
17439 if (!HeapTupleIsValid(pg_index_tuple))
17440 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
17441 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
17443 if (thisIndexOid == indexOid)
17445 /* Set the bit if not already set. */
17446 if (!pg_index_form->indisreplident)
17448 dirty = true;
17449 pg_index_form->indisreplident = true;
17452 else
17454 /* Unset the bit if set. */
17455 if (pg_index_form->indisreplident)
17457 dirty = true;
17458 pg_index_form->indisreplident = false;
17462 if (dirty)
17464 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
17465 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
17466 InvalidOid, is_internal);
17469 * Invalidate the relcache for the table, so that after we commit
17470 * all sessions will refresh the table's replica identity index
17471 * before attempting any UPDATE or DELETE on the table. (If we
17472 * changed the table's pg_class row above, then a relcache inval
17473 * is already queued due to that; but we might not have.)
17475 CacheInvalidateRelcache(rel);
17477 heap_freetuple(pg_index_tuple);
17480 table_close(pg_index, RowExclusiveLock);
17484 * ALTER TABLE <name> REPLICA IDENTITY ...
17486 static void
17487 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
17489 Oid indexOid;
17490 Relation indexRel;
17491 int key;
17493 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
17495 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17496 return;
17498 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
17500 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17501 return;
17503 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
17505 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
17506 return;
17508 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
17510 /* fallthrough */ ;
17512 else
17513 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
17515 /* Check that the index exists */
17516 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
17517 if (!OidIsValid(indexOid))
17518 ereport(ERROR,
17519 (errcode(ERRCODE_UNDEFINED_OBJECT),
17520 errmsg("index \"%s\" for table \"%s\" does not exist",
17521 stmt->name, RelationGetRelationName(rel))));
17523 indexRel = index_open(indexOid, ShareLock);
17525 /* Check that the index is on the relation we're altering. */
17526 if (indexRel->rd_index == NULL ||
17527 indexRel->rd_index->indrelid != RelationGetRelid(rel))
17528 ereport(ERROR,
17529 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17530 errmsg("\"%s\" is not an index for table \"%s\"",
17531 RelationGetRelationName(indexRel),
17532 RelationGetRelationName(rel))));
17533 /* The AM must support uniqueness, and the index must in fact be unique. */
17534 if (!indexRel->rd_indam->amcanunique ||
17535 !indexRel->rd_index->indisunique)
17536 ereport(ERROR,
17537 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17538 errmsg("cannot use non-unique index \"%s\" as replica identity",
17539 RelationGetRelationName(indexRel))));
17540 /* Deferred indexes are not guaranteed to be always unique. */
17541 if (!indexRel->rd_index->indimmediate)
17542 ereport(ERROR,
17543 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17544 errmsg("cannot use non-immediate index \"%s\" as replica identity",
17545 RelationGetRelationName(indexRel))));
17546 /* Expression indexes aren't supported. */
17547 if (RelationGetIndexExpressions(indexRel) != NIL)
17548 ereport(ERROR,
17549 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17550 errmsg("cannot use expression index \"%s\" as replica identity",
17551 RelationGetRelationName(indexRel))));
17552 /* Predicate indexes aren't supported. */
17553 if (RelationGetIndexPredicate(indexRel) != NIL)
17554 ereport(ERROR,
17555 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17556 errmsg("cannot use partial index \"%s\" as replica identity",
17557 RelationGetRelationName(indexRel))));
17559 /* Check index for nullable columns. */
17560 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
17562 int16 attno = indexRel->rd_index->indkey.values[key];
17563 Form_pg_attribute attr;
17566 * Reject any other system columns. (Going forward, we'll disallow
17567 * indexes containing such columns in the first place, but they might
17568 * exist in older branches.)
17570 if (attno <= 0)
17571 ereport(ERROR,
17572 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
17573 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
17574 RelationGetRelationName(indexRel), attno)));
17576 attr = TupleDescAttr(rel->rd_att, attno - 1);
17577 if (!attr->attnotnull)
17578 ereport(ERROR,
17579 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17580 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
17581 RelationGetRelationName(indexRel),
17582 NameStr(attr->attname))));
17585 /* This index is suitable for use as a replica identity. Mark it. */
17586 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
17588 index_close(indexRel, NoLock);
17592 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
17594 static void
17595 ATExecSetRowSecurity(Relation rel, bool rls)
17597 Relation pg_class;
17598 Oid relid;
17599 HeapTuple tuple;
17601 relid = RelationGetRelid(rel);
17603 /* Pull the record for this relation and update it */
17604 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17606 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17608 if (!HeapTupleIsValid(tuple))
17609 elog(ERROR, "cache lookup failed for relation %u", relid);
17611 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
17612 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17614 InvokeObjectPostAlterHook(RelationRelationId,
17615 RelationGetRelid(rel), 0);
17617 table_close(pg_class, RowExclusiveLock);
17618 heap_freetuple(tuple);
17622 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
17624 static void
17625 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
17627 Relation pg_class;
17628 Oid relid;
17629 HeapTuple tuple;
17631 relid = RelationGetRelid(rel);
17633 pg_class = table_open(RelationRelationId, RowExclusiveLock);
17635 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
17637 if (!HeapTupleIsValid(tuple))
17638 elog(ERROR, "cache lookup failed for relation %u", relid);
17640 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
17641 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17643 InvokeObjectPostAlterHook(RelationRelationId,
17644 RelationGetRelid(rel), 0);
17646 table_close(pg_class, RowExclusiveLock);
17647 heap_freetuple(tuple);
17651 * ALTER FOREIGN TABLE <name> OPTIONS (...)
17653 static void
17654 ATExecGenericOptions(Relation rel, List *options)
17656 Relation ftrel;
17657 ForeignServer *server;
17658 ForeignDataWrapper *fdw;
17659 HeapTuple tuple;
17660 bool isnull;
17661 Datum repl_val[Natts_pg_foreign_table];
17662 bool repl_null[Natts_pg_foreign_table];
17663 bool repl_repl[Natts_pg_foreign_table];
17664 Datum datum;
17665 Form_pg_foreign_table tableform;
17667 if (options == NIL)
17668 return;
17670 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
17672 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
17673 ObjectIdGetDatum(rel->rd_id));
17674 if (!HeapTupleIsValid(tuple))
17675 ereport(ERROR,
17676 (errcode(ERRCODE_UNDEFINED_OBJECT),
17677 errmsg("foreign table \"%s\" does not exist",
17678 RelationGetRelationName(rel))));
17679 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
17680 server = GetForeignServer(tableform->ftserver);
17681 fdw = GetForeignDataWrapper(server->fdwid);
17683 memset(repl_val, 0, sizeof(repl_val));
17684 memset(repl_null, false, sizeof(repl_null));
17685 memset(repl_repl, false, sizeof(repl_repl));
17687 /* Extract the current options */
17688 datum = SysCacheGetAttr(FOREIGNTABLEREL,
17689 tuple,
17690 Anum_pg_foreign_table_ftoptions,
17691 &isnull);
17692 if (isnull)
17693 datum = PointerGetDatum(NULL);
17695 /* Transform the options */
17696 datum = transformGenericOptions(ForeignTableRelationId,
17697 datum,
17698 options,
17699 fdw->fdwvalidator);
17701 if (PointerIsValid(DatumGetPointer(datum)))
17702 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
17703 else
17704 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
17706 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
17708 /* Everything looks good - update the tuple */
17710 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
17711 repl_val, repl_null, repl_repl);
17713 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
17716 * Invalidate relcache so that all sessions will refresh any cached plans
17717 * that might depend on the old options.
17719 CacheInvalidateRelcache(rel);
17721 InvokeObjectPostAlterHook(ForeignTableRelationId,
17722 RelationGetRelid(rel), 0);
17724 table_close(ftrel, RowExclusiveLock);
17726 heap_freetuple(tuple);
17730 * ALTER TABLE ALTER COLUMN SET COMPRESSION
17732 * Return value is the address of the modified column
17734 static ObjectAddress
17735 ATExecSetCompression(Relation rel,
17736 const char *column,
17737 Node *newValue,
17738 LOCKMODE lockmode)
17740 Relation attrel;
17741 HeapTuple tuple;
17742 Form_pg_attribute atttableform;
17743 AttrNumber attnum;
17744 char *compression;
17745 char cmethod;
17746 ObjectAddress address;
17748 compression = strVal(newValue);
17750 attrel = table_open(AttributeRelationId, RowExclusiveLock);
17752 /* copy the cache entry so we can scribble on it below */
17753 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
17754 if (!HeapTupleIsValid(tuple))
17755 ereport(ERROR,
17756 (errcode(ERRCODE_UNDEFINED_COLUMN),
17757 errmsg("column \"%s\" of relation \"%s\" does not exist",
17758 column, RelationGetRelationName(rel))));
17760 /* prevent them from altering a system attribute */
17761 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
17762 attnum = atttableform->attnum;
17763 if (attnum <= 0)
17764 ereport(ERROR,
17765 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17766 errmsg("cannot alter system column \"%s\"", column)));
17769 * Check that column type is compressible, then get the attribute
17770 * compression method code
17772 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
17774 /* update pg_attribute entry */
17775 atttableform->attcompression = cmethod;
17776 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
17778 InvokeObjectPostAlterHook(RelationRelationId,
17779 RelationGetRelid(rel),
17780 attnum);
17783 * Apply the change to indexes as well (only for simple index columns,
17784 * matching behavior of index.c ConstructTupleDescriptor()).
17786 SetIndexStorageProperties(rel, attrel, attnum,
17787 false, 0,
17788 true, cmethod,
17789 lockmode);
17791 heap_freetuple(tuple);
17793 table_close(attrel, RowExclusiveLock);
17795 /* make changes visible */
17796 CommandCounterIncrement();
17798 ObjectAddressSubSet(address, RelationRelationId,
17799 RelationGetRelid(rel), attnum);
17800 return address;
17805 * Preparation phase for SET LOGGED/UNLOGGED
17807 * This verifies that we're not trying to change a temp table. Also,
17808 * existing foreign key constraints are checked to avoid ending up with
17809 * permanent tables referencing unlogged tables.
17811 * Return value is false if the operation is a no-op (in which case the
17812 * checks are skipped), otherwise true.
17814 static bool
17815 ATPrepChangePersistence(Relation rel, bool toLogged)
17817 Relation pg_constraint;
17818 HeapTuple tuple;
17819 SysScanDesc scan;
17820 ScanKeyData skey[1];
17823 * Disallow changing status for a temp table. Also verify whether we can
17824 * get away with doing nothing; in such cases we don't need to run the
17825 * checks below, either.
17827 switch (rel->rd_rel->relpersistence)
17829 case RELPERSISTENCE_TEMP:
17830 ereport(ERROR,
17831 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17832 errmsg("cannot change logged status of table \"%s\" because it is temporary",
17833 RelationGetRelationName(rel)),
17834 errtable(rel)));
17835 break;
17836 case RELPERSISTENCE_PERMANENT:
17837 if (toLogged)
17838 /* nothing to do */
17839 return false;
17840 break;
17841 case RELPERSISTENCE_UNLOGGED:
17842 if (!toLogged)
17843 /* nothing to do */
17844 return false;
17845 break;
17849 * Check that the table is not part of any publication when changing to
17850 * UNLOGGED, as UNLOGGED tables can't be published.
17852 if (!toLogged &&
17853 GetRelationPublications(RelationGetRelid(rel)) != NIL)
17854 ereport(ERROR,
17855 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
17856 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
17857 RelationGetRelationName(rel)),
17858 errdetail("Unlogged relations cannot be replicated.")));
17861 * Check existing foreign key constraints to preserve the invariant that
17862 * permanent tables cannot reference unlogged ones. Self-referencing
17863 * foreign keys can safely be ignored.
17865 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
17868 * Scan conrelid if changing to permanent, else confrelid. This also
17869 * determines whether a useful index exists.
17871 ScanKeyInit(&skey[0],
17872 toLogged ? Anum_pg_constraint_conrelid :
17873 Anum_pg_constraint_confrelid,
17874 BTEqualStrategyNumber, F_OIDEQ,
17875 ObjectIdGetDatum(RelationGetRelid(rel)));
17876 scan = systable_beginscan(pg_constraint,
17877 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
17878 true, NULL, 1, skey);
17880 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
17882 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
17884 if (con->contype == CONSTRAINT_FOREIGN)
17886 Oid foreignrelid;
17887 Relation foreignrel;
17889 /* the opposite end of what we used as scankey */
17890 foreignrelid = toLogged ? con->confrelid : con->conrelid;
17892 /* ignore if self-referencing */
17893 if (RelationGetRelid(rel) == foreignrelid)
17894 continue;
17896 foreignrel = relation_open(foreignrelid, AccessShareLock);
17898 if (toLogged)
17900 if (!RelationIsPermanent(foreignrel))
17901 ereport(ERROR,
17902 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17903 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17904 RelationGetRelationName(rel),
17905 RelationGetRelationName(foreignrel)),
17906 errtableconstraint(rel, NameStr(con->conname))));
17908 else
17910 if (RelationIsPermanent(foreignrel))
17911 ereport(ERROR,
17912 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17913 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17914 RelationGetRelationName(rel),
17915 RelationGetRelationName(foreignrel)),
17916 errtableconstraint(rel, NameStr(con->conname))));
17919 relation_close(foreignrel, AccessShareLock);
17923 systable_endscan(scan);
17925 table_close(pg_constraint, AccessShareLock);
17927 return true;
17931 * Execute ALTER TABLE SET SCHEMA
17933 ObjectAddress
17934 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17936 Relation rel;
17937 Oid relid;
17938 Oid oldNspOid;
17939 Oid nspOid;
17940 RangeVar *newrv;
17941 ObjectAddresses *objsMoved;
17942 ObjectAddress myself;
17944 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17945 stmt->missing_ok ? RVR_MISSING_OK : 0,
17946 RangeVarCallbackForAlterRelation,
17947 (void *) stmt);
17949 if (!OidIsValid(relid))
17951 ereport(NOTICE,
17952 (errmsg("relation \"%s\" does not exist, skipping",
17953 stmt->relation->relname)));
17954 return InvalidObjectAddress;
17957 rel = relation_open(relid, NoLock);
17959 oldNspOid = RelationGetNamespace(rel);
17961 /* If it's an owned sequence, disallow moving it by itself. */
17962 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17964 Oid tableId;
17965 int32 colId;
17967 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17968 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17969 ereport(ERROR,
17970 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17971 errmsg("cannot move an owned sequence into another schema"),
17972 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17973 RelationGetRelationName(rel),
17974 get_rel_name(tableId))));
17977 /* Get and lock schema OID and check its permissions. */
17978 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17979 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17981 /* common checks on switching namespaces */
17982 CheckSetNamespace(oldNspOid, nspOid);
17984 objsMoved = new_object_addresses();
17985 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17986 free_object_addresses(objsMoved);
17988 ObjectAddressSet(myself, RelationRelationId, relid);
17990 if (oldschema)
17991 *oldschema = oldNspOid;
17993 /* close rel, but keep lock until commit */
17994 relation_close(rel, NoLock);
17996 return myself;
18000 * The guts of relocating a table or materialized view to another namespace:
18001 * besides moving the relation itself, its dependent objects are relocated to
18002 * the new schema.
18004 void
18005 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
18006 ObjectAddresses *objsMoved)
18008 Relation classRel;
18010 Assert(objsMoved != NULL);
18012 /* OK, modify the pg_class row and pg_depend entry */
18013 classRel = table_open(RelationRelationId, RowExclusiveLock);
18015 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
18016 nspOid, true, objsMoved);
18018 /* Fix the table's row type too, if it has one */
18019 if (OidIsValid(rel->rd_rel->reltype))
18020 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
18021 false, /* isImplicitArray */
18022 false, /* ignoreDependent */
18023 false, /* errorOnTableType */
18024 objsMoved);
18026 /* Fix other dependent stuff */
18027 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
18028 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
18029 objsMoved, AccessExclusiveLock);
18030 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
18031 false, objsMoved);
18033 table_close(classRel, RowExclusiveLock);
18037 * The guts of relocating a relation to another namespace: fix the pg_class
18038 * entry, and the pg_depend entry if any. Caller must already have
18039 * opened and write-locked pg_class.
18041 void
18042 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
18043 Oid oldNspOid, Oid newNspOid,
18044 bool hasDependEntry,
18045 ObjectAddresses *objsMoved)
18047 HeapTuple classTup;
18048 Form_pg_class classForm;
18049 ObjectAddress thisobj;
18050 bool already_done = false;
18052 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
18053 if (!HeapTupleIsValid(classTup))
18054 elog(ERROR, "cache lookup failed for relation %u", relOid);
18055 classForm = (Form_pg_class) GETSTRUCT(classTup);
18057 Assert(classForm->relnamespace == oldNspOid);
18059 thisobj.classId = RelationRelationId;
18060 thisobj.objectId = relOid;
18061 thisobj.objectSubId = 0;
18064 * If the object has already been moved, don't move it again. If it's
18065 * already in the right place, don't move it, but still fire the object
18066 * access hook.
18068 already_done = object_address_present(&thisobj, objsMoved);
18069 if (!already_done && oldNspOid != newNspOid)
18071 /* check for duplicate name (more friendly than unique-index failure) */
18072 if (get_relname_relid(NameStr(classForm->relname),
18073 newNspOid) != InvalidOid)
18074 ereport(ERROR,
18075 (errcode(ERRCODE_DUPLICATE_TABLE),
18076 errmsg("relation \"%s\" already exists in schema \"%s\"",
18077 NameStr(classForm->relname),
18078 get_namespace_name(newNspOid))));
18080 /* classTup is a copy, so OK to scribble on */
18081 classForm->relnamespace = newNspOid;
18083 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
18085 /* Update dependency on schema if caller said so */
18086 if (hasDependEntry &&
18087 changeDependencyFor(RelationRelationId,
18088 relOid,
18089 NamespaceRelationId,
18090 oldNspOid,
18091 newNspOid) != 1)
18092 elog(ERROR, "could not change schema dependency for relation \"%s\"",
18093 NameStr(classForm->relname));
18095 if (!already_done)
18097 add_exact_object_address(&thisobj, objsMoved);
18099 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
18102 heap_freetuple(classTup);
18106 * Move all indexes for the specified relation to another namespace.
18108 * Note: we assume adequate permission checking was done by the caller,
18109 * and that the caller has a suitable lock on the owning relation.
18111 static void
18112 AlterIndexNamespaces(Relation classRel, Relation rel,
18113 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
18115 List *indexList;
18116 ListCell *l;
18118 indexList = RelationGetIndexList(rel);
18120 foreach(l, indexList)
18122 Oid indexOid = lfirst_oid(l);
18123 ObjectAddress thisobj;
18125 thisobj.classId = RelationRelationId;
18126 thisobj.objectId = indexOid;
18127 thisobj.objectSubId = 0;
18130 * Note: currently, the index will not have its own dependency on the
18131 * namespace, so we don't need to do changeDependencyFor(). There's no
18132 * row type in pg_type, either.
18134 * XXX this objsMoved test may be pointless -- surely we have a single
18135 * dependency link from a relation to each index?
18137 if (!object_address_present(&thisobj, objsMoved))
18139 AlterRelationNamespaceInternal(classRel, indexOid,
18140 oldNspOid, newNspOid,
18141 false, objsMoved);
18142 add_exact_object_address(&thisobj, objsMoved);
18146 list_free(indexList);
18150 * Move all identity and SERIAL-column sequences of the specified relation to another
18151 * namespace.
18153 * Note: we assume adequate permission checking was done by the caller,
18154 * and that the caller has a suitable lock on the owning relation.
18156 static void
18157 AlterSeqNamespaces(Relation classRel, Relation rel,
18158 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
18159 LOCKMODE lockmode)
18161 Relation depRel;
18162 SysScanDesc scan;
18163 ScanKeyData key[2];
18164 HeapTuple tup;
18167 * SERIAL sequences are those having an auto dependency on one of the
18168 * table's columns (we don't care *which* column, exactly).
18170 depRel = table_open(DependRelationId, AccessShareLock);
18172 ScanKeyInit(&key[0],
18173 Anum_pg_depend_refclassid,
18174 BTEqualStrategyNumber, F_OIDEQ,
18175 ObjectIdGetDatum(RelationRelationId));
18176 ScanKeyInit(&key[1],
18177 Anum_pg_depend_refobjid,
18178 BTEqualStrategyNumber, F_OIDEQ,
18179 ObjectIdGetDatum(RelationGetRelid(rel)));
18180 /* we leave refobjsubid unspecified */
18182 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
18183 NULL, 2, key);
18185 while (HeapTupleIsValid(tup = systable_getnext(scan)))
18187 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
18188 Relation seqRel;
18190 /* skip dependencies other than auto dependencies on columns */
18191 if (depForm->refobjsubid == 0 ||
18192 depForm->classid != RelationRelationId ||
18193 depForm->objsubid != 0 ||
18194 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
18195 continue;
18197 /* Use relation_open just in case it's an index */
18198 seqRel = relation_open(depForm->objid, lockmode);
18200 /* skip non-sequence relations */
18201 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
18203 /* No need to keep the lock */
18204 relation_close(seqRel, lockmode);
18205 continue;
18208 /* Fix the pg_class and pg_depend entries */
18209 AlterRelationNamespaceInternal(classRel, depForm->objid,
18210 oldNspOid, newNspOid,
18211 true, objsMoved);
18214 * Sequences used to have entries in pg_type, but no longer do. If we
18215 * ever re-instate that, we'll need to move the pg_type entry to the
18216 * new namespace, too (using AlterTypeNamespaceInternal).
18218 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
18220 /* Now we can close it. Keep the lock till end of transaction. */
18221 relation_close(seqRel, NoLock);
18224 systable_endscan(scan);
18226 relation_close(depRel, AccessShareLock);
18231 * This code supports
18232 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
18234 * Because we only support this for TEMP tables, it's sufficient to remember
18235 * the state in a backend-local data structure.
18239 * Register a newly-created relation's ON COMMIT action.
18241 void
18242 register_on_commit_action(Oid relid, OnCommitAction action)
18244 OnCommitItem *oc;
18245 MemoryContext oldcxt;
18248 * We needn't bother registering the relation unless there is an ON COMMIT
18249 * action we need to take.
18251 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
18252 return;
18254 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
18256 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
18257 oc->relid = relid;
18258 oc->oncommit = action;
18259 oc->creating_subid = GetCurrentSubTransactionId();
18260 oc->deleting_subid = InvalidSubTransactionId;
18263 * We use lcons() here so that ON COMMIT actions are processed in reverse
18264 * order of registration. That might not be essential but it seems
18265 * reasonable.
18267 on_commits = lcons(oc, on_commits);
18269 MemoryContextSwitchTo(oldcxt);
18273 * Unregister any ON COMMIT action when a relation is deleted.
18275 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
18277 void
18278 remove_on_commit_action(Oid relid)
18280 ListCell *l;
18282 foreach(l, on_commits)
18284 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18286 if (oc->relid == relid)
18288 oc->deleting_subid = GetCurrentSubTransactionId();
18289 break;
18295 * Perform ON COMMIT actions.
18297 * This is invoked just before actually committing, since it's possible
18298 * to encounter errors.
18300 void
18301 PreCommit_on_commit_actions(void)
18303 ListCell *l;
18304 List *oids_to_truncate = NIL;
18305 List *oids_to_drop = NIL;
18307 foreach(l, on_commits)
18309 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18311 /* Ignore entry if already dropped in this xact */
18312 if (oc->deleting_subid != InvalidSubTransactionId)
18313 continue;
18315 switch (oc->oncommit)
18317 case ONCOMMIT_NOOP:
18318 case ONCOMMIT_PRESERVE_ROWS:
18319 /* Do nothing (there shouldn't be such entries, actually) */
18320 break;
18321 case ONCOMMIT_DELETE_ROWS:
18324 * If this transaction hasn't accessed any temporary
18325 * relations, we can skip truncating ON COMMIT DELETE ROWS
18326 * tables, as they must still be empty.
18328 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
18329 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
18330 break;
18331 case ONCOMMIT_DROP:
18332 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
18333 break;
18338 * Truncate relations before dropping so that all dependencies between
18339 * relations are removed after they are worked on. Doing it like this
18340 * might be a waste as it is possible that a relation being truncated will
18341 * be dropped anyway due to its parent being dropped, but this makes the
18342 * code more robust because of not having to re-check that the relation
18343 * exists at truncation time.
18345 if (oids_to_truncate != NIL)
18346 heap_truncate(oids_to_truncate);
18348 if (oids_to_drop != NIL)
18350 ObjectAddresses *targetObjects = new_object_addresses();
18352 foreach(l, oids_to_drop)
18354 ObjectAddress object;
18356 object.classId = RelationRelationId;
18357 object.objectId = lfirst_oid(l);
18358 object.objectSubId = 0;
18360 Assert(!object_address_present(&object, targetObjects));
18362 add_exact_object_address(&object, targetObjects);
18366 * Object deletion might involve toast table access (to clean up
18367 * toasted catalog entries), so ensure we have a valid snapshot.
18369 PushActiveSnapshot(GetTransactionSnapshot());
18372 * Since this is an automatic drop, rather than one directly initiated
18373 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
18375 performMultipleDeletions(targetObjects, DROP_CASCADE,
18376 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
18378 PopActiveSnapshot();
18380 #ifdef USE_ASSERT_CHECKING
18383 * Note that table deletion will call remove_on_commit_action, so the
18384 * entry should get marked as deleted.
18386 foreach(l, on_commits)
18388 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
18390 if (oc->oncommit != ONCOMMIT_DROP)
18391 continue;
18393 Assert(oc->deleting_subid != InvalidSubTransactionId);
18395 #endif
18400 * Post-commit or post-abort cleanup for ON COMMIT management.
18402 * All we do here is remove no-longer-needed OnCommitItem entries.
18404 * During commit, remove entries that were deleted during this transaction;
18405 * during abort, remove those created during this transaction.
18407 void
18408 AtEOXact_on_commit_actions(bool isCommit)
18410 ListCell *cur_item;
18412 foreach(cur_item, on_commits)
18414 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18416 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
18417 oc->creating_subid != InvalidSubTransactionId)
18419 /* cur_item must be removed */
18420 on_commits = foreach_delete_current(on_commits, cur_item);
18421 pfree(oc);
18423 else
18425 /* cur_item must be preserved */
18426 oc->creating_subid = InvalidSubTransactionId;
18427 oc->deleting_subid = InvalidSubTransactionId;
18433 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
18435 * During subabort, we can immediately remove entries created during this
18436 * subtransaction. During subcommit, just relabel entries marked during
18437 * this subtransaction as being the parent's responsibility.
18439 void
18440 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
18441 SubTransactionId parentSubid)
18443 ListCell *cur_item;
18445 foreach(cur_item, on_commits)
18447 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
18449 if (!isCommit && oc->creating_subid == mySubid)
18451 /* cur_item must be removed */
18452 on_commits = foreach_delete_current(on_commits, cur_item);
18453 pfree(oc);
18455 else
18457 /* cur_item must be preserved */
18458 if (oc->creating_subid == mySubid)
18459 oc->creating_subid = parentSubid;
18460 if (oc->deleting_subid == mySubid)
18461 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
18467 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
18468 * the relation to be locked only if (1) it's a plain or partitioned table,
18469 * materialized view, or TOAST table and (2) the current user is the owner (or
18470 * the superuser) or has been granted MAINTAIN. This meets the
18471 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
18472 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
18474 void
18475 RangeVarCallbackMaintainsTable(const RangeVar *relation,
18476 Oid relId, Oid oldRelId, void *arg)
18478 char relkind;
18479 AclResult aclresult;
18481 /* Nothing to do if the relation was not found. */
18482 if (!OidIsValid(relId))
18483 return;
18486 * If the relation does exist, check whether it's an index. But note that
18487 * the relation might have been dropped between the time we did the name
18488 * lookup and now. In that case, there's nothing to do.
18490 relkind = get_rel_relkind(relId);
18491 if (!relkind)
18492 return;
18493 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
18494 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
18495 ereport(ERROR,
18496 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18497 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
18499 /* Check permissions */
18500 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
18501 if (aclresult != ACLCHECK_OK)
18502 aclcheck_error(aclresult,
18503 get_relkind_objtype(get_rel_relkind(relId)),
18504 relation->relname);
18508 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
18510 static void
18511 RangeVarCallbackForTruncate(const RangeVar *relation,
18512 Oid relId, Oid oldRelId, void *arg)
18514 HeapTuple tuple;
18516 /* Nothing to do if the relation was not found. */
18517 if (!OidIsValid(relId))
18518 return;
18520 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18521 if (!HeapTupleIsValid(tuple)) /* should not happen */
18522 elog(ERROR, "cache lookup failed for relation %u", relId);
18524 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
18525 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
18527 ReleaseSysCache(tuple);
18531 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
18532 * the owner of the relation, or superuser.
18534 void
18535 RangeVarCallbackOwnsRelation(const RangeVar *relation,
18536 Oid relId, Oid oldRelId, void *arg)
18538 HeapTuple tuple;
18540 /* Nothing to do if the relation was not found. */
18541 if (!OidIsValid(relId))
18542 return;
18544 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
18545 if (!HeapTupleIsValid(tuple)) /* should not happen */
18546 elog(ERROR, "cache lookup failed for relation %u", relId);
18548 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
18549 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
18550 relation->relname);
18552 if (!allowSystemTableMods &&
18553 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
18554 ereport(ERROR,
18555 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18556 errmsg("permission denied: \"%s\" is a system catalog",
18557 relation->relname)));
18559 ReleaseSysCache(tuple);
18563 * Common RangeVarGetRelid callback for rename, set schema, and alter table
18564 * processing.
18566 static void
18567 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
18568 void *arg)
18570 Node *stmt = (Node *) arg;
18571 ObjectType reltype;
18572 HeapTuple tuple;
18573 Form_pg_class classform;
18574 AclResult aclresult;
18575 char relkind;
18577 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
18578 if (!HeapTupleIsValid(tuple))
18579 return; /* concurrently dropped */
18580 classform = (Form_pg_class) GETSTRUCT(tuple);
18581 relkind = classform->relkind;
18583 /* Must own relation. */
18584 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
18585 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
18587 /* No system table modifications unless explicitly allowed. */
18588 if (!allowSystemTableMods && IsSystemClass(relid, classform))
18589 ereport(ERROR,
18590 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
18591 errmsg("permission denied: \"%s\" is a system catalog",
18592 rv->relname)));
18595 * Extract the specified relation type from the statement parse tree.
18597 * Also, for ALTER .. RENAME, check permissions: the user must (still)
18598 * have CREATE rights on the containing namespace.
18600 if (IsA(stmt, RenameStmt))
18602 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
18603 GetUserId(), ACL_CREATE);
18604 if (aclresult != ACLCHECK_OK)
18605 aclcheck_error(aclresult, OBJECT_SCHEMA,
18606 get_namespace_name(classform->relnamespace));
18607 reltype = ((RenameStmt *) stmt)->renameType;
18609 else if (IsA(stmt, AlterObjectSchemaStmt))
18610 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
18612 else if (IsA(stmt, AlterTableStmt))
18613 reltype = ((AlterTableStmt *) stmt)->objtype;
18614 else
18616 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
18617 reltype = OBJECT_TABLE; /* placate compiler */
18621 * For compatibility with prior releases, we allow ALTER TABLE to be used
18622 * with most other types of relations (but not composite types). We allow
18623 * similar flexibility for ALTER INDEX in the case of RENAME, but not
18624 * otherwise. Otherwise, the user must select the correct form of the
18625 * command for the relation at issue.
18627 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
18628 ereport(ERROR,
18629 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18630 errmsg("\"%s\" is not a sequence", rv->relname)));
18632 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
18633 ereport(ERROR,
18634 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18635 errmsg("\"%s\" is not a view", rv->relname)));
18637 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
18638 ereport(ERROR,
18639 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18640 errmsg("\"%s\" is not a materialized view", rv->relname)));
18642 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
18643 ereport(ERROR,
18644 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18645 errmsg("\"%s\" is not a foreign table", rv->relname)));
18647 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
18648 ereport(ERROR,
18649 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18650 errmsg("\"%s\" is not a composite type", rv->relname)));
18652 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
18653 relkind != RELKIND_PARTITIONED_INDEX
18654 && !IsA(stmt, RenameStmt))
18655 ereport(ERROR,
18656 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18657 errmsg("\"%s\" is not an index", rv->relname)));
18660 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
18661 * TYPE for that.
18663 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
18664 ereport(ERROR,
18665 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18666 errmsg("\"%s\" is a composite type", rv->relname),
18667 /* translator: %s is an SQL ALTER command */
18668 errhint("Use %s instead.",
18669 "ALTER TYPE")));
18672 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
18673 * to a different schema, such as indexes and TOAST tables.
18675 if (IsA(stmt, AlterObjectSchemaStmt))
18677 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
18678 ereport(ERROR,
18679 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18680 errmsg("cannot change schema of index \"%s\"",
18681 rv->relname),
18682 errhint("Change the schema of the table instead.")));
18683 else if (relkind == RELKIND_COMPOSITE_TYPE)
18684 ereport(ERROR,
18685 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18686 errmsg("cannot change schema of composite type \"%s\"",
18687 rv->relname),
18688 /* translator: %s is an SQL ALTER command */
18689 errhint("Use %s instead.",
18690 "ALTER TYPE")));
18691 else if (relkind == RELKIND_TOASTVALUE)
18692 ereport(ERROR,
18693 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18694 errmsg("cannot change schema of TOAST table \"%s\"",
18695 rv->relname),
18696 errhint("Change the schema of the table instead.")));
18699 ReleaseSysCache(tuple);
18703 * Transform any expressions present in the partition key
18705 * Returns a transformed PartitionSpec.
18707 static PartitionSpec *
18708 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
18710 PartitionSpec *newspec;
18711 ParseState *pstate;
18712 ParseNamespaceItem *nsitem;
18713 ListCell *l;
18715 newspec = makeNode(PartitionSpec);
18717 newspec->strategy = partspec->strategy;
18718 newspec->partParams = NIL;
18719 newspec->location = partspec->location;
18721 /* Check valid number of columns for strategy */
18722 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
18723 list_length(partspec->partParams) != 1)
18724 ereport(ERROR,
18725 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18726 errmsg("cannot use \"list\" partition strategy with more than one column")));
18729 * Create a dummy ParseState and insert the target relation as its sole
18730 * rangetable entry. We need a ParseState for transformExpr.
18732 pstate = make_parsestate(NULL);
18733 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
18734 NULL, false, true);
18735 addNSItemToQuery(pstate, nsitem, true, true, true);
18737 /* take care of any partition expressions */
18738 foreach(l, partspec->partParams)
18740 PartitionElem *pelem = lfirst_node(PartitionElem, l);
18742 if (pelem->expr)
18744 /* Copy, to avoid scribbling on the input */
18745 pelem = copyObject(pelem);
18747 /* Now do parse transformation of the expression */
18748 pelem->expr = transformExpr(pstate, pelem->expr,
18749 EXPR_KIND_PARTITION_EXPRESSION);
18751 /* we have to fix its collations too */
18752 assign_expr_collations(pstate, pelem->expr);
18755 newspec->partParams = lappend(newspec->partParams, pelem);
18758 return newspec;
18762 * Compute per-partition-column information from a list of PartitionElems.
18763 * Expressions in the PartitionElems must be parse-analyzed already.
18765 static void
18766 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
18767 List **partexprs, Oid *partopclass, Oid *partcollation,
18768 PartitionStrategy strategy)
18770 int attn;
18771 ListCell *lc;
18772 Oid am_oid;
18774 attn = 0;
18775 foreach(lc, partParams)
18777 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
18778 Oid atttype;
18779 Oid attcollation;
18781 if (pelem->name != NULL)
18783 /* Simple attribute reference */
18784 HeapTuple atttuple;
18785 Form_pg_attribute attform;
18787 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
18788 pelem->name);
18789 if (!HeapTupleIsValid(atttuple))
18790 ereport(ERROR,
18791 (errcode(ERRCODE_UNDEFINED_COLUMN),
18792 errmsg("column \"%s\" named in partition key does not exist",
18793 pelem->name),
18794 parser_errposition(pstate, pelem->location)));
18795 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
18797 if (attform->attnum <= 0)
18798 ereport(ERROR,
18799 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18800 errmsg("cannot use system column \"%s\" in partition key",
18801 pelem->name),
18802 parser_errposition(pstate, pelem->location)));
18805 * Generated columns cannot work: They are computed after BEFORE
18806 * triggers, but partition routing is done before all triggers.
18808 if (attform->attgenerated)
18809 ereport(ERROR,
18810 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18811 errmsg("cannot use generated column in partition key"),
18812 errdetail("Column \"%s\" is a generated column.",
18813 pelem->name),
18814 parser_errposition(pstate, pelem->location)));
18816 partattrs[attn] = attform->attnum;
18817 atttype = attform->atttypid;
18818 attcollation = attform->attcollation;
18819 ReleaseSysCache(atttuple);
18821 else
18823 /* Expression */
18824 Node *expr = pelem->expr;
18825 char partattname[16];
18827 Assert(expr != NULL);
18828 atttype = exprType(expr);
18829 attcollation = exprCollation(expr);
18832 * The expression must be of a storable type (e.g., not RECORD).
18833 * The test is the same as for whether a table column is of a safe
18834 * type (which is why we needn't check for the non-expression
18835 * case).
18837 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
18838 CheckAttributeType(partattname,
18839 atttype, attcollation,
18840 NIL, CHKATYPE_IS_PARTKEY);
18843 * Strip any top-level COLLATE clause. This ensures that we treat
18844 * "x COLLATE y" and "(x COLLATE y)" alike.
18846 while (IsA(expr, CollateExpr))
18847 expr = (Node *) ((CollateExpr *) expr)->arg;
18849 if (IsA(expr, Var) &&
18850 ((Var *) expr)->varattno > 0)
18853 * User wrote "(column)" or "(column COLLATE something)".
18854 * Treat it like simple attribute anyway.
18856 partattrs[attn] = ((Var *) expr)->varattno;
18858 else
18860 Bitmapset *expr_attrs = NULL;
18861 int i;
18863 partattrs[attn] = 0; /* marks the column as expression */
18864 *partexprs = lappend(*partexprs, expr);
18867 * transformPartitionSpec() should have already rejected
18868 * subqueries, aggregates, window functions, and SRFs, based
18869 * on the EXPR_KIND_ for partition expressions.
18873 * Cannot allow system column references, since that would
18874 * make partition routing impossible: their values won't be
18875 * known yet when we need to do that.
18877 pull_varattnos(expr, 1, &expr_attrs);
18878 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
18880 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
18881 expr_attrs))
18882 ereport(ERROR,
18883 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18884 errmsg("partition key expressions cannot contain system column references")));
18888 * Generated columns cannot work: They are computed after
18889 * BEFORE triggers, but partition routing is done before all
18890 * triggers.
18892 i = -1;
18893 while ((i = bms_next_member(expr_attrs, i)) >= 0)
18895 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18897 if (attno > 0 &&
18898 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18899 ereport(ERROR,
18900 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18901 errmsg("cannot use generated column in partition key"),
18902 errdetail("Column \"%s\" is a generated column.",
18903 get_attname(RelationGetRelid(rel), attno, false)),
18904 parser_errposition(pstate, pelem->location)));
18908 * Preprocess the expression before checking for mutability.
18909 * This is essential for the reasons described in
18910 * contain_mutable_functions_after_planning. However, we call
18911 * expression_planner for ourselves rather than using that
18912 * function, because if constant-folding reduces the
18913 * expression to a constant, we'd like to know that so we can
18914 * complain below.
18916 * Like contain_mutable_functions_after_planning, assume that
18917 * expression_planner won't scribble on its input, so this
18918 * won't affect the partexprs entry we saved above.
18920 expr = (Node *) expression_planner((Expr *) expr);
18923 * Partition expressions cannot contain mutable functions,
18924 * because a given row must always map to the same partition
18925 * as long as there is no change in the partition boundary
18926 * structure.
18928 if (contain_mutable_functions(expr))
18929 ereport(ERROR,
18930 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18931 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18934 * While it is not exactly *wrong* for a partition expression
18935 * to be a constant, it seems better to reject such keys.
18937 if (IsA(expr, Const))
18938 ereport(ERROR,
18939 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18940 errmsg("cannot use constant expression as partition key")));
18945 * Apply collation override if any
18947 if (pelem->collation)
18948 attcollation = get_collation_oid(pelem->collation, false);
18951 * Check we have a collation iff it's a collatable type. The only
18952 * expected failures here are (1) COLLATE applied to a noncollatable
18953 * type, or (2) partition expression had an unresolved collation. But
18954 * we might as well code this to be a complete consistency check.
18956 if (type_is_collatable(atttype))
18958 if (!OidIsValid(attcollation))
18959 ereport(ERROR,
18960 (errcode(ERRCODE_INDETERMINATE_COLLATION),
18961 errmsg("could not determine which collation to use for partition expression"),
18962 errhint("Use the COLLATE clause to set the collation explicitly.")));
18964 else
18966 if (OidIsValid(attcollation))
18967 ereport(ERROR,
18968 (errcode(ERRCODE_DATATYPE_MISMATCH),
18969 errmsg("collations are not supported by type %s",
18970 format_type_be(atttype))));
18973 partcollation[attn] = attcollation;
18976 * Identify the appropriate operator class. For list and range
18977 * partitioning, we use a btree operator class; hash partitioning uses
18978 * a hash operator class.
18980 if (strategy == PARTITION_STRATEGY_HASH)
18981 am_oid = HASH_AM_OID;
18982 else
18983 am_oid = BTREE_AM_OID;
18985 if (!pelem->opclass)
18987 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18989 if (!OidIsValid(partopclass[attn]))
18991 if (strategy == PARTITION_STRATEGY_HASH)
18992 ereport(ERROR,
18993 (errcode(ERRCODE_UNDEFINED_OBJECT),
18994 errmsg("data type %s has no default operator class for access method \"%s\"",
18995 format_type_be(atttype), "hash"),
18996 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18997 else
18998 ereport(ERROR,
18999 (errcode(ERRCODE_UNDEFINED_OBJECT),
19000 errmsg("data type %s has no default operator class for access method \"%s\"",
19001 format_type_be(atttype), "btree"),
19002 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
19005 else
19006 partopclass[attn] = ResolveOpClass(pelem->opclass,
19007 atttype,
19008 am_oid == HASH_AM_OID ? "hash" : "btree",
19009 am_oid);
19011 attn++;
19016 * PartConstraintImpliedByRelConstraint
19017 * Do scanrel's existing constraints imply the partition constraint?
19019 * "Existing constraints" include its check constraints and column-level
19020 * not-null constraints. partConstraint describes the partition constraint,
19021 * in implicit-AND form.
19023 bool
19024 PartConstraintImpliedByRelConstraint(Relation scanrel,
19025 List *partConstraint)
19027 List *existConstraint = NIL;
19028 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19029 int i;
19031 if (constr && constr->has_not_null)
19033 int natts = scanrel->rd_att->natts;
19035 for (i = 1; i <= natts; i++)
19037 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
19039 if (att->attnotnull && !att->attisdropped)
19041 NullTest *ntest = makeNode(NullTest);
19043 ntest->arg = (Expr *) makeVar(1,
19045 att->atttypid,
19046 att->atttypmod,
19047 att->attcollation,
19049 ntest->nulltesttype = IS_NOT_NULL;
19052 * argisrow=false is correct even for a composite column,
19053 * because attnotnull does not represent a SQL-spec IS NOT
19054 * NULL test in such a case, just IS DISTINCT FROM NULL.
19056 ntest->argisrow = false;
19057 ntest->location = -1;
19058 existConstraint = lappend(existConstraint, ntest);
19063 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
19067 * ConstraintImpliedByRelConstraint
19068 * Do scanrel's existing constraints imply the given constraint?
19070 * testConstraint is the constraint to validate. provenConstraint is a
19071 * caller-provided list of conditions which this function may assume
19072 * to be true. Both provenConstraint and testConstraint must be in
19073 * implicit-AND form, must only contain immutable clauses, and must
19074 * contain only Vars with varno = 1.
19076 bool
19077 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
19079 List *existConstraint = list_copy(provenConstraint);
19080 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
19081 int num_check,
19084 num_check = (constr != NULL) ? constr->num_check : 0;
19085 for (i = 0; i < num_check; i++)
19087 Node *cexpr;
19090 * If this constraint hasn't been fully validated yet, we must ignore
19091 * it here.
19093 if (!constr->check[i].ccvalid)
19094 continue;
19096 cexpr = stringToNode(constr->check[i].ccbin);
19099 * Run each expression through const-simplification and
19100 * canonicalization. It is necessary, because we will be comparing it
19101 * to similarly-processed partition constraint expressions, and may
19102 * fail to detect valid matches without this.
19104 cexpr = eval_const_expressions(NULL, cexpr);
19105 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
19107 existConstraint = list_concat(existConstraint,
19108 make_ands_implicit((Expr *) cexpr));
19112 * Try to make the proof. Since we are comparing CHECK constraints, we
19113 * need to use weak implication, i.e., we assume existConstraint is
19114 * not-false and try to prove the same for testConstraint.
19116 * Note that predicate_implied_by assumes its first argument is known
19117 * immutable. That should always be true for both NOT NULL and partition
19118 * constraints, so we don't test it here.
19120 return predicate_implied_by(testConstraint, existConstraint, true);
19124 * QueuePartitionConstraintValidation
19126 * Add an entry to wqueue to have the given partition constraint validated by
19127 * Phase 3, for the given relation, and all its children.
19129 * We first verify whether the given constraint is implied by pre-existing
19130 * relation constraints; if it is, there's no need to scan the table to
19131 * validate, so don't queue in that case.
19133 static void
19134 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
19135 List *partConstraint,
19136 bool validate_default)
19139 * Based on the table's existing constraints, determine whether or not we
19140 * may skip scanning the table.
19142 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
19144 if (!validate_default)
19145 ereport(DEBUG1,
19146 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
19147 RelationGetRelationName(scanrel))));
19148 else
19149 ereport(DEBUG1,
19150 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
19151 RelationGetRelationName(scanrel))));
19152 return;
19156 * Constraints proved insufficient. For plain relations, queue a
19157 * validation item now; for partitioned tables, recurse to process each
19158 * partition.
19160 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
19162 AlteredTableInfo *tab;
19164 /* Grab a work queue entry. */
19165 tab = ATGetQueueEntry(wqueue, scanrel);
19166 Assert(tab->partition_constraint == NULL);
19167 tab->partition_constraint = (Expr *) linitial(partConstraint);
19168 tab->validate_default = validate_default;
19170 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19172 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
19173 int i;
19175 for (i = 0; i < partdesc->nparts; i++)
19177 Relation part_rel;
19178 List *thisPartConstraint;
19181 * This is the minimum lock we need to prevent deadlocks.
19183 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
19186 * Adjust the constraint for scanrel so that it matches this
19187 * partition's attribute numbers.
19189 thisPartConstraint =
19190 map_partition_varattnos(partConstraint, 1,
19191 part_rel, scanrel);
19193 QueuePartitionConstraintValidation(wqueue, part_rel,
19194 thisPartConstraint,
19195 validate_default);
19196 table_close(part_rel, NoLock); /* keep lock till commit */
19202 * attachPartitionTable: attach a new partition to the partitioned table
19204 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
19205 * of an ALTER TABLE sequence.
19206 * rel: partitioned relation;
19207 * attachrel: relation of attached partition;
19208 * bound: bounds of attached relation.
19210 static void
19211 attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
19213 /* OK to create inheritance. Rest of the checks performed there */
19214 CreateInheritance(attachrel, rel, true);
19216 /* Update the pg_class entry. */
19217 StorePartitionBound(attachrel, rel, bound);
19219 /* Ensure there exists a correct set of indexes in the partition. */
19220 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
19222 /* and triggers */
19223 CloneRowTriggersToPartition(rel, attachrel);
19226 * Clone foreign key constraints. Callee is responsible for setting up
19227 * for phase 3 constraint verification.
19229 CloneForeignKeyConstraints(wqueue, rel, attachrel);
19233 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
19235 * Return the address of the newly attached partition.
19237 static ObjectAddress
19238 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
19239 AlterTableUtilityContext *context)
19241 Relation attachrel,
19242 catalog;
19243 List *attachrel_children;
19244 List *partConstraint;
19245 SysScanDesc scan;
19246 ScanKeyData skey;
19247 AttrNumber attno;
19248 int natts;
19249 TupleDesc tupleDesc;
19250 ObjectAddress address;
19251 const char *trigger_name;
19252 Oid defaultPartOid;
19253 List *partBoundConstraint;
19254 ParseState *pstate = make_parsestate(NULL);
19256 pstate->p_sourcetext = context->queryString;
19259 * We must lock the default partition if one exists, because attaching a
19260 * new partition will change its partition constraint.
19262 defaultPartOid =
19263 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19264 if (OidIsValid(defaultPartOid))
19265 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19267 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
19270 * XXX I think it'd be a good idea to grab locks on all tables referenced
19271 * by FKs at this point also.
19275 * Must be owner of both parent and source table -- parent was checked by
19276 * ATSimplePermissions call in ATPrepCmd
19278 ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
19280 /* A partition can only have one parent */
19281 if (attachrel->rd_rel->relispartition)
19282 ereport(ERROR,
19283 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19284 errmsg("\"%s\" is already a partition",
19285 RelationGetRelationName(attachrel))));
19287 if (OidIsValid(attachrel->rd_rel->reloftype))
19288 ereport(ERROR,
19289 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19290 errmsg("cannot attach a typed table as partition")));
19293 * Table being attached should not already be part of inheritance; either
19294 * as a child table...
19296 catalog = table_open(InheritsRelationId, AccessShareLock);
19297 ScanKeyInit(&skey,
19298 Anum_pg_inherits_inhrelid,
19299 BTEqualStrategyNumber, F_OIDEQ,
19300 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19301 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
19302 NULL, 1, &skey);
19303 if (HeapTupleIsValid(systable_getnext(scan)))
19304 ereport(ERROR,
19305 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19306 errmsg("cannot attach inheritance child as partition")));
19307 systable_endscan(scan);
19309 /* ...or as a parent table (except the case when it is partitioned) */
19310 ScanKeyInit(&skey,
19311 Anum_pg_inherits_inhparent,
19312 BTEqualStrategyNumber, F_OIDEQ,
19313 ObjectIdGetDatum(RelationGetRelid(attachrel)));
19314 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
19315 1, &skey);
19316 if (HeapTupleIsValid(systable_getnext(scan)) &&
19317 attachrel->rd_rel->relkind == RELKIND_RELATION)
19318 ereport(ERROR,
19319 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19320 errmsg("cannot attach inheritance parent as partition")));
19321 systable_endscan(scan);
19322 table_close(catalog, AccessShareLock);
19325 * Prevent circularity by seeing if rel is a partition of attachrel. (In
19326 * particular, this disallows making a rel a partition of itself.)
19328 * We do that by checking if rel is a member of the list of attachrel's
19329 * partitions provided the latter is partitioned at all. We want to avoid
19330 * having to construct this list again, so we request the strongest lock
19331 * on all partitions. We need the strongest lock, because we may decide
19332 * to scan them if we find out that the table being attached (or its leaf
19333 * partitions) may contain rows that violate the partition constraint. If
19334 * the table has a constraint that would prevent such rows, which by
19335 * definition is present in all the partitions, we need not scan the
19336 * table, nor its partitions. But we cannot risk a deadlock by taking a
19337 * weaker lock now and the stronger one only when needed.
19339 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
19340 AccessExclusiveLock, NULL);
19341 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
19342 ereport(ERROR,
19343 (errcode(ERRCODE_DUPLICATE_TABLE),
19344 errmsg("circular inheritance not allowed"),
19345 errdetail("\"%s\" is already a child of \"%s\".",
19346 RelationGetRelationName(rel),
19347 RelationGetRelationName(attachrel))));
19349 /* If the parent is permanent, so must be all of its partitions. */
19350 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
19351 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
19352 ereport(ERROR,
19353 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19354 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
19355 RelationGetRelationName(rel))));
19357 /* Temp parent cannot have a partition that is itself not a temp */
19358 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19359 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
19360 ereport(ERROR,
19361 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19362 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
19363 RelationGetRelationName(rel))));
19365 /* If the parent is temp, it must belong to this session */
19366 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19367 !rel->rd_islocaltemp)
19368 ereport(ERROR,
19369 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19370 errmsg("cannot attach as partition of temporary relation of another session")));
19372 /* Ditto for the partition */
19373 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
19374 !attachrel->rd_islocaltemp)
19375 ereport(ERROR,
19376 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19377 errmsg("cannot attach temporary relation of another session as partition")));
19380 * Check if attachrel has any identity columns or any columns that aren't
19381 * in the parent.
19383 tupleDesc = RelationGetDescr(attachrel);
19384 natts = tupleDesc->natts;
19385 for (attno = 1; attno <= natts; attno++)
19387 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
19388 char *attributeName = NameStr(attribute->attname);
19390 /* Ignore dropped */
19391 if (attribute->attisdropped)
19392 continue;
19394 if (attribute->attidentity)
19395 ereport(ERROR,
19396 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19397 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
19398 RelationGetRelationName(attachrel), attributeName),
19399 errdetail("The new partition may not contain an identity column."));
19401 /* Try to find the column in parent (matching on column name) */
19402 if (!SearchSysCacheExists2(ATTNAME,
19403 ObjectIdGetDatum(RelationGetRelid(rel)),
19404 CStringGetDatum(attributeName)))
19405 ereport(ERROR,
19406 (errcode(ERRCODE_DATATYPE_MISMATCH),
19407 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
19408 RelationGetRelationName(attachrel), attributeName,
19409 RelationGetRelationName(rel)),
19410 errdetail("The new partition may contain only the columns present in parent.")));
19414 * If child_rel has row-level triggers with transition tables, we
19415 * currently don't allow it to become a partition. See also prohibitions
19416 * in ATExecAddInherit() and CreateTrigger().
19418 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
19419 if (trigger_name != NULL)
19420 ereport(ERROR,
19421 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19422 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
19423 trigger_name, RelationGetRelationName(attachrel)),
19424 errdetail("ROW triggers with transition tables are not supported on partitions.")));
19427 * Check that the new partition's bound is valid and does not overlap any
19428 * of existing partitions of the parent - note that it does not return on
19429 * error.
19431 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
19432 cmd->bound, pstate);
19434 /* Attach a new partition to the partitioned table. */
19435 attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
19438 * Generate partition constraint from the partition bound specification.
19439 * If the parent itself is a partition, make sure to include its
19440 * constraint as well.
19442 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
19443 partConstraint = list_concat(partBoundConstraint,
19444 RelationGetPartitionQual(rel));
19446 /* Skip validation if there are no constraints to validate. */
19447 if (partConstraint)
19450 * Run the partition quals through const-simplification similar to
19451 * check constraints. We skip canonicalize_qual, though, because
19452 * partition quals should be in canonical form already.
19454 partConstraint =
19455 (List *) eval_const_expressions(NULL,
19456 (Node *) partConstraint);
19458 /* XXX this sure looks wrong */
19459 partConstraint = list_make1(make_ands_explicit(partConstraint));
19462 * Adjust the generated constraint to match this partition's attribute
19463 * numbers.
19465 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
19466 rel);
19468 /* Validate partition constraints against the table being attached. */
19469 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
19470 false);
19474 * If we're attaching a partition other than the default partition and a
19475 * default one exists, then that partition's partition constraint changes,
19476 * so add an entry to the work queue to validate it, too. (We must not do
19477 * this when the partition being attached is the default one; we already
19478 * did it above!)
19480 if (OidIsValid(defaultPartOid))
19482 Relation defaultrel;
19483 List *defPartConstraint;
19485 Assert(!cmd->bound->is_default);
19487 /* we already hold a lock on the default partition */
19488 defaultrel = table_open(defaultPartOid, NoLock);
19489 defPartConstraint =
19490 get_proposed_default_constraint(partBoundConstraint);
19493 * Map the Vars in the constraint expression from rel's attnos to
19494 * defaultrel's.
19496 defPartConstraint =
19497 map_partition_varattnos(defPartConstraint,
19498 1, defaultrel, rel);
19499 QueuePartitionConstraintValidation(wqueue, defaultrel,
19500 defPartConstraint, true);
19502 /* keep our lock until commit. */
19503 table_close(defaultrel, NoLock);
19506 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
19509 * If the partition we just attached is partitioned itself, invalidate
19510 * relcache for all descendent partitions too to ensure that their
19511 * rd_partcheck expression trees are rebuilt; partitions already locked at
19512 * the beginning of this function.
19514 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19516 ListCell *l;
19518 foreach(l, attachrel_children)
19520 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
19524 /* keep our lock until commit */
19525 table_close(attachrel, NoLock);
19527 return address;
19531 * AttachPartitionEnsureIndexes
19532 * subroutine for ATExecAttachPartition to create/match indexes
19534 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
19535 * PARTITION: every partition must have an index attached to each index on the
19536 * partitioned table.
19538 static void
19539 AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
19541 List *idxes;
19542 List *attachRelIdxs;
19543 Relation *attachrelIdxRels;
19544 IndexInfo **attachInfos;
19545 ListCell *cell;
19546 MemoryContext cxt;
19547 MemoryContext oldcxt;
19549 cxt = AllocSetContextCreate(CurrentMemoryContext,
19550 "AttachPartitionEnsureIndexes",
19551 ALLOCSET_DEFAULT_SIZES);
19552 oldcxt = MemoryContextSwitchTo(cxt);
19554 idxes = RelationGetIndexList(rel);
19555 attachRelIdxs = RelationGetIndexList(attachrel);
19556 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
19557 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
19559 /* Build arrays of all existing indexes and their IndexInfos */
19560 foreach(cell, attachRelIdxs)
19562 Oid cldIdxId = lfirst_oid(cell);
19563 int i = foreach_current_index(cell);
19565 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
19566 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
19570 * If we're attaching a foreign table, we must fail if any of the indexes
19571 * is a constraint index; otherwise, there's nothing to do here. Do this
19572 * before starting work, to avoid wasting the effort of building a few
19573 * non-unique indexes before coming across a unique one.
19575 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
19577 foreach(cell, idxes)
19579 Oid idx = lfirst_oid(cell);
19580 Relation idxRel = index_open(idx, AccessShareLock);
19582 if (idxRel->rd_index->indisunique ||
19583 idxRel->rd_index->indisprimary)
19584 ereport(ERROR,
19585 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
19586 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
19587 RelationGetRelationName(attachrel),
19588 RelationGetRelationName(rel)),
19589 errdetail("Partitioned table \"%s\" contains unique indexes.",
19590 RelationGetRelationName(rel))));
19591 index_close(idxRel, AccessShareLock);
19594 goto out;
19598 * For each index on the partitioned table, find a matching one in the
19599 * partition-to-be; if one is not found, create one.
19601 foreach(cell, idxes)
19603 Oid idx = lfirst_oid(cell);
19604 Relation idxRel = index_open(idx, AccessShareLock);
19605 IndexInfo *info;
19606 AttrMap *attmap;
19607 bool found = false;
19608 Oid constraintOid;
19611 * Ignore indexes in the partitioned table other than partitioned
19612 * indexes.
19614 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
19616 index_close(idxRel, AccessShareLock);
19617 continue;
19620 /* construct an indexinfo to compare existing indexes against */
19621 info = BuildIndexInfo(idxRel);
19622 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
19623 RelationGetDescr(rel),
19624 false);
19625 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
19628 * Scan the list of existing indexes in the partition-to-be, and mark
19629 * the first matching, valid, unattached one we find, if any, as
19630 * partition of the parent index. If we find one, we're done.
19632 for (int i = 0; i < list_length(attachRelIdxs); i++)
19634 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
19635 Oid cldConstrOid = InvalidOid;
19637 /* does this index have a parent? if so, can't use it */
19638 if (attachrelIdxRels[i]->rd_rel->relispartition)
19639 continue;
19641 /* If this index is invalid, can't use it */
19642 if (!attachrelIdxRels[i]->rd_index->indisvalid)
19643 continue;
19645 if (CompareIndexInfo(attachInfos[i], info,
19646 attachrelIdxRels[i]->rd_indcollation,
19647 idxRel->rd_indcollation,
19648 attachrelIdxRels[i]->rd_opfamily,
19649 idxRel->rd_opfamily,
19650 attmap))
19653 * If this index is being created in the parent because of a
19654 * constraint, then the child needs to have a constraint also,
19655 * so look for one. If there is no such constraint, this
19656 * index is no good, so keep looking.
19658 if (OidIsValid(constraintOid))
19660 cldConstrOid =
19661 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
19662 cldIdxId);
19663 /* no dice */
19664 if (!OidIsValid(cldConstrOid))
19665 continue;
19667 /* Ensure they're both the same type of constraint */
19668 if (get_constraint_type(constraintOid) !=
19669 get_constraint_type(cldConstrOid))
19670 continue;
19673 /* bingo. */
19674 IndexSetParentIndex(attachrelIdxRels[i], idx);
19675 if (OidIsValid(constraintOid))
19676 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
19677 RelationGetRelid(attachrel));
19678 found = true;
19680 CommandCounterIncrement();
19681 break;
19686 * If no suitable index was found in the partition-to-be, create one
19687 * now.
19689 if (!found)
19691 IndexStmt *stmt;
19692 Oid conOid;
19694 stmt = generateClonedIndexStmt(NULL,
19695 idxRel, attmap,
19696 &conOid);
19699 * If the index is a primary key, mark all columns as NOT NULL if
19700 * they aren't already.
19702 if (stmt->primary)
19704 MemoryContextSwitchTo(oldcxt);
19705 for (int j = 0; j < info->ii_NumIndexKeyAttrs; j++)
19707 AttrNumber childattno;
19709 childattno = get_attnum(RelationGetRelid(attachrel),
19710 get_attname(RelationGetRelid(rel),
19711 info->ii_IndexAttrNumbers[j],
19712 false));
19713 set_attnotnull(wqueue, attachrel, childattno,
19714 true, AccessExclusiveLock);
19716 MemoryContextSwitchTo(cxt);
19719 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
19720 RelationGetRelid(idxRel),
19721 conOid,
19723 true, false, false, false, false);
19726 index_close(idxRel, AccessShareLock);
19729 out:
19730 /* Clean up. */
19731 for (int i = 0; i < list_length(attachRelIdxs); i++)
19732 index_close(attachrelIdxRels[i], AccessShareLock);
19733 MemoryContextSwitchTo(oldcxt);
19734 MemoryContextDelete(cxt);
19738 * CloneRowTriggersToPartition
19739 * subroutine for ATExecAttachPartition/DefineRelation to create row
19740 * triggers on partitions
19742 static void
19743 CloneRowTriggersToPartition(Relation parent, Relation partition)
19745 Relation pg_trigger;
19746 ScanKeyData key;
19747 SysScanDesc scan;
19748 HeapTuple tuple;
19749 MemoryContext perTupCxt;
19751 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19752 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
19753 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
19754 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
19755 true, NULL, 1, &key);
19757 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
19758 "clone trig", ALLOCSET_SMALL_SIZES);
19760 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19762 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
19763 CreateTrigStmt *trigStmt;
19764 Node *qual = NULL;
19765 Datum value;
19766 bool isnull;
19767 List *cols = NIL;
19768 List *trigargs = NIL;
19769 MemoryContext oldcxt;
19772 * Ignore statement-level triggers; those are not cloned.
19774 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
19775 continue;
19778 * Don't clone internal triggers, because the constraint cloning code
19779 * will.
19781 if (trigForm->tgisinternal)
19782 continue;
19785 * Complain if we find an unexpected trigger type.
19787 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
19788 !TRIGGER_FOR_AFTER(trigForm->tgtype))
19789 elog(ERROR, "unexpected trigger \"%s\" found",
19790 NameStr(trigForm->tgname));
19792 /* Use short-lived context for CREATE TRIGGER */
19793 oldcxt = MemoryContextSwitchTo(perTupCxt);
19796 * If there is a WHEN clause, generate a 'cooked' version of it that's
19797 * appropriate for the partition.
19799 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
19800 RelationGetDescr(pg_trigger), &isnull);
19801 if (!isnull)
19803 qual = stringToNode(TextDatumGetCString(value));
19804 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
19805 partition, parent);
19806 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
19807 partition, parent);
19811 * If there is a column list, transform it to a list of column names.
19812 * Note we don't need to map this list in any way ...
19814 if (trigForm->tgattr.dim1 > 0)
19816 int i;
19818 for (i = 0; i < trigForm->tgattr.dim1; i++)
19820 Form_pg_attribute col;
19822 col = TupleDescAttr(parent->rd_att,
19823 trigForm->tgattr.values[i] - 1);
19824 cols = lappend(cols,
19825 makeString(pstrdup(NameStr(col->attname))));
19829 /* Reconstruct trigger arguments list. */
19830 if (trigForm->tgnargs > 0)
19832 char *p;
19834 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
19835 RelationGetDescr(pg_trigger), &isnull);
19836 if (isnull)
19837 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
19838 NameStr(trigForm->tgname), RelationGetRelationName(partition));
19840 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
19842 for (int i = 0; i < trigForm->tgnargs; i++)
19844 trigargs = lappend(trigargs, makeString(pstrdup(p)));
19845 p += strlen(p) + 1;
19849 trigStmt = makeNode(CreateTrigStmt);
19850 trigStmt->replace = false;
19851 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
19852 trigStmt->trigname = NameStr(trigForm->tgname);
19853 trigStmt->relation = NULL;
19854 trigStmt->funcname = NULL; /* passed separately */
19855 trigStmt->args = trigargs;
19856 trigStmt->row = true;
19857 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
19858 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
19859 trigStmt->columns = cols;
19860 trigStmt->whenClause = NULL; /* passed separately */
19861 trigStmt->transitionRels = NIL; /* not supported at present */
19862 trigStmt->deferrable = trigForm->tgdeferrable;
19863 trigStmt->initdeferred = trigForm->tginitdeferred;
19864 trigStmt->constrrel = NULL; /* passed separately */
19866 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
19867 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
19868 trigForm->tgfoid, trigForm->oid, qual,
19869 false, true, trigForm->tgenabled);
19871 MemoryContextSwitchTo(oldcxt);
19872 MemoryContextReset(perTupCxt);
19875 MemoryContextDelete(perTupCxt);
19877 systable_endscan(scan);
19878 table_close(pg_trigger, RowExclusiveLock);
19882 * ALTER TABLE DETACH PARTITION
19884 * Return the address of the relation that is no longer a partition of rel.
19886 * If concurrent mode is requested, we run in two transactions. A side-
19887 * effect is that this command cannot run in a multi-part ALTER TABLE.
19888 * Currently, that's enforced by the grammar.
19890 * The strategy for concurrency is to first modify the partition's
19891 * pg_inherit catalog row to make it visible to everyone that the
19892 * partition is detached, lock the partition against writes, and commit
19893 * the transaction; anyone who requests the partition descriptor from
19894 * that point onwards has to ignore such a partition. In a second
19895 * transaction, we wait until all transactions that could have seen the
19896 * partition as attached are gone, then we remove the rest of partition
19897 * metadata (pg_inherits and pg_class.relpartbounds).
19899 static ObjectAddress
19900 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
19901 RangeVar *name, bool concurrent)
19903 Relation partRel;
19904 ObjectAddress address;
19905 Oid defaultPartOid;
19908 * We must lock the default partition, because detaching this partition
19909 * will change its partition constraint.
19911 defaultPartOid =
19912 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
19913 if (OidIsValid(defaultPartOid))
19916 * Concurrent detaching when a default partition exists is not
19917 * supported. The main problem is that the default partition
19918 * constraint would change. And there's a definitional problem: what
19919 * should happen to the tuples that are being inserted that belong to
19920 * the partition being detached? Putting them on the partition being
19921 * detached would be wrong, since they'd become "lost" after the
19922 * detaching completes but we cannot put them in the default partition
19923 * either until we alter its partition constraint.
19925 * I think we could solve this problem if we effected the constraint
19926 * change before committing the first transaction. But the lock would
19927 * have to remain AEL and it would cause concurrent query planning to
19928 * be blocked, so changing it that way would be even worse.
19930 if (concurrent)
19931 ereport(ERROR,
19932 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19933 errmsg("cannot detach partitions concurrently when a default partition exists")));
19934 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19938 * In concurrent mode, the partition is locked with share-update-exclusive
19939 * in the first transaction. This allows concurrent transactions to be
19940 * doing DML to the partition.
19942 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19943 AccessExclusiveLock);
19946 * Check inheritance conditions and either delete the pg_inherits row (in
19947 * non-concurrent mode) or just set the inhdetachpending flag.
19949 if (!concurrent)
19950 RemoveInheritance(partRel, rel, false);
19951 else
19952 MarkInheritDetached(partRel, rel);
19955 * Ensure that foreign keys still hold after this detach. This keeps
19956 * locks on the referencing tables, which prevents concurrent transactions
19957 * from adding rows that we wouldn't see. For this to work in concurrent
19958 * mode, it is critical that the partition appears as no longer attached
19959 * for the RI queries as soon as the first transaction commits.
19961 ATDetachCheckNoForeignKeyRefs(partRel);
19964 * Concurrent mode has to work harder; first we add a new constraint to
19965 * the partition that matches the partition constraint. Then we close our
19966 * existing transaction, and in a new one wait for all processes to catch
19967 * up on the catalog updates we've done so far; at that point we can
19968 * complete the operation.
19970 if (concurrent)
19972 Oid partrelid,
19973 parentrelid;
19974 LOCKTAG tag;
19975 char *parentrelname;
19976 char *partrelname;
19979 * Add a new constraint to the partition being detached, which
19980 * supplants the partition constraint (unless there is one already).
19982 DetachAddConstraintIfNeeded(wqueue, partRel);
19985 * We're almost done now; the only traces that remain are the
19986 * pg_inherits tuple and the partition's relpartbounds. Before we can
19987 * remove those, we need to wait until all transactions that know that
19988 * this is a partition are gone.
19992 * Remember relation OIDs to re-acquire them later; and relation names
19993 * too, for error messages if something is dropped in between.
19995 partrelid = RelationGetRelid(partRel);
19996 parentrelid = RelationGetRelid(rel);
19997 parentrelname = MemoryContextStrdup(PortalContext,
19998 RelationGetRelationName(rel));
19999 partrelname = MemoryContextStrdup(PortalContext,
20000 RelationGetRelationName(partRel));
20002 /* Invalidate relcache entries for the parent -- must be before close */
20003 CacheInvalidateRelcache(rel);
20005 table_close(partRel, NoLock);
20006 table_close(rel, NoLock);
20007 tab->rel = NULL;
20009 /* Make updated catalog entry visible */
20010 PopActiveSnapshot();
20011 CommitTransactionCommand();
20013 StartTransactionCommand();
20016 * Now wait. This ensures that all queries that were planned
20017 * including the partition are finished before we remove the rest of
20018 * catalog entries. We don't need or indeed want to acquire this
20019 * lock, though -- that would block later queries.
20021 * We don't need to concern ourselves with waiting for a lock on the
20022 * partition itself, since we will acquire AccessExclusiveLock below.
20024 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
20025 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
20028 * Now acquire locks in both relations again. Note they may have been
20029 * removed in the meantime, so care is required.
20031 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
20032 partRel = try_relation_open(partrelid, AccessExclusiveLock);
20034 /* If the relations aren't there, something bad happened; bail out */
20035 if (rel == NULL)
20037 if (partRel != NULL) /* shouldn't happen */
20038 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
20039 partrelname);
20040 ereport(ERROR,
20041 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20042 errmsg("partitioned table \"%s\" was removed concurrently",
20043 parentrelname)));
20045 if (partRel == NULL)
20046 ereport(ERROR,
20047 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20048 errmsg("partition \"%s\" was removed concurrently", partrelname)));
20050 tab->rel = rel;
20053 /* Do the final part of detaching */
20054 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
20056 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20058 /* keep our lock until commit */
20059 table_close(partRel, NoLock);
20061 return address;
20065 * Second part of ALTER TABLE .. DETACH.
20067 * This is separate so that it can be run independently when the second
20068 * transaction of the concurrent algorithm fails (crash or abort).
20070 static void
20071 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
20072 Oid defaultPartOid)
20074 Relation classRel;
20075 List *fks;
20076 ListCell *cell;
20077 List *indexes;
20078 Datum new_val[Natts_pg_class];
20079 bool new_null[Natts_pg_class],
20080 new_repl[Natts_pg_class];
20081 HeapTuple tuple,
20082 newtuple;
20083 Relation trigrel = NULL;
20085 if (concurrent)
20088 * We can remove the pg_inherits row now. (In the non-concurrent case,
20089 * this was already done).
20091 RemoveInheritance(partRel, rel, true);
20094 /* Drop any triggers that were cloned on creation/attach. */
20095 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
20098 * Detach any foreign keys that are inherited. This includes creating
20099 * additional action triggers.
20101 fks = copyObject(RelationGetFKeyList(partRel));
20102 if (fks != NIL)
20103 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
20104 foreach(cell, fks)
20106 ForeignKeyCacheInfo *fk = lfirst(cell);
20107 HeapTuple contup;
20108 Form_pg_constraint conform;
20109 Constraint *fkconstraint;
20110 Oid insertTriggerOid,
20111 updateTriggerOid;
20113 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
20114 if (!HeapTupleIsValid(contup))
20115 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
20116 conform = (Form_pg_constraint) GETSTRUCT(contup);
20118 /* consider only the inherited foreign keys */
20119 if (conform->contype != CONSTRAINT_FOREIGN ||
20120 !OidIsValid(conform->conparentid))
20122 ReleaseSysCache(contup);
20123 continue;
20126 /* unset conparentid and adjust conislocal, coninhcount, etc. */
20127 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
20130 * Also, look up the partition's "check" triggers corresponding to the
20131 * constraint being detached and detach them from the parent triggers.
20133 GetForeignKeyCheckTriggers(trigrel,
20134 fk->conoid, fk->confrelid, fk->conrelid,
20135 &insertTriggerOid, &updateTriggerOid);
20136 Assert(OidIsValid(insertTriggerOid));
20137 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
20138 RelationGetRelid(partRel));
20139 Assert(OidIsValid(updateTriggerOid));
20140 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
20141 RelationGetRelid(partRel));
20144 * Make the action triggers on the referenced relation. When this was
20145 * a partition the action triggers pointed to the parent rel (they
20146 * still do), but now we need separate ones of our own.
20148 fkconstraint = makeNode(Constraint);
20149 fkconstraint->contype = CONSTRAINT_FOREIGN;
20150 fkconstraint->conname = pstrdup(NameStr(conform->conname));
20151 fkconstraint->deferrable = conform->condeferrable;
20152 fkconstraint->initdeferred = conform->condeferred;
20153 fkconstraint->location = -1;
20154 fkconstraint->pktable = NULL;
20155 fkconstraint->fk_attrs = NIL;
20156 fkconstraint->pk_attrs = NIL;
20157 fkconstraint->fk_matchtype = conform->confmatchtype;
20158 fkconstraint->fk_upd_action = conform->confupdtype;
20159 fkconstraint->fk_del_action = conform->confdeltype;
20160 fkconstraint->fk_del_set_cols = NIL;
20161 fkconstraint->old_conpfeqop = NIL;
20162 fkconstraint->old_pktable_oid = InvalidOid;
20163 fkconstraint->skip_validation = false;
20164 fkconstraint->initially_valid = true;
20166 createForeignKeyActionTriggers(partRel, conform->confrelid,
20167 fkconstraint, fk->conoid,
20168 conform->conindid,
20169 InvalidOid, InvalidOid,
20170 NULL, NULL);
20172 ReleaseSysCache(contup);
20174 list_free_deep(fks);
20175 if (trigrel)
20176 table_close(trigrel, RowExclusiveLock);
20179 * Any sub-constraints that are in the referenced-side of a larger
20180 * constraint have to be removed. This partition is no longer part of the
20181 * key space of the constraint.
20183 foreach(cell, GetParentedForeignKeyRefs(partRel))
20185 Oid constrOid = lfirst_oid(cell);
20186 ObjectAddress constraint;
20188 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20189 deleteDependencyRecordsForClass(ConstraintRelationId,
20190 constrOid,
20191 ConstraintRelationId,
20192 DEPENDENCY_INTERNAL);
20193 CommandCounterIncrement();
20195 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
20196 performDeletion(&constraint, DROP_RESTRICT, 0);
20199 /* Now we can detach indexes */
20200 indexes = RelationGetIndexList(partRel);
20201 foreach(cell, indexes)
20203 Oid idxid = lfirst_oid(cell);
20204 Relation idx;
20205 Oid constrOid;
20207 if (!has_superclass(idxid))
20208 continue;
20210 Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
20211 RelationGetRelid(rel)));
20213 idx = index_open(idxid, AccessExclusiveLock);
20214 IndexSetParentIndex(idx, InvalidOid);
20216 /* If there's a constraint associated with the index, detach it too */
20217 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
20218 idxid);
20219 if (OidIsValid(constrOid))
20220 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
20222 index_close(idx, NoLock);
20225 /* Update pg_class tuple */
20226 classRel = table_open(RelationRelationId, RowExclusiveLock);
20227 tuple = SearchSysCacheCopy1(RELOID,
20228 ObjectIdGetDatum(RelationGetRelid(partRel)));
20229 if (!HeapTupleIsValid(tuple))
20230 elog(ERROR, "cache lookup failed for relation %u",
20231 RelationGetRelid(partRel));
20232 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
20234 /* Clear relpartbound and reset relispartition */
20235 memset(new_val, 0, sizeof(new_val));
20236 memset(new_null, false, sizeof(new_null));
20237 memset(new_repl, false, sizeof(new_repl));
20238 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
20239 new_null[Anum_pg_class_relpartbound - 1] = true;
20240 new_repl[Anum_pg_class_relpartbound - 1] = true;
20241 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
20242 new_val, new_null, new_repl);
20244 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
20245 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
20246 heap_freetuple(newtuple);
20247 table_close(classRel, RowExclusiveLock);
20250 * Drop identity property from all identity columns of partition.
20252 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
20254 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
20256 if (!attr->attisdropped && attr->attidentity)
20257 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
20258 AccessExclusiveLock, true, true);
20261 if (OidIsValid(defaultPartOid))
20264 * If the relation being detached is the default partition itself,
20265 * remove it from the parent's pg_partitioned_table entry.
20267 * If not, we must invalidate default partition's relcache entry, as
20268 * in StorePartitionBound: its partition constraint depends on every
20269 * other partition's partition constraint.
20271 if (RelationGetRelid(partRel) == defaultPartOid)
20272 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
20273 else
20274 CacheInvalidateRelcacheByRelid(defaultPartOid);
20278 * Invalidate the parent's relcache so that the partition is no longer
20279 * included in its partition descriptor.
20281 CacheInvalidateRelcache(rel);
20284 * If the partition we just detached is partitioned itself, invalidate
20285 * relcache for all descendent partitions too to ensure that their
20286 * rd_partcheck expression trees are rebuilt; must lock partitions before
20287 * doing so, using the same lockmode as what partRel has been locked with
20288 * by the caller.
20290 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20292 List *children;
20294 children = find_all_inheritors(RelationGetRelid(partRel),
20295 AccessExclusiveLock, NULL);
20296 foreach(cell, children)
20298 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
20304 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
20306 * To use when a DETACH PARTITION command previously did not run to
20307 * completion; this completes the detaching process.
20309 static ObjectAddress
20310 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
20312 Relation partRel;
20313 ObjectAddress address;
20314 Snapshot snap = GetActiveSnapshot();
20316 partRel = table_openrv(name, AccessExclusiveLock);
20319 * Wait until existing snapshots are gone. This is important if the
20320 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
20321 * user could immediately run DETACH FINALIZE without actually waiting for
20322 * existing transactions. We must not complete the detach action until
20323 * all such queries are complete (otherwise we would present them with an
20324 * inconsistent view of catalogs).
20326 WaitForOlderSnapshots(snap->xmin, false);
20328 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
20330 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
20332 table_close(partRel, NoLock);
20334 return address;
20338 * DetachAddConstraintIfNeeded
20339 * Subroutine for ATExecDetachPartition. Create a constraint that
20340 * takes the place of the partition constraint, but avoid creating
20341 * a dupe if a constraint already exists which implies the needed
20342 * constraint.
20344 static void
20345 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
20347 List *constraintExpr;
20349 constraintExpr = RelationGetPartitionQual(partRel);
20350 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
20353 * Avoid adding a new constraint if the needed constraint is implied by an
20354 * existing constraint
20356 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
20358 AlteredTableInfo *tab;
20359 Constraint *n;
20361 tab = ATGetQueueEntry(wqueue, partRel);
20363 /* Add constraint on partition, equivalent to the partition constraint */
20364 n = makeNode(Constraint);
20365 n->contype = CONSTR_CHECK;
20366 n->conname = NULL;
20367 n->location = -1;
20368 n->is_no_inherit = false;
20369 n->raw_expr = NULL;
20370 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
20371 n->initially_valid = true;
20372 n->skip_validation = true;
20373 /* It's a re-add, since it nominally already exists */
20374 ATAddCheckNNConstraint(wqueue, tab, partRel, n,
20375 true, false, true, ShareUpdateExclusiveLock);
20380 * DropClonedTriggersFromPartition
20381 * subroutine for ATExecDetachPartition to remove any triggers that were
20382 * cloned to the partition when it was created-as-partition or attached.
20383 * This undoes what CloneRowTriggersToPartition did.
20385 static void
20386 DropClonedTriggersFromPartition(Oid partitionId)
20388 ScanKeyData skey;
20389 SysScanDesc scan;
20390 HeapTuple trigtup;
20391 Relation tgrel;
20392 ObjectAddresses *objects;
20394 objects = new_object_addresses();
20397 * Scan pg_trigger to search for all triggers on this rel.
20399 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
20400 F_OIDEQ, ObjectIdGetDatum(partitionId));
20401 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
20402 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
20403 true, NULL, 1, &skey);
20404 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
20406 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
20407 ObjectAddress trig;
20409 /* Ignore triggers that weren't cloned */
20410 if (!OidIsValid(pg_trigger->tgparentid))
20411 continue;
20414 * Ignore internal triggers that are implementation objects of foreign
20415 * keys, because these will be detached when the foreign keys
20416 * themselves are.
20418 if (OidIsValid(pg_trigger->tgconstrrelid))
20419 continue;
20422 * This is ugly, but necessary: remove the dependency markings on the
20423 * trigger so that it can be removed.
20425 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20426 TriggerRelationId,
20427 DEPENDENCY_PARTITION_PRI);
20428 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
20429 RelationRelationId,
20430 DEPENDENCY_PARTITION_SEC);
20432 /* remember this trigger to remove it below */
20433 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
20434 add_exact_object_address(&trig, objects);
20437 /* make the dependency removal visible to the deletion below */
20438 CommandCounterIncrement();
20439 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
20441 /* done */
20442 free_object_addresses(objects);
20443 systable_endscan(scan);
20444 table_close(tgrel, RowExclusiveLock);
20448 * Before acquiring lock on an index, acquire the same lock on the owning
20449 * table.
20451 struct AttachIndexCallbackState
20453 Oid partitionOid;
20454 Oid parentTblOid;
20455 bool lockedParentTbl;
20458 static void
20459 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
20460 void *arg)
20462 struct AttachIndexCallbackState *state;
20463 Form_pg_class classform;
20464 HeapTuple tuple;
20466 state = (struct AttachIndexCallbackState *) arg;
20468 if (!state->lockedParentTbl)
20470 LockRelationOid(state->parentTblOid, AccessShareLock);
20471 state->lockedParentTbl = true;
20475 * If we previously locked some other heap, and the name we're looking up
20476 * no longer refers to an index on that relation, release the now-useless
20477 * lock. XXX maybe we should do *after* we verify whether the index does
20478 * not actually belong to the same relation ...
20480 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
20482 UnlockRelationOid(state->partitionOid, AccessShareLock);
20483 state->partitionOid = InvalidOid;
20486 /* Didn't find a relation, so no need for locking or permission checks. */
20487 if (!OidIsValid(relOid))
20488 return;
20490 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
20491 if (!HeapTupleIsValid(tuple))
20492 return; /* concurrently dropped, so nothing to do */
20493 classform = (Form_pg_class) GETSTRUCT(tuple);
20494 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
20495 classform->relkind != RELKIND_INDEX)
20496 ereport(ERROR,
20497 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20498 errmsg("\"%s\" is not an index", rv->relname)));
20499 ReleaseSysCache(tuple);
20502 * Since we need only examine the heap's tupledesc, an access share lock
20503 * on it (preventing any DDL) is sufficient.
20505 state->partitionOid = IndexGetRelation(relOid, false);
20506 LockRelationOid(state->partitionOid, AccessShareLock);
20510 * ALTER INDEX i1 ATTACH PARTITION i2
20512 static ObjectAddress
20513 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
20515 Relation partIdx;
20516 Relation partTbl;
20517 Relation parentTbl;
20518 ObjectAddress address;
20519 Oid partIdxId;
20520 Oid currParent;
20521 struct AttachIndexCallbackState state;
20524 * We need to obtain lock on the index 'name' to modify it, but we also
20525 * need to read its owning table's tuple descriptor -- so we need to lock
20526 * both. To avoid deadlocks, obtain lock on the table before doing so on
20527 * the index. Furthermore, we need to examine the parent table of the
20528 * partition, so lock that one too.
20530 state.partitionOid = InvalidOid;
20531 state.parentTblOid = parentIdx->rd_index->indrelid;
20532 state.lockedParentTbl = false;
20533 partIdxId =
20534 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
20535 RangeVarCallbackForAttachIndex,
20536 (void *) &state);
20537 /* Not there? */
20538 if (!OidIsValid(partIdxId))
20539 ereport(ERROR,
20540 (errcode(ERRCODE_UNDEFINED_OBJECT),
20541 errmsg("index \"%s\" does not exist", name->relname)));
20543 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
20544 partIdx = relation_open(partIdxId, AccessExclusiveLock);
20546 /* we already hold locks on both tables, so this is safe: */
20547 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
20548 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
20550 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
20552 /* Silently do nothing if already in the right state */
20553 currParent = partIdx->rd_rel->relispartition ?
20554 get_partition_parent(partIdxId, false) : InvalidOid;
20555 if (currParent != RelationGetRelid(parentIdx))
20557 IndexInfo *childInfo;
20558 IndexInfo *parentInfo;
20559 AttrMap *attmap;
20560 bool found;
20561 int i;
20562 PartitionDesc partDesc;
20563 Oid constraintOid,
20564 cldConstrId = InvalidOid;
20567 * If this partition already has an index attached, refuse the
20568 * operation.
20570 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
20572 if (OidIsValid(currParent))
20573 ereport(ERROR,
20574 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20575 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20576 RelationGetRelationName(partIdx),
20577 RelationGetRelationName(parentIdx)),
20578 errdetail("Index \"%s\" is already attached to another index.",
20579 RelationGetRelationName(partIdx))));
20581 /* Make sure it indexes a partition of the other index's table */
20582 partDesc = RelationGetPartitionDesc(parentTbl, true);
20583 found = false;
20584 for (i = 0; i < partDesc->nparts; i++)
20586 if (partDesc->oids[i] == state.partitionOid)
20588 found = true;
20589 break;
20592 if (!found)
20593 ereport(ERROR,
20594 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20595 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20596 RelationGetRelationName(partIdx),
20597 RelationGetRelationName(parentIdx)),
20598 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
20599 RelationGetRelationName(partIdx),
20600 RelationGetRelationName(parentTbl))));
20602 /* Ensure the indexes are compatible */
20603 childInfo = BuildIndexInfo(partIdx);
20604 parentInfo = BuildIndexInfo(parentIdx);
20605 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
20606 RelationGetDescr(parentTbl),
20607 false);
20608 if (!CompareIndexInfo(childInfo, parentInfo,
20609 partIdx->rd_indcollation,
20610 parentIdx->rd_indcollation,
20611 partIdx->rd_opfamily,
20612 parentIdx->rd_opfamily,
20613 attmap))
20614 ereport(ERROR,
20615 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20616 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20617 RelationGetRelationName(partIdx),
20618 RelationGetRelationName(parentIdx)),
20619 errdetail("The index definitions do not match.")));
20622 * If there is a constraint in the parent, make sure there is one in
20623 * the child too.
20625 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
20626 RelationGetRelid(parentIdx));
20628 if (OidIsValid(constraintOid))
20630 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
20631 partIdxId);
20632 if (!OidIsValid(cldConstrId))
20633 ereport(ERROR,
20634 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
20635 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20636 RelationGetRelationName(partIdx),
20637 RelationGetRelationName(parentIdx)),
20638 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
20639 RelationGetRelationName(parentIdx),
20640 RelationGetRelationName(parentTbl),
20641 RelationGetRelationName(partIdx))));
20645 * If it's a primary key, make sure the columns in the partition are
20646 * NOT NULL.
20648 if (parentIdx->rd_index->indisprimary)
20649 verifyPartitionIndexNotNull(childInfo, partTbl);
20651 /* All good -- do it */
20652 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
20653 if (OidIsValid(constraintOid))
20654 ConstraintSetParentConstraint(cldConstrId, constraintOid,
20655 RelationGetRelid(partTbl));
20657 free_attrmap(attmap);
20659 validatePartitionedIndex(parentIdx, parentTbl);
20662 relation_close(parentTbl, AccessShareLock);
20663 /* keep these locks till commit */
20664 relation_close(partTbl, NoLock);
20665 relation_close(partIdx, NoLock);
20667 return address;
20671 * Verify whether the given partition already contains an index attached
20672 * to the given partitioned index. If so, raise an error.
20674 static void
20675 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
20677 Oid existingIdx;
20679 existingIdx = index_get_partition(partitionTbl,
20680 RelationGetRelid(parentIdx));
20681 if (OidIsValid(existingIdx))
20682 ereport(ERROR,
20683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
20684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
20685 RelationGetRelationName(partIdx),
20686 RelationGetRelationName(parentIdx)),
20687 errdetail("Another index is already attached for partition \"%s\".",
20688 RelationGetRelationName(partitionTbl))));
20692 * Verify whether the set of attached partition indexes to a parent index on
20693 * a partitioned table is complete. If it is, mark the parent index valid.
20695 * This should be called each time a partition index is attached.
20697 static void
20698 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
20700 Relation inheritsRel;
20701 SysScanDesc scan;
20702 ScanKeyData key;
20703 int tuples = 0;
20704 HeapTuple inhTup;
20705 bool updated = false;
20707 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
20710 * Scan pg_inherits for this parent index. Count each valid index we find
20711 * (verifying the pg_index entry for each), and if we reach the total
20712 * amount we expect, we can mark this parent index as valid.
20714 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
20715 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
20716 BTEqualStrategyNumber, F_OIDEQ,
20717 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20718 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
20719 NULL, 1, &key);
20720 while ((inhTup = systable_getnext(scan)) != NULL)
20722 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
20723 HeapTuple indTup;
20724 Form_pg_index indexForm;
20726 indTup = SearchSysCache1(INDEXRELID,
20727 ObjectIdGetDatum(inhForm->inhrelid));
20728 if (!HeapTupleIsValid(indTup))
20729 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
20730 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20731 if (indexForm->indisvalid)
20732 tuples += 1;
20733 ReleaseSysCache(indTup);
20736 /* Done with pg_inherits */
20737 systable_endscan(scan);
20738 table_close(inheritsRel, AccessShareLock);
20741 * If we found as many inherited indexes as the partitioned table has
20742 * partitions, we're good; update pg_index to set indisvalid.
20744 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
20746 Relation idxRel;
20747 HeapTuple indTup;
20748 Form_pg_index indexForm;
20750 idxRel = table_open(IndexRelationId, RowExclusiveLock);
20751 indTup = SearchSysCacheCopy1(INDEXRELID,
20752 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
20753 if (!HeapTupleIsValid(indTup))
20754 elog(ERROR, "cache lookup failed for index %u",
20755 RelationGetRelid(partedIdx));
20756 indexForm = (Form_pg_index) GETSTRUCT(indTup);
20758 indexForm->indisvalid = true;
20759 updated = true;
20761 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
20763 table_close(idxRel, RowExclusiveLock);
20764 heap_freetuple(indTup);
20768 * If this index is in turn a partition of a larger index, validating it
20769 * might cause the parent to become valid also. Try that.
20771 if (updated && partedIdx->rd_rel->relispartition)
20773 Oid parentIdxId,
20774 parentTblId;
20775 Relation parentIdx,
20776 parentTbl;
20778 /* make sure we see the validation we just did */
20779 CommandCounterIncrement();
20781 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
20782 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
20783 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
20784 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
20785 Assert(!parentIdx->rd_index->indisvalid);
20787 validatePartitionedIndex(parentIdx, parentTbl);
20789 relation_close(parentIdx, AccessExclusiveLock);
20790 relation_close(parentTbl, AccessExclusiveLock);
20795 * When attaching an index as a partition of a partitioned index which is a
20796 * primary key, verify that all the columns in the partition are marked NOT
20797 * NULL.
20799 static void
20800 verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
20802 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
20804 Form_pg_attribute att = TupleDescAttr(RelationGetDescr(partition),
20805 iinfo->ii_IndexAttrNumbers[i] - 1);
20807 if (!att->attnotnull)
20808 ereport(ERROR,
20809 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
20810 errmsg("invalid primary key definition"),
20811 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
20812 NameStr(att->attname),
20813 RelationGetRelationName(partition)));
20818 * Return an OID list of constraints that reference the given relation
20819 * that are marked as having a parent constraints.
20821 static List *
20822 GetParentedForeignKeyRefs(Relation partition)
20824 Relation pg_constraint;
20825 HeapTuple tuple;
20826 SysScanDesc scan;
20827 ScanKeyData key[2];
20828 List *constraints = NIL;
20831 * If no indexes, or no columns are referenceable by FKs, we can avoid the
20832 * scan.
20834 if (RelationGetIndexList(partition) == NIL ||
20835 bms_is_empty(RelationGetIndexAttrBitmap(partition,
20836 INDEX_ATTR_BITMAP_KEY)))
20837 return NIL;
20839 /* Search for constraints referencing this table */
20840 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
20841 ScanKeyInit(&key[0],
20842 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
20843 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
20844 ScanKeyInit(&key[1],
20845 Anum_pg_constraint_contype, BTEqualStrategyNumber,
20846 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
20848 /* XXX This is a seqscan, as we don't have a usable index */
20849 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
20850 while ((tuple = systable_getnext(scan)) != NULL)
20852 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20855 * We only need to process constraints that are part of larger ones.
20857 if (!OidIsValid(constrForm->conparentid))
20858 continue;
20860 constraints = lappend_oid(constraints, constrForm->oid);
20863 systable_endscan(scan);
20864 table_close(pg_constraint, AccessShareLock);
20866 return constraints;
20870 * During DETACH PARTITION, verify that any foreign keys pointing to the
20871 * partitioned table would not become invalid. An error is raised if any
20872 * referenced values exist.
20874 static void
20875 ATDetachCheckNoForeignKeyRefs(Relation partition)
20877 List *constraints;
20878 ListCell *cell;
20880 constraints = GetParentedForeignKeyRefs(partition);
20882 foreach(cell, constraints)
20884 Oid constrOid = lfirst_oid(cell);
20885 HeapTuple tuple;
20886 Form_pg_constraint constrForm;
20887 Relation rel;
20888 Trigger trig = {0};
20890 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
20891 if (!HeapTupleIsValid(tuple))
20892 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
20893 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
20895 Assert(OidIsValid(constrForm->conparentid));
20896 Assert(constrForm->confrelid == RelationGetRelid(partition));
20898 /* prevent data changes into the referencing table until commit */
20899 rel = table_open(constrForm->conrelid, ShareLock);
20901 trig.tgoid = InvalidOid;
20902 trig.tgname = NameStr(constrForm->conname);
20903 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
20904 trig.tgisinternal = true;
20905 trig.tgconstrrelid = RelationGetRelid(partition);
20906 trig.tgconstrindid = constrForm->conindid;
20907 trig.tgconstraint = constrForm->oid;
20908 trig.tgdeferrable = false;
20909 trig.tginitdeferred = false;
20910 /* we needn't fill in remaining fields */
20912 RI_PartitionRemove_Check(&trig, rel, partition);
20914 ReleaseSysCache(tuple);
20916 table_close(rel, NoLock);
20921 * resolve column compression specification to compression method.
20923 static char
20924 GetAttributeCompression(Oid atttypid, const char *compression)
20926 char cmethod;
20928 if (compression == NULL || strcmp(compression, "default") == 0)
20929 return InvalidCompressionMethod;
20932 * To specify a nondefault method, the column data type must be toastable.
20933 * Note this says nothing about whether the column's attstorage setting
20934 * permits compression; we intentionally allow attstorage and
20935 * attcompression to be independent. But with a non-toastable type,
20936 * attstorage could not be set to a value that would permit compression.
20938 * We don't actually need to enforce this, since nothing bad would happen
20939 * if attcompression were non-default; it would never be consulted. But
20940 * it seems more user-friendly to complain about a certainly-useless
20941 * attempt to set the property.
20943 if (!TypeIsToastable(atttypid))
20944 ereport(ERROR,
20945 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20946 errmsg("column data type %s does not support compression",
20947 format_type_be(atttypid))));
20949 cmethod = CompressionNameToMethod(compression);
20950 if (!CompressionMethodIsValid(cmethod))
20951 ereport(ERROR,
20952 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20953 errmsg("invalid compression method \"%s\"", compression)));
20955 return cmethod;
20959 * resolve column storage specification
20961 static char
20962 GetAttributeStorage(Oid atttypid, const char *storagemode)
20964 char cstorage = 0;
20966 if (pg_strcasecmp(storagemode, "plain") == 0)
20967 cstorage = TYPSTORAGE_PLAIN;
20968 else if (pg_strcasecmp(storagemode, "external") == 0)
20969 cstorage = TYPSTORAGE_EXTERNAL;
20970 else if (pg_strcasecmp(storagemode, "extended") == 0)
20971 cstorage = TYPSTORAGE_EXTENDED;
20972 else if (pg_strcasecmp(storagemode, "main") == 0)
20973 cstorage = TYPSTORAGE_MAIN;
20974 else if (pg_strcasecmp(storagemode, "default") == 0)
20975 cstorage = get_typstorage(atttypid);
20976 else
20977 ereport(ERROR,
20978 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20979 errmsg("invalid storage type \"%s\"",
20980 storagemode)));
20983 * safety check: do not allow toasted storage modes unless column datatype
20984 * is TOAST-aware.
20986 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20987 ereport(ERROR,
20988 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20989 errmsg("column data type %s can only have storage PLAIN",
20990 format_type_be(atttypid))));
20992 return cstorage;
20996 * Struct with context of new partition for inserting rows from split partition
20998 typedef struct SplitPartitionContext
21000 ExprState *partqualstate; /* expression for checking slot for partition
21001 * (NULL for DEFAULT partition) */
21002 BulkInsertState bistate; /* state of bulk inserts for partition */
21003 TupleTableSlot *dstslot; /* slot for inserting row into partition */
21004 Relation partRel; /* relation for partition */
21005 } SplitPartitionContext;
21009 * createSplitPartitionContext: create context for partition and fill it
21011 static SplitPartitionContext *
21012 createSplitPartitionContext(Relation partRel)
21014 SplitPartitionContext *pc;
21016 pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
21017 pc->partRel = partRel;
21020 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
21021 * don't bother using it.
21023 pc->bistate = GetBulkInsertState();
21025 /* Create tuple slot for new partition. */
21026 pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
21027 table_slot_callbacks(pc->partRel));
21028 ExecStoreAllNullTuple(pc->dstslot);
21030 return pc;
21034 * deleteSplitPartitionContext: delete context for partition
21036 static void
21037 deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
21039 ExecDropSingleTupleTableSlot(pc->dstslot);
21040 FreeBulkInsertState(pc->bistate);
21042 table_finish_bulk_insert(pc->partRel, ti_options);
21044 pfree(pc);
21048 * moveSplitTableRows: scan split partition (splitRel) of partitioned table
21049 * (rel) and move rows into new partitions.
21051 * New partitions description:
21052 * partlist: list of pointers to SinglePartitionSpec structures.
21053 * newPartRels: list of Relations.
21054 * defaultPartOid: oid of DEFAULT partition, for table rel.
21056 static void
21057 moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
21059 /* The FSM is empty, so don't bother using it. */
21060 int ti_options = TABLE_INSERT_SKIP_FSM;
21061 CommandId mycid;
21062 EState *estate;
21063 ListCell *listptr,
21064 *listptr2;
21065 TupleTableSlot *srcslot;
21066 ExprContext *econtext;
21067 TableScanDesc scan;
21068 Snapshot snapshot;
21069 MemoryContext oldCxt;
21070 List *partContexts = NIL;
21071 TupleConversionMap *tuple_map;
21072 SplitPartitionContext *defaultPartCtx = NULL,
21073 *pc;
21074 bool isOldDefaultPart = false;
21076 mycid = GetCurrentCommandId(true);
21078 estate = CreateExecutorState();
21080 forboth(listptr, partlist, listptr2, newPartRels)
21082 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21084 pc = createSplitPartitionContext((Relation) lfirst(listptr2));
21086 if (sps->bound->is_default)
21088 /* We should not create constraint for detached DEFAULT partition. */
21089 defaultPartCtx = pc;
21091 else
21093 List *partConstraint;
21095 /* Build expression execution states for partition check quals. */
21096 partConstraint = get_qual_from_partbound(rel, sps->bound);
21097 partConstraint =
21098 (List *) eval_const_expressions(NULL,
21099 (Node *) partConstraint);
21100 /* Make boolean expression for ExecCheck(). */
21101 partConstraint = list_make1(make_ands_explicit(partConstraint));
21104 * Map the vars in the constraint expression from rel's attnos to
21105 * splitRel's.
21107 partConstraint = map_partition_varattnos(partConstraint,
21108 1, splitRel, rel);
21110 pc->partqualstate =
21111 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
21112 Assert(pc->partqualstate != NULL);
21115 /* Store partition context into list. */
21116 partContexts = lappend(partContexts, pc);
21120 * Create partition context for DEFAULT partition. We can insert values
21121 * into this partition in case spaces with values between new partitions.
21123 if (!defaultPartCtx && OidIsValid(defaultPartOid))
21125 /* Indicate that we allocate context for old DEFAULT partition */
21126 isOldDefaultPart = true;
21127 defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
21130 econtext = GetPerTupleExprContext(estate);
21132 /* Create necessary tuple slot. */
21133 srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
21134 table_slot_callbacks(splitRel));
21137 * Map computing for moving attributes of split partition to new partition
21138 * (for first new partition, but other new partitions can use the same
21139 * map).
21141 pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
21142 tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
21143 RelationGetDescr(pc->partRel));
21145 /* Scan through the rows. */
21146 snapshot = RegisterSnapshot(GetLatestSnapshot());
21147 scan = table_beginscan(splitRel, snapshot, 0, NULL);
21150 * Switch to per-tuple memory context and reset it for each tuple
21151 * produced, so we don't leak memory.
21153 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
21155 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21157 bool found = false;
21158 TupleTableSlot *insertslot;
21160 /* Extract data from old tuple. */
21161 slot_getallattrs(srcslot);
21163 econtext->ecxt_scantuple = srcslot;
21165 /* Search partition for current slot srcslot. */
21166 foreach(listptr, partContexts)
21168 pc = (SplitPartitionContext *) lfirst(listptr);
21170 if (pc->partqualstate /* skip DEFAULT partition */ &&
21171 ExecCheck(pc->partqualstate, econtext))
21173 found = true;
21174 break;
21176 ResetExprContext(econtext);
21178 if (!found)
21180 /* Use DEFAULT partition if it exists. */
21181 if (defaultPartCtx)
21182 pc = defaultPartCtx;
21183 else
21184 ereport(ERROR,
21185 (errcode(ERRCODE_CHECK_VIOLATION),
21186 errmsg("can not find partition for split partition row"),
21187 errtable(splitRel)));
21190 if (tuple_map)
21192 /* Need to use map to copy attributes. */
21193 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
21195 else
21197 /* Copy attributes directly. */
21198 insertslot = pc->dstslot;
21200 ExecClearTuple(insertslot);
21202 memcpy(insertslot->tts_values, srcslot->tts_values,
21203 sizeof(Datum) * srcslot->tts_nvalid);
21204 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21205 sizeof(bool) * srcslot->tts_nvalid);
21207 ExecStoreVirtualTuple(insertslot);
21210 /* Write the tuple out to the new relation. */
21211 table_tuple_insert(pc->partRel, insertslot, mycid,
21212 ti_options, pc->bistate);
21214 ResetExprContext(econtext);
21216 CHECK_FOR_INTERRUPTS();
21219 MemoryContextSwitchTo(oldCxt);
21221 table_endscan(scan);
21222 UnregisterSnapshot(snapshot);
21224 if (tuple_map)
21225 free_conversion_map(tuple_map);
21227 ExecDropSingleTupleTableSlot(srcslot);
21229 FreeExecutorState(estate);
21231 foreach(listptr, partContexts)
21232 deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
21234 /* Need to close table and free buffers for DEFAULT partition. */
21235 if (isOldDefaultPart)
21237 Relation defaultPartRel = defaultPartCtx->partRel;
21239 deleteSplitPartitionContext(defaultPartCtx, ti_options);
21240 /* Keep the lock until commit. */
21241 table_close(defaultPartRel, NoLock);
21246 * createPartitionTable: create table for a new partition with given name
21247 * (newPartName) like table (modelRel)
21249 * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
21250 * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY)
21252 * Also, this function sets the new partition access method same as parent
21253 * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
21254 * checks that parent and child tables have compatible persistence.
21256 * Function returns the created relation (locked in AccessExclusiveLock mode).
21258 static Relation
21259 createPartitionTable(RangeVar *newPartName, Relation modelRel,
21260 AlterTableUtilityContext *context)
21262 CreateStmt *createStmt;
21263 TableLikeClause *tlc;
21264 PlannedStmt *wrapper;
21265 Relation newRel;
21267 /* If existing rel is temp, it must belong to this session */
21268 if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
21269 !modelRel->rd_islocaltemp)
21270 ereport(ERROR,
21271 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21272 errmsg("cannot create as partition of temporary relation of another session")));
21274 /* New partition should have the same persistence as modelRel */
21275 newPartName->relpersistence = modelRel->rd_rel->relpersistence;
21277 createStmt = makeNode(CreateStmt);
21278 createStmt->relation = newPartName;
21279 createStmt->tableElts = NIL;
21280 createStmt->inhRelations = NIL;
21281 createStmt->constraints = NIL;
21282 createStmt->options = NIL;
21283 createStmt->oncommit = ONCOMMIT_NOOP;
21284 createStmt->tablespacename = NULL;
21285 createStmt->if_not_exists = false;
21286 createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
21288 tlc = makeNode(TableLikeClause);
21289 tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
21290 RelationGetRelationName(modelRel), -1);
21293 * Indexes will be inherited on "attach new partitions" stage, after data
21294 * moving.
21296 tlc->options = CREATE_TABLE_LIKE_ALL & ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY);
21297 tlc->relationOid = InvalidOid;
21298 createStmt->tableElts = lappend(createStmt->tableElts, tlc);
21300 /* Need to make a wrapper PlannedStmt. */
21301 wrapper = makeNode(PlannedStmt);
21302 wrapper->commandType = CMD_UTILITY;
21303 wrapper->canSetTag = false;
21304 wrapper->utilityStmt = (Node *) createStmt;
21305 wrapper->stmt_location = context->pstmt->stmt_location;
21306 wrapper->stmt_len = context->pstmt->stmt_len;
21308 ProcessUtility(wrapper,
21309 context->queryString,
21310 false,
21311 PROCESS_UTILITY_SUBCOMMAND,
21312 NULL,
21313 NULL,
21314 None_Receiver,
21315 NULL);
21318 * Open the new partition with no lock, because we already have
21319 * AccessExclusiveLock placed there after creation.
21321 newRel = table_openrv(newPartName, NoLock);
21324 * We intended to create the partition with the same persistence as the
21325 * parent table, but we still need to recheck because that might be
21326 * affected by the search_path. If the parent is permanent, so must be
21327 * all of its partitions.
21329 if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
21330 newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
21331 ereport(ERROR,
21332 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21333 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
21334 RelationGetRelationName(modelRel))));
21336 /* Permanent rels cannot be partitions belonging to temporary parent */
21337 if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
21338 modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
21339 ereport(ERROR,
21340 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
21341 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
21342 RelationGetRelationName(modelRel))));
21344 return newRel;
21348 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
21350 static void
21351 ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
21352 PartitionCmd *cmd, AlterTableUtilityContext *context)
21354 Relation splitRel;
21355 Oid splitRelOid;
21356 char relname[NAMEDATALEN];
21357 Oid namespaceId;
21358 ListCell *listptr,
21359 *listptr2;
21360 bool isSameName = false;
21361 char tmpRelName[NAMEDATALEN];
21362 List *newPartRels = NIL;
21363 ObjectAddress object;
21364 Oid defaultPartOid;
21366 defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21369 * We are going to detach and remove this partition: need to use exclusive
21370 * lock for preventing DML-queries to the partition.
21372 splitRel = table_openrv(cmd->name, AccessExclusiveLock);
21374 splitRelOid = RelationGetRelid(splitRel);
21376 /* Check descriptions of new partitions. */
21377 foreach(listptr, cmd->partlist)
21379 Oid existing_relid;
21380 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21382 strlcpy(relname, sps->name->relname, NAMEDATALEN);
21385 * Look up the namespace in which we are supposed to create the
21386 * partition, check we have permission to create there, lock it
21387 * against concurrent drop, and mark stmt->relation as
21388 * RELPERSISTENCE_TEMP if a temporary namespace is selected.
21390 namespaceId =
21391 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
21394 * This would fail later on anyway if the relation already exists. But
21395 * by catching it here we can emit a nicer error message.
21397 existing_relid = get_relname_relid(relname, namespaceId);
21398 if (existing_relid == splitRelOid && !isSameName)
21399 /* One new partition can have the same name as split partition. */
21400 isSameName = true;
21401 else if (existing_relid != InvalidOid)
21402 ereport(ERROR,
21403 (errcode(ERRCODE_DUPLICATE_TABLE),
21404 errmsg("relation \"%s\" already exists", relname)));
21407 /* Detach split partition. */
21408 RemoveInheritance(splitRel, rel, false);
21409 /* Do the final part of detaching. */
21410 DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
21413 * If new partition has the same name as split partition then we should
21414 * rename split partition for reusing name.
21416 if (isSameName)
21419 * We must bump the command counter to make the split partition tuple
21420 * visible for renaming.
21422 CommandCounterIncrement();
21423 /* Rename partition. */
21424 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21425 RenameRelationInternal(splitRelOid, tmpRelName, false, false);
21428 * We must bump the command counter to make the split partition tuple
21429 * visible after renaming.
21431 CommandCounterIncrement();
21434 /* Create new partitions (like split partition), without indexes. */
21435 foreach(listptr, cmd->partlist)
21437 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21438 Relation newPartRel;
21440 newPartRel = createPartitionTable(sps->name, rel, context);
21441 newPartRels = lappend(newPartRels, newPartRel);
21444 /* Copy data from split partition to new partitions. */
21445 moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
21446 /* Keep the lock until commit. */
21447 table_close(splitRel, NoLock);
21449 /* Attach new partitions to partitioned table. */
21450 forboth(listptr, cmd->partlist, listptr2, newPartRels)
21452 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
21453 Relation newPartRel = (Relation) lfirst(listptr2);
21456 * wqueue = NULL: verification for each cloned constraint is not
21457 * needed.
21459 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
21460 /* Keep the lock until commit. */
21461 table_close(newPartRel, NoLock);
21464 /* Drop split partition. */
21465 object.classId = RelationRelationId;
21466 object.objectId = splitRelOid;
21467 object.objectSubId = 0;
21468 /* Probably DROP_CASCADE is not needed. */
21469 performDeletion(&object, DROP_RESTRICT, 0);
21473 * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
21474 * of the partitioned table (rel) and move rows into the new partition
21475 * (newPartRel).
21477 static void
21478 moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
21479 Relation newPartRel)
21481 CommandId mycid;
21483 /* The FSM is empty, so don't bother using it. */
21484 int ti_options = TABLE_INSERT_SKIP_FSM;
21485 ListCell *listptr;
21486 BulkInsertState bistate; /* state of bulk inserts for partition */
21487 TupleTableSlot *dstslot;
21489 mycid = GetCurrentCommandId(true);
21491 /* Prepare a BulkInsertState for table_tuple_insert. */
21492 bistate = GetBulkInsertState();
21494 /* Create necessary tuple slot. */
21495 dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
21496 table_slot_callbacks(newPartRel));
21497 ExecStoreAllNullTuple(dstslot);
21499 foreach(listptr, mergingPartitionsList)
21501 Relation mergingPartition = (Relation) lfirst(listptr);
21502 TupleTableSlot *srcslot;
21503 TupleConversionMap *tuple_map;
21504 TableScanDesc scan;
21505 Snapshot snapshot;
21507 /* Create tuple slot for new partition. */
21508 srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
21509 table_slot_callbacks(mergingPartition));
21512 * Map computing for moving attributes of merged partition to new
21513 * partition.
21515 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
21516 RelationGetDescr(newPartRel));
21518 /* Scan through the rows. */
21519 snapshot = RegisterSnapshot(GetLatestSnapshot());
21520 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
21522 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
21524 TupleTableSlot *insertslot;
21526 /* Extract data from old tuple. */
21527 slot_getallattrs(srcslot);
21529 if (tuple_map)
21531 /* Need to use map to copy attributes. */
21532 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
21534 else
21536 /* Copy attributes directly. */
21537 insertslot = dstslot;
21539 ExecClearTuple(insertslot);
21541 memcpy(insertslot->tts_values, srcslot->tts_values,
21542 sizeof(Datum) * srcslot->tts_nvalid);
21543 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
21544 sizeof(bool) * srcslot->tts_nvalid);
21546 ExecStoreVirtualTuple(insertslot);
21549 /* Write the tuple out to the new relation. */
21550 table_tuple_insert(newPartRel, insertslot, mycid,
21551 ti_options, bistate);
21553 CHECK_FOR_INTERRUPTS();
21556 table_endscan(scan);
21557 UnregisterSnapshot(snapshot);
21559 if (tuple_map)
21560 free_conversion_map(tuple_map);
21562 ExecDropSingleTupleTableSlot(srcslot);
21565 ExecDropSingleTupleTableSlot(dstslot);
21566 FreeBulkInsertState(bistate);
21568 table_finish_bulk_insert(newPartRel, ti_options);
21572 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
21574 static void
21575 ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
21576 PartitionCmd *cmd, AlterTableUtilityContext *context)
21578 Relation newPartRel;
21579 ListCell *listptr;
21580 List *mergingPartitionsList = NIL;
21581 Oid defaultPartOid;
21584 * Lock all merged partitions, check them and create list with partitions
21585 * contexts.
21587 foreach(listptr, cmd->partlist)
21589 RangeVar *name = (RangeVar *) lfirst(listptr);
21590 Relation mergingPartition;
21593 * We are going to detach and remove this partition: need to use
21594 * exclusive lock for preventing DML-queries to the partition.
21596 mergingPartition = table_openrv(name, AccessExclusiveLock);
21599 * Checking that two partitions have the same name was before, in
21600 * function transformPartitionCmdForMerge().
21602 if (equal(name, cmd->name))
21604 /* One new partition can have the same name as merged partition. */
21605 char tmpRelName[NAMEDATALEN];
21607 /* Generate temporary name. */
21608 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
21611 * Rename the existing partition with a temporary name, leaving it
21612 * free for the new partition. We don't need to care about this
21613 * in the future because we're going to eventually drop the
21614 * existing partition anyway.
21616 RenameRelationInternal(RelationGetRelid(mergingPartition),
21617 tmpRelName, false, false);
21620 * We must bump the command counter to make the new partition
21621 * tuple visible for rename.
21623 CommandCounterIncrement();
21626 /* Store a next merging partition into the list. */
21627 mergingPartitionsList = lappend(mergingPartitionsList,
21628 mergingPartition);
21631 /* Detach all merged partitions. */
21632 defaultPartOid =
21633 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
21634 foreach(listptr, mergingPartitionsList)
21636 Relation mergingPartition = (Relation) lfirst(listptr);
21638 /* Remove the pg_inherits row first. */
21639 RemoveInheritance(mergingPartition, rel, false);
21640 /* Do the final part of detaching. */
21641 DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
21644 /* Create table for new partition, use partitioned table as model. */
21645 newPartRel = createPartitionTable(cmd->name, rel, context);
21647 /* Copy data from merged partitions to new partition. */
21648 moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
21650 /* Drop the current partitions before attaching the new one. */
21651 foreach(listptr, mergingPartitionsList)
21653 ObjectAddress object;
21654 Relation mergingPartition = (Relation) lfirst(listptr);
21656 /* Get relation id before table_close() call. */
21657 object.objectId = RelationGetRelid(mergingPartition);
21658 object.classId = RelationRelationId;
21659 object.objectSubId = 0;
21661 /* Keep the lock until commit. */
21662 table_close(mergingPartition, NoLock);
21664 performDeletion(&object, DROP_RESTRICT, 0);
21666 list_free(mergingPartitionsList);
21669 * Attach a new partition to the partitioned table. wqueue = NULL:
21670 * verification for each cloned constraint is not needed.
21672 attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
21674 /* Keep the lock until commit. */
21675 table_close(newPartRel, NoLock);