Add OAT hook calls for more subcommands of ALTER TABLE
[pgsql.git] / src / backend / commands / tablecmds.c
blobf77de4e7c99a20aba5f5caeaceadab18dc16740e
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2023, 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/heapam.h"
20 #include "access/heapam_xlog.h"
21 #include "access/multixact.h"
22 #include "access/reloptions.h"
23 #include "access/relscan.h"
24 #include "access/sysattr.h"
25 #include "access/tableam.h"
26 #include "access/toast_compression.h"
27 #include "access/xact.h"
28 #include "access/xlog.h"
29 #include "access/xloginsert.h"
30 #include "catalog/catalog.h"
31 #include "catalog/heap.h"
32 #include "catalog/index.h"
33 #include "catalog/namespace.h"
34 #include "catalog/objectaccess.h"
35 #include "catalog/partition.h"
36 #include "catalog/pg_am.h"
37 #include "catalog/pg_attrdef.h"
38 #include "catalog/pg_collation.h"
39 #include "catalog/pg_constraint.h"
40 #include "catalog/pg_depend.h"
41 #include "catalog/pg_foreign_table.h"
42 #include "catalog/pg_inherits.h"
43 #include "catalog/pg_largeobject.h"
44 #include "catalog/pg_namespace.h"
45 #include "catalog/pg_opclass.h"
46 #include "catalog/pg_statistic_ext.h"
47 #include "catalog/pg_tablespace.h"
48 #include "catalog/pg_trigger.h"
49 #include "catalog/pg_type.h"
50 #include "catalog/storage.h"
51 #include "catalog/storage_xlog.h"
52 #include "catalog/toasting.h"
53 #include "commands/cluster.h"
54 #include "commands/comment.h"
55 #include "commands/defrem.h"
56 #include "commands/event_trigger.h"
57 #include "commands/policy.h"
58 #include "commands/sequence.h"
59 #include "commands/tablecmds.h"
60 #include "commands/tablespace.h"
61 #include "commands/trigger.h"
62 #include "commands/typecmds.h"
63 #include "commands/user.h"
64 #include "commands/vacuum.h"
65 #include "executor/executor.h"
66 #include "foreign/fdwapi.h"
67 #include "foreign/foreign.h"
68 #include "miscadmin.h"
69 #include "nodes/makefuncs.h"
70 #include "nodes/nodeFuncs.h"
71 #include "nodes/parsenodes.h"
72 #include "optimizer/optimizer.h"
73 #include "parser/parse_clause.h"
74 #include "parser/parse_coerce.h"
75 #include "parser/parse_collate.h"
76 #include "parser/parse_expr.h"
77 #include "parser/parse_oper.h"
78 #include "parser/parse_relation.h"
79 #include "parser/parse_type.h"
80 #include "parser/parse_utilcmd.h"
81 #include "parser/parser.h"
82 #include "partitioning/partbounds.h"
83 #include "partitioning/partdesc.h"
84 #include "pgstat.h"
85 #include "rewrite/rewriteDefine.h"
86 #include "rewrite/rewriteHandler.h"
87 #include "rewrite/rewriteManip.h"
88 #include "storage/bufmgr.h"
89 #include "storage/lmgr.h"
90 #include "storage/lock.h"
91 #include "storage/predicate.h"
92 #include "storage/smgr.h"
93 #include "tcop/utility.h"
94 #include "utils/acl.h"
95 #include "utils/builtins.h"
96 #include "utils/fmgroids.h"
97 #include "utils/inval.h"
98 #include "utils/lsyscache.h"
99 #include "utils/memutils.h"
100 #include "utils/partcache.h"
101 #include "utils/relcache.h"
102 #include "utils/ruleutils.h"
103 #include "utils/snapmgr.h"
104 #include "utils/syscache.h"
105 #include "utils/timestamp.h"
106 #include "utils/typcache.h"
107 #include "utils/usercontext.h"
110 * ON COMMIT action list
112 typedef struct OnCommitItem
114 Oid relid; /* relid of relation */
115 OnCommitAction oncommit; /* what to do at end of xact */
118 * If this entry was created during the current transaction,
119 * creating_subid is the ID of the creating subxact; if created in a prior
120 * transaction, creating_subid is zero. If deleted during the current
121 * transaction, deleting_subid is the ID of the deleting subxact; if no
122 * deletion request is pending, deleting_subid is zero.
124 SubTransactionId creating_subid;
125 SubTransactionId deleting_subid;
126 } OnCommitItem;
128 static List *on_commits = NIL;
132 * State information for ALTER TABLE
134 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
135 * structs, one for each table modified by the operation (the named table
136 * plus any child tables that are affected). We save lists of subcommands
137 * to apply to this table (possibly modified by parse transformation steps);
138 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
139 * necessary information is stored in the constraints and newvals lists.
141 * Phase 2 is divided into multiple passes; subcommands are executed in
142 * a pass determined by subcommand type.
145 #define AT_PASS_UNSET -1 /* UNSET will cause ERROR */
146 #define AT_PASS_DROP 0 /* DROP (all flavors) */
147 #define AT_PASS_ALTER_TYPE 1 /* ALTER COLUMN TYPE */
148 #define AT_PASS_OLD_INDEX 2 /* re-add existing indexes */
149 #define AT_PASS_OLD_CONSTR 3 /* re-add existing constraints */
150 /* We could support a RENAME COLUMN pass here, but not currently used */
151 #define AT_PASS_ADD_COL 4 /* ADD COLUMN */
152 #define AT_PASS_ADD_CONSTR 5 /* ADD constraints (initial examination) */
153 #define AT_PASS_COL_ATTRS 6 /* set column attributes, eg NOT NULL */
154 #define AT_PASS_ADD_INDEXCONSTR 7 /* ADD index-based constraints */
155 #define AT_PASS_ADD_INDEX 8 /* ADD indexes */
156 #define AT_PASS_ADD_OTHERCONSTR 9 /* ADD other constraints, defaults */
157 #define AT_PASS_MISC 10 /* other stuff */
158 #define AT_NUM_PASSES 11
160 typedef struct AlteredTableInfo
162 /* Information saved before any work commences: */
163 Oid relid; /* Relation to work on */
164 char relkind; /* Its relkind */
165 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
168 * Transiently set during Phase 2, normally set to NULL.
170 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
171 * returns control. This can be exploited by ATExecCmd subroutines to
172 * close/reopen across transaction boundaries.
174 Relation rel;
176 /* Information saved by Phase 1 for Phase 2: */
177 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
178 /* Information saved by Phases 1/2 for Phase 3: */
179 List *constraints; /* List of NewConstraint */
180 List *newvals; /* List of NewColumnValue */
181 List *afterStmts; /* List of utility command parsetrees */
182 bool verify_new_notnull; /* T if we should recheck NOT NULL */
183 int rewrite; /* Reason for forced rewrite, if any */
184 Oid newAccessMethod; /* new access method; 0 means no change */
185 Oid newTableSpace; /* new tablespace; 0 means no change */
186 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
187 char newrelpersistence; /* if above is true */
188 Expr *partition_constraint; /* for attach partition validation */
189 /* true, if validating default due to some other attach/detach */
190 bool validate_default;
191 /* Objects to rebuild after completing ALTER TYPE operations */
192 List *changedConstraintOids; /* OIDs of constraints to rebuild */
193 List *changedConstraintDefs; /* string definitions of same */
194 List *changedIndexOids; /* OIDs of indexes to rebuild */
195 List *changedIndexDefs; /* string definitions of same */
196 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
197 char *clusterOnIndex; /* index to use for CLUSTER */
198 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
199 List *changedStatisticsDefs; /* string definitions of same */
200 } AlteredTableInfo;
202 /* Struct describing one new constraint to check in Phase 3 scan */
203 /* Note: new NOT NULL constraints are handled elsewhere */
204 typedef struct NewConstraint
206 char *name; /* Constraint name, or NULL if none */
207 ConstrType contype; /* CHECK or FOREIGN */
208 Oid refrelid; /* PK rel, if FOREIGN */
209 Oid refindid; /* OID of PK's index, if FOREIGN */
210 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
211 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
212 ExprState *qualstate; /* Execution state for CHECK expr */
213 } NewConstraint;
216 * Struct describing one new column value that needs to be computed during
217 * Phase 3 copy (this could be either a new column with a non-null default, or
218 * a column that we're changing the type of). Columns without such an entry
219 * are just copied from the old table during ATRewriteTable. Note that the
220 * expr is an expression over *old* table values, except when is_generated
221 * is true; then it is an expression over columns of the *new* tuple.
223 typedef struct NewColumnValue
225 AttrNumber attnum; /* which column */
226 Expr *expr; /* expression to compute */
227 ExprState *exprstate; /* execution state */
228 bool is_generated; /* is it a GENERATED expression? */
229 } NewColumnValue;
232 * Error-reporting support for RemoveRelations
234 struct dropmsgstrings
236 char kind;
237 int nonexistent_code;
238 const char *nonexistent_msg;
239 const char *skipping_msg;
240 const char *nota_msg;
241 const char *drophint_msg;
244 static const struct dropmsgstrings dropmsgstringarray[] = {
245 {RELKIND_RELATION,
246 ERRCODE_UNDEFINED_TABLE,
247 gettext_noop("table \"%s\" does not exist"),
248 gettext_noop("table \"%s\" does not exist, skipping"),
249 gettext_noop("\"%s\" is not a table"),
250 gettext_noop("Use DROP TABLE to remove a table.")},
251 {RELKIND_SEQUENCE,
252 ERRCODE_UNDEFINED_TABLE,
253 gettext_noop("sequence \"%s\" does not exist"),
254 gettext_noop("sequence \"%s\" does not exist, skipping"),
255 gettext_noop("\"%s\" is not a sequence"),
256 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
257 {RELKIND_VIEW,
258 ERRCODE_UNDEFINED_TABLE,
259 gettext_noop("view \"%s\" does not exist"),
260 gettext_noop("view \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a view"),
262 gettext_noop("Use DROP VIEW to remove a view.")},
263 {RELKIND_MATVIEW,
264 ERRCODE_UNDEFINED_TABLE,
265 gettext_noop("materialized view \"%s\" does not exist"),
266 gettext_noop("materialized view \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a materialized view"),
268 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
269 {RELKIND_INDEX,
270 ERRCODE_UNDEFINED_OBJECT,
271 gettext_noop("index \"%s\" does not exist"),
272 gettext_noop("index \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not an index"),
274 gettext_noop("Use DROP INDEX to remove an index.")},
275 {RELKIND_COMPOSITE_TYPE,
276 ERRCODE_UNDEFINED_OBJECT,
277 gettext_noop("type \"%s\" does not exist"),
278 gettext_noop("type \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a type"),
280 gettext_noop("Use DROP TYPE to remove a type.")},
281 {RELKIND_FOREIGN_TABLE,
282 ERRCODE_UNDEFINED_OBJECT,
283 gettext_noop("foreign table \"%s\" does not exist"),
284 gettext_noop("foreign table \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not a foreign table"),
286 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
287 {RELKIND_PARTITIONED_TABLE,
288 ERRCODE_UNDEFINED_TABLE,
289 gettext_noop("table \"%s\" does not exist"),
290 gettext_noop("table \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a table"),
292 gettext_noop("Use DROP TABLE to remove a table.")},
293 {RELKIND_PARTITIONED_INDEX,
294 ERRCODE_UNDEFINED_OBJECT,
295 gettext_noop("index \"%s\" does not exist"),
296 gettext_noop("index \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not an index"),
298 gettext_noop("Use DROP INDEX to remove an index.")},
299 {'\0', 0, NULL, NULL, NULL, NULL}
302 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
303 struct DropRelationCallbackState
305 /* These fields are set by RemoveRelations: */
306 char expected_relkind;
307 LOCKMODE heap_lockmode;
308 /* These fields are state to track which subsidiary locks are held: */
309 Oid heapOid;
310 Oid partParentOid;
311 /* These fields are passed back by RangeVarCallbackForDropRelation: */
312 char actual_relkind;
313 char actual_relpersistence;
316 /* Alter table target-type flags for ATSimplePermissions */
317 #define ATT_TABLE 0x0001
318 #define ATT_VIEW 0x0002
319 #define ATT_MATVIEW 0x0004
320 #define ATT_INDEX 0x0008
321 #define ATT_COMPOSITE_TYPE 0x0010
322 #define ATT_FOREIGN_TABLE 0x0020
323 #define ATT_PARTITIONED_INDEX 0x0040
324 #define ATT_SEQUENCE 0x0080
327 * ForeignTruncateInfo
329 * Information related to truncation of foreign tables. This is used for
330 * the elements in a hash table. It uses the server OID as lookup key,
331 * and includes a per-server list of all foreign tables involved in the
332 * truncation.
334 typedef struct ForeignTruncateInfo
336 Oid serverid;
337 List *rels;
338 } ForeignTruncateInfo;
341 * Partition tables are expected to be dropped when the parent partitioned
342 * table gets dropped. Hence for partitioning we use AUTO dependency.
343 * Otherwise, for regular inheritance use NORMAL dependency.
345 #define child_dependency_type(child_is_partition) \
346 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
348 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
349 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
350 static void truncate_check_activity(Relation rel);
351 static void RangeVarCallbackForTruncate(const RangeVar *relation,
352 Oid relId, Oid oldRelId, void *arg);
353 static List *MergeAttributes(List *schema, List *supers, char relpersistence,
354 bool is_partition, List **supconstr);
355 static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
356 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
357 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
358 static void StoreCatalogInheritance(Oid relationId, List *supers,
359 bool child_is_partition);
360 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
361 int32 seqNumber, Relation inhRelation,
362 bool child_is_partition);
363 static int findAttrByName(const char *attributeName, List *schema);
364 static void AlterIndexNamespaces(Relation classRel, Relation rel,
365 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
366 static void AlterSeqNamespaces(Relation classRel, Relation rel,
367 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
368 LOCKMODE lockmode);
369 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
370 bool recurse, bool recursing, LOCKMODE lockmode);
371 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
372 Relation rel, HeapTuple contuple, List **otherrelids,
373 LOCKMODE lockmode);
374 static ObjectAddress ATExecValidateConstraint(List **wqueue,
375 Relation rel, char *constrName,
376 bool recurse, bool recursing, LOCKMODE lockmode);
377 static int transformColumnNameList(Oid relId, List *colList,
378 int16 *attnums, Oid *atttypids);
379 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
380 List **attnamelist,
381 int16 *attnums, Oid *atttypids,
382 Oid *opclasses);
383 static Oid transformFkeyCheckAttrs(Relation pkrel,
384 int numattrs, int16 *attnums,
385 Oid *opclasses);
386 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
387 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
388 Oid *funcid);
389 static void validateForeignKeyConstraint(char *conname,
390 Relation rel, Relation pkrel,
391 Oid pkindOid, Oid constraintOid);
392 static void ATController(AlterTableStmt *parsetree,
393 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
394 AlterTableUtilityContext *context);
395 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
396 bool recurse, bool recursing, LOCKMODE lockmode,
397 AlterTableUtilityContext *context);
398 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
399 AlterTableUtilityContext *context);
400 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
401 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
402 AlterTableUtilityContext *context);
403 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
404 Relation rel, AlterTableCmd *cmd,
405 bool recurse, LOCKMODE lockmode,
406 int cur_pass,
407 AlterTableUtilityContext *context);
408 static void ATRewriteTables(AlterTableStmt *parsetree,
409 List **wqueue, LOCKMODE lockmode,
410 AlterTableUtilityContext *context);
411 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
412 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
413 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
414 static void ATSimpleRecursion(List **wqueue, Relation rel,
415 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
416 AlterTableUtilityContext *context);
417 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
418 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
419 LOCKMODE lockmode,
420 AlterTableUtilityContext *context);
421 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
422 DropBehavior behavior);
423 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
424 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
425 AlterTableUtilityContext *context);
426 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
427 Relation rel, AlterTableCmd **cmd,
428 bool recurse, bool recursing,
429 LOCKMODE lockmode, int cur_pass,
430 AlterTableUtilityContext *context);
431 static bool check_for_column_name_collision(Relation rel, const char *colname,
432 bool if_not_exists);
433 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
434 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
435 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
436 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
437 static void ATPrepSetNotNull(List **wqueue, Relation rel,
438 AlterTableCmd *cmd, bool recurse, bool recursing,
439 LOCKMODE lockmode,
440 AlterTableUtilityContext *context);
441 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
442 const char *colName, LOCKMODE lockmode);
443 static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
444 const char *colName, LOCKMODE lockmode);
445 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
446 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
447 List *testConstraint, List *provenConstraint);
448 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
449 Node *newDefault, LOCKMODE lockmode);
450 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
451 Node *newDefault);
452 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
453 Node *def, LOCKMODE lockmode);
454 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
455 Node *def, LOCKMODE lockmode);
456 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
457 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
458 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
459 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
460 Node *newValue, LOCKMODE lockmode);
461 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
462 Node *options, bool isReset, LOCKMODE lockmode);
463 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
464 Node *newValue, LOCKMODE lockmode);
465 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
466 AlterTableCmd *cmd, LOCKMODE lockmode,
467 AlterTableUtilityContext *context);
468 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
469 DropBehavior behavior,
470 bool recurse, bool recursing,
471 bool missing_ok, LOCKMODE lockmode,
472 ObjectAddresses *addrs);
473 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
474 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
475 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
476 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
477 static ObjectAddress ATExecAddConstraint(List **wqueue,
478 AlteredTableInfo *tab, Relation rel,
479 Constraint *newConstraint, bool recurse, bool is_readd,
480 LOCKMODE lockmode);
481 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
482 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
483 IndexStmt *stmt, LOCKMODE lockmode);
484 static ObjectAddress ATAddCheckConstraint(List **wqueue,
485 AlteredTableInfo *tab, Relation rel,
486 Constraint *constr,
487 bool recurse, bool recursing, bool is_readd,
488 LOCKMODE lockmode);
489 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
490 Relation rel, Constraint *fkconstraint,
491 bool recurse, bool recursing,
492 LOCKMODE lockmode);
493 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
494 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
495 int numfks, int16 *pkattnum, int16 *fkattnum,
496 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
497 int numfkdelsetcols, int16 *fkdelsetcols,
498 bool old_check_ok,
499 Oid parentDelTrigger, Oid parentUpdTrigger);
500 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
501 int numfksetcols, const int16 *fksetcolsattnums,
502 List *fksetcols);
503 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
504 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
505 int numfks, int16 *pkattnum, int16 *fkattnum,
506 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
507 int numfkdelsetcols, int16 *fkdelsetcols,
508 bool old_check_ok, LOCKMODE lockmode,
509 Oid parentInsTrigger, Oid parentUpdTrigger);
510 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
511 Relation partitionRel);
512 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
513 static void CloneFkReferencing(List **wqueue, Relation parentRel,
514 Relation partRel);
515 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
516 Constraint *fkconstraint, Oid constraintOid,
517 Oid indexOid,
518 Oid parentInsTrigger, Oid parentUpdTrigger,
519 Oid *insertTrigOid, Oid *updateTrigOid);
520 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
521 Constraint *fkconstraint, Oid constraintOid,
522 Oid indexOid,
523 Oid parentDelTrigger, Oid parentUpdTrigger,
524 Oid *deleteTrigOid, Oid *updateTrigOid);
525 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
526 Oid partRelid,
527 Oid parentConstrOid, int numfks,
528 AttrNumber *mapped_conkey, AttrNumber *confkey,
529 Oid *conpfeqop,
530 Oid parentInsTrigger,
531 Oid parentUpdTrigger,
532 Relation trigrel);
533 static void GetForeignKeyActionTriggers(Relation trigrel,
534 Oid conoid, Oid confrelid, Oid conrelid,
535 Oid *deleteTriggerOid,
536 Oid *updateTriggerOid);
537 static void GetForeignKeyCheckTriggers(Relation trigrel,
538 Oid conoid, Oid confrelid, Oid conrelid,
539 Oid *insertTriggerOid,
540 Oid *updateTriggerOid);
541 static void ATExecDropConstraint(Relation rel, const char *constrName,
542 DropBehavior behavior,
543 bool recurse, bool recursing,
544 bool missing_ok, LOCKMODE lockmode);
545 static void ATPrepAlterColumnType(List **wqueue,
546 AlteredTableInfo *tab, Relation rel,
547 bool recurse, bool recursing,
548 AlterTableCmd *cmd, LOCKMODE lockmode,
549 AlterTableUtilityContext *context);
550 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
551 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
552 AlterTableCmd *cmd, LOCKMODE lockmode);
553 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
554 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
555 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
556 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
557 LOCKMODE lockmode);
558 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
559 char *cmd, List **wqueue, LOCKMODE lockmode,
560 bool rewrite);
561 static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
562 Oid objid, Relation rel, List *domname,
563 const char *conname);
564 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
565 static void TryReuseForeignKey(Oid oldId, Constraint *con);
566 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
567 List *options, LOCKMODE lockmode);
568 static void change_owner_fix_column_acls(Oid relationOid,
569 Oid oldOwnerId, Oid newOwnerId);
570 static void change_owner_recurse_to_sequences(Oid relationOid,
571 Oid newOwnerId, LOCKMODE lockmode);
572 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
573 LOCKMODE lockmode);
574 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
575 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
576 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
577 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
578 const char *tablespacename, LOCKMODE lockmode);
579 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
580 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
581 static void ATExecSetRelOptions(Relation rel, List *defList,
582 AlterTableType operation,
583 LOCKMODE lockmode);
584 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
585 char fires_when, bool skip_system, bool recurse,
586 LOCKMODE lockmode);
587 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
588 char fires_when, LOCKMODE lockmode);
589 static void ATPrepAddInherit(Relation child_rel);
590 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
591 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
592 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
593 DependencyType deptype);
594 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
595 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
596 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
597 static void ATExecGenericOptions(Relation rel, List *options);
598 static void ATExecSetRowSecurity(Relation rel, bool rls);
599 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
600 static ObjectAddress ATExecSetCompression(Relation rel,
601 const char *column, Node *newValue, LOCKMODE lockmode);
603 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
604 static const char *storage_name(char c);
606 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
607 Oid oldRelOid, void *arg);
608 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
609 Oid oldrelid, void *arg);
610 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
611 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
612 List **partexprs, Oid *partopclass, Oid *partcollation,
613 PartitionStrategy strategy);
614 static void CreateInheritance(Relation child_rel, Relation parent_rel);
615 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
616 bool expect_detached);
617 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
618 PartitionCmd *cmd,
619 AlterTableUtilityContext *context);
620 static void AttachPartitionEnsureIndexes(Relation rel, Relation attachrel);
621 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
622 List *partConstraint,
623 bool validate_default);
624 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
625 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
626 static void DropClonedTriggersFromPartition(Oid partitionId);
627 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
628 Relation rel, RangeVar *name,
629 bool concurrent);
630 static void DetachPartitionFinalize(Relation rel, Relation partRel,
631 bool concurrent, Oid defaultPartOid);
632 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
633 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
634 RangeVar *name);
635 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
636 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
637 Relation partitionTbl);
638 static List *GetParentedForeignKeyRefs(Relation partition);
639 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
640 static char GetAttributeCompression(Oid atttypid, char *compression);
641 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
644 /* ----------------------------------------------------------------
645 * DefineRelation
646 * Creates a new relation.
648 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
649 * The other arguments are used to extend the behavior for other cases:
650 * relkind: relkind to assign to the new relation
651 * ownerId: if not InvalidOid, use this as the new relation's owner.
652 * typaddress: if not null, it's set to the pg_type entry's address.
653 * queryString: for error reporting
655 * Note that permissions checks are done against current user regardless of
656 * ownerId. A nonzero ownerId is used when someone is creating a relation
657 * "on behalf of" someone else, so we still want to see that the current user
658 * has permissions to do it.
660 * If successful, returns the address of the new relation.
661 * ----------------------------------------------------------------
663 ObjectAddress
664 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
665 ObjectAddress *typaddress, const char *queryString)
667 char relname[NAMEDATALEN];
668 Oid namespaceId;
669 Oid relationId;
670 Oid tablespaceId;
671 Relation rel;
672 TupleDesc descriptor;
673 List *inheritOids;
674 List *old_constraints;
675 List *rawDefaults;
676 List *cookedDefaults;
677 Datum reloptions;
678 ListCell *listptr;
679 AttrNumber attnum;
680 bool partitioned;
681 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
682 Oid ofTypeId;
683 ObjectAddress address;
684 LOCKMODE parentLockmode;
685 const char *accessMethod = NULL;
686 Oid accessMethodId = InvalidOid;
689 * Truncate relname to appropriate length (probably a waste of time, as
690 * parser should have done this already).
692 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
695 * Check consistency of arguments
697 if (stmt->oncommit != ONCOMMIT_NOOP
698 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
699 ereport(ERROR,
700 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
701 errmsg("ON COMMIT can only be used on temporary tables")));
703 if (stmt->partspec != NULL)
705 if (relkind != RELKIND_RELATION)
706 elog(ERROR, "unexpected relkind: %d", (int) relkind);
708 relkind = RELKIND_PARTITIONED_TABLE;
709 partitioned = true;
711 else
712 partitioned = false;
715 * Look up the namespace in which we are supposed to create the relation,
716 * check we have permission to create there, lock it against concurrent
717 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
718 * namespace is selected.
720 namespaceId =
721 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
724 * Security check: disallow creating temp tables from security-restricted
725 * code. This is needed because calling code might not expect untrusted
726 * tables to appear in pg_temp at the front of its search path.
728 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
729 && InSecurityRestrictedOperation())
730 ereport(ERROR,
731 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
732 errmsg("cannot create temporary table within security-restricted operation")));
735 * Determine the lockmode to use when scanning parents. A self-exclusive
736 * lock is needed here.
738 * For regular inheritance, if two backends attempt to add children to the
739 * same parent simultaneously, and that parent has no pre-existing
740 * children, then both will attempt to update the parent's relhassubclass
741 * field, leading to a "tuple concurrently updated" error. Also, this
742 * interlocks against a concurrent ANALYZE on the parent table, which
743 * might otherwise be attempting to clear the parent's relhassubclass
744 * field, if its previous children were recently dropped.
746 * If the child table is a partition, then we instead grab an exclusive
747 * lock on the parent because its partition descriptor will be changed by
748 * addition of the new partition.
750 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
751 ShareUpdateExclusiveLock);
753 /* Determine the list of OIDs of the parents. */
754 inheritOids = NIL;
755 foreach(listptr, stmt->inhRelations)
757 RangeVar *rv = (RangeVar *) lfirst(listptr);
758 Oid parentOid;
760 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
763 * Reject duplications in the list of parents.
765 if (list_member_oid(inheritOids, parentOid))
766 ereport(ERROR,
767 (errcode(ERRCODE_DUPLICATE_TABLE),
768 errmsg("relation \"%s\" would be inherited from more than once",
769 get_rel_name(parentOid))));
771 inheritOids = lappend_oid(inheritOids, parentOid);
775 * Select tablespace to use: an explicitly indicated one, or (in the case
776 * of a partitioned table) the parent's, if it has one.
778 if (stmt->tablespacename)
780 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
782 if (partitioned && tablespaceId == MyDatabaseTableSpace)
783 ereport(ERROR,
784 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
785 errmsg("cannot specify default tablespace for partitioned relations")));
787 else if (stmt->partbound)
790 * For partitions, when no other tablespace is specified, we default
791 * the tablespace to the parent partitioned table's.
793 Assert(list_length(inheritOids) == 1);
794 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
796 else
797 tablespaceId = InvalidOid;
799 /* still nothing? use the default */
800 if (!OidIsValid(tablespaceId))
801 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
802 partitioned);
804 /* Check permissions except when using database's default */
805 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
807 AclResult aclresult;
809 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
810 ACL_CREATE);
811 if (aclresult != ACLCHECK_OK)
812 aclcheck_error(aclresult, OBJECT_TABLESPACE,
813 get_tablespace_name(tablespaceId));
816 /* In all cases disallow placing user relations in pg_global */
817 if (tablespaceId == GLOBALTABLESPACE_OID)
818 ereport(ERROR,
819 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
820 errmsg("only shared relations can be placed in pg_global tablespace")));
822 /* Identify user ID that will own the table */
823 if (!OidIsValid(ownerId))
824 ownerId = GetUserId();
827 * Parse and validate reloptions, if any.
829 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
830 true, false);
832 switch (relkind)
834 case RELKIND_VIEW:
835 (void) view_reloptions(reloptions, true);
836 break;
837 case RELKIND_PARTITIONED_TABLE:
838 (void) partitioned_table_reloptions(reloptions, true);
839 break;
840 default:
841 (void) heap_reloptions(relkind, reloptions, true);
844 if (stmt->ofTypename)
846 AclResult aclresult;
848 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
850 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
851 if (aclresult != ACLCHECK_OK)
852 aclcheck_error_type(aclresult, ofTypeId);
854 else
855 ofTypeId = InvalidOid;
858 * Look up inheritance ancestors and generate relation schema, including
859 * inherited attributes. (Note that stmt->tableElts is destructively
860 * modified by MergeAttributes.)
862 stmt->tableElts =
863 MergeAttributes(stmt->tableElts, inheritOids,
864 stmt->relation->relpersistence,
865 stmt->partbound != NULL,
866 &old_constraints);
869 * Create a tuple descriptor from the relation schema. Note that this
870 * deals with column names, types, and NOT NULL constraints, but not
871 * default values or CHECK constraints; we handle those below.
873 descriptor = BuildDescForRelation(stmt->tableElts);
876 * Find columns with default values and prepare for insertion of the
877 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
878 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
879 * while raw defaults go into a list of RawColumnDefault structs that will
880 * be processed by AddRelationNewConstraints. (We can't deal with raw
881 * expressions until we can do transformExpr.)
883 * We can set the atthasdef flags now in the tuple descriptor; this just
884 * saves StoreAttrDefault from having to do an immediate update of the
885 * pg_attribute rows.
887 rawDefaults = NIL;
888 cookedDefaults = NIL;
889 attnum = 0;
891 foreach(listptr, stmt->tableElts)
893 ColumnDef *colDef = lfirst(listptr);
894 Form_pg_attribute attr;
896 attnum++;
897 attr = TupleDescAttr(descriptor, attnum - 1);
899 if (colDef->raw_default != NULL)
901 RawColumnDefault *rawEnt;
903 Assert(colDef->cooked_default == NULL);
905 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
906 rawEnt->attnum = attnum;
907 rawEnt->raw_default = colDef->raw_default;
908 rawEnt->missingMode = false;
909 rawEnt->generated = colDef->generated;
910 rawDefaults = lappend(rawDefaults, rawEnt);
911 attr->atthasdef = true;
913 else if (colDef->cooked_default != NULL)
915 CookedConstraint *cooked;
917 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
918 cooked->contype = CONSTR_DEFAULT;
919 cooked->conoid = InvalidOid; /* until created */
920 cooked->name = NULL;
921 cooked->attnum = attnum;
922 cooked->expr = colDef->cooked_default;
923 cooked->skip_validation = false;
924 cooked->is_local = true; /* not used for defaults */
925 cooked->inhcount = 0; /* ditto */
926 cooked->is_no_inherit = false;
927 cookedDefaults = lappend(cookedDefaults, cooked);
928 attr->atthasdef = true;
931 if (colDef->identity)
932 attr->attidentity = colDef->identity;
934 if (colDef->generated)
935 attr->attgenerated = colDef->generated;
937 if (colDef->compression)
938 attr->attcompression = GetAttributeCompression(attr->atttypid,
939 colDef->compression);
941 if (colDef->storage_name)
942 attr->attstorage = GetAttributeStorage(attr->atttypid, colDef->storage_name);
946 * If the statement hasn't specified an access method, but we're defining
947 * a type of relation that needs one, use the default.
949 if (stmt->accessMethod != NULL)
951 accessMethod = stmt->accessMethod;
953 if (partitioned)
954 ereport(ERROR,
955 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
956 errmsg("specifying a table access method is not supported on a partitioned table")));
958 else if (RELKIND_HAS_TABLE_AM(relkind))
959 accessMethod = default_table_access_method;
961 /* look up the access method, verify it is for a table */
962 if (accessMethod != NULL)
963 accessMethodId = get_table_am_oid(accessMethod, false);
966 * Create the relation. Inherited defaults and constraints are passed in
967 * for immediate handling --- since they don't need parsing, they can be
968 * stored immediately.
970 relationId = heap_create_with_catalog(relname,
971 namespaceId,
972 tablespaceId,
973 InvalidOid,
974 InvalidOid,
975 ofTypeId,
976 ownerId,
977 accessMethodId,
978 descriptor,
979 list_concat(cookedDefaults,
980 old_constraints),
981 relkind,
982 stmt->relation->relpersistence,
983 false,
984 false,
985 stmt->oncommit,
986 reloptions,
987 true,
988 allowSystemTableMods,
989 false,
990 InvalidOid,
991 typaddress);
994 * We must bump the command counter to make the newly-created relation
995 * tuple visible for opening.
997 CommandCounterIncrement();
1000 * Open the new relation and acquire exclusive lock on it. This isn't
1001 * really necessary for locking out other backends (since they can't see
1002 * the new rel anyway until we commit), but it keeps the lock manager from
1003 * complaining about deadlock risks.
1005 rel = relation_open(relationId, AccessExclusiveLock);
1008 * Now add any newly specified column default and generation expressions
1009 * to the new relation. These are passed to us in the form of raw
1010 * parsetrees; we need to transform them to executable expression trees
1011 * before they can be added. The most convenient way to do that is to
1012 * apply the parser's transformExpr routine, but transformExpr doesn't
1013 * work unless we have a pre-existing relation. So, the transformation has
1014 * to be postponed to this final step of CREATE TABLE.
1016 * This needs to be before processing the partitioning clauses because
1017 * those could refer to generated columns.
1019 if (rawDefaults)
1020 AddRelationNewConstraints(rel, rawDefaults, NIL,
1021 true, true, false, queryString);
1024 * Make column generation expressions visible for use by partitioning.
1026 CommandCounterIncrement();
1028 /* Process and store partition bound, if any. */
1029 if (stmt->partbound)
1031 PartitionBoundSpec *bound;
1032 ParseState *pstate;
1033 Oid parentId = linitial_oid(inheritOids),
1034 defaultPartOid;
1035 Relation parent,
1036 defaultRel = NULL;
1037 ParseNamespaceItem *nsitem;
1039 /* Already have strong enough lock on the parent */
1040 parent = table_open(parentId, NoLock);
1043 * We are going to try to validate the partition bound specification
1044 * against the partition key of parentRel, so it better have one.
1046 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1047 ereport(ERROR,
1048 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1049 errmsg("\"%s\" is not partitioned",
1050 RelationGetRelationName(parent))));
1053 * The partition constraint of the default partition depends on the
1054 * partition bounds of every other partition. It is possible that
1055 * another backend might be about to execute a query on the default
1056 * partition table, and that the query relies on previously cached
1057 * default partition constraints. We must therefore take a table lock
1058 * strong enough to prevent all queries on the default partition from
1059 * proceeding until we commit and send out a shared-cache-inval notice
1060 * that will make them update their index lists.
1062 * Order of locking: The relation being added won't be visible to
1063 * other backends until it is committed, hence here in
1064 * DefineRelation() the order of locking the default partition and the
1065 * relation being added does not matter. But at all other places we
1066 * need to lock the default relation before we lock the relation being
1067 * added or removed i.e. we should take the lock in same order at all
1068 * the places such that lock parent, lock default partition and then
1069 * lock the partition so as to avoid a deadlock.
1071 defaultPartOid =
1072 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1073 true));
1074 if (OidIsValid(defaultPartOid))
1075 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1077 /* Transform the bound values */
1078 pstate = make_parsestate(NULL);
1079 pstate->p_sourcetext = queryString;
1082 * Add an nsitem containing this relation, so that transformExpr
1083 * called on partition bound expressions is able to report errors
1084 * using a proper context.
1086 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1087 NULL, false, false);
1088 addNSItemToQuery(pstate, nsitem, false, true, true);
1090 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1093 * Check first that the new partition's bound is valid and does not
1094 * overlap with any of existing partitions of the parent.
1096 check_new_partition_bound(relname, parent, bound, pstate);
1099 * If the default partition exists, its partition constraints will
1100 * change after the addition of this new partition such that it won't
1101 * allow any row that qualifies for this new partition. So, check that
1102 * the existing data in the default partition satisfies the constraint
1103 * as it will exist after adding this partition.
1105 if (OidIsValid(defaultPartOid))
1107 check_default_partition_contents(parent, defaultRel, bound);
1108 /* Keep the lock until commit. */
1109 table_close(defaultRel, NoLock);
1112 /* Update the pg_class entry. */
1113 StorePartitionBound(rel, parent, bound);
1115 table_close(parent, NoLock);
1118 /* Store inheritance information for new rel. */
1119 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1122 * Process the partitioning specification (if any) and store the partition
1123 * key information into the catalog.
1125 if (partitioned)
1127 ParseState *pstate;
1128 int partnatts;
1129 AttrNumber partattrs[PARTITION_MAX_KEYS];
1130 Oid partopclass[PARTITION_MAX_KEYS];
1131 Oid partcollation[PARTITION_MAX_KEYS];
1132 List *partexprs = NIL;
1134 pstate = make_parsestate(NULL);
1135 pstate->p_sourcetext = queryString;
1137 partnatts = list_length(stmt->partspec->partParams);
1139 /* Protect fixed-size arrays here and in executor */
1140 if (partnatts > PARTITION_MAX_KEYS)
1141 ereport(ERROR,
1142 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1143 errmsg("cannot partition using more than %d columns",
1144 PARTITION_MAX_KEYS)));
1147 * We need to transform the raw parsetrees corresponding to partition
1148 * expressions into executable expression trees. Like column defaults
1149 * and CHECK constraints, we could not have done the transformation
1150 * earlier.
1152 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1154 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1155 partattrs, &partexprs, partopclass,
1156 partcollation, stmt->partspec->strategy);
1158 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1159 partexprs,
1160 partopclass, partcollation);
1162 /* make it all visible */
1163 CommandCounterIncrement();
1167 * If we're creating a partition, create now all the indexes, triggers,
1168 * FKs defined in the parent.
1170 * We can't do it earlier, because DefineIndex wants to know the partition
1171 * key which we just stored.
1173 if (stmt->partbound)
1175 Oid parentId = linitial_oid(inheritOids);
1176 Relation parent;
1177 List *idxlist;
1178 ListCell *cell;
1180 /* Already have strong enough lock on the parent */
1181 parent = table_open(parentId, NoLock);
1182 idxlist = RelationGetIndexList(parent);
1185 * For each index in the parent table, create one in the partition
1187 foreach(cell, idxlist)
1189 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1190 AttrMap *attmap;
1191 IndexStmt *idxstmt;
1192 Oid constraintOid;
1194 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1196 if (idxRel->rd_index->indisunique)
1197 ereport(ERROR,
1198 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1199 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1200 RelationGetRelationName(parent)),
1201 errdetail("Table \"%s\" contains indexes that are unique.",
1202 RelationGetRelationName(parent))));
1203 else
1205 index_close(idxRel, AccessShareLock);
1206 continue;
1210 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1211 RelationGetDescr(parent),
1212 false);
1213 idxstmt =
1214 generateClonedIndexStmt(NULL, idxRel,
1215 attmap, &constraintOid);
1216 DefineIndex(RelationGetRelid(rel),
1217 idxstmt,
1218 InvalidOid,
1219 RelationGetRelid(idxRel),
1220 constraintOid,
1222 false, false, false, false, false);
1224 index_close(idxRel, AccessShareLock);
1227 list_free(idxlist);
1230 * If there are any row-level triggers, clone them to the new
1231 * partition.
1233 if (parent->trigdesc != NULL)
1234 CloneRowTriggersToPartition(parent, rel);
1237 * And foreign keys too. Note that because we're freshly creating the
1238 * table, there is no need to verify these new constraints.
1240 CloneForeignKeyConstraints(NULL, parent, rel);
1242 table_close(parent, NoLock);
1246 * Now add any newly specified CHECK constraints to the new relation. Same
1247 * as for defaults above, but these need to come after partitioning is set
1248 * up.
1250 if (stmt->constraints)
1251 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1252 true, true, false, queryString);
1254 ObjectAddressSet(address, RelationRelationId, relationId);
1257 * Clean up. We keep lock on new relation (although it shouldn't be
1258 * visible to anyone else anyway, until commit).
1260 relation_close(rel, NoLock);
1262 return address;
1266 * Emit the right error or warning message for a "DROP" command issued on a
1267 * non-existent relation
1269 static void
1270 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1272 const struct dropmsgstrings *rentry;
1274 if (rel->schemaname != NULL &&
1275 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1277 if (!missing_ok)
1279 ereport(ERROR,
1280 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1281 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1283 else
1285 ereport(NOTICE,
1286 (errmsg("schema \"%s\" does not exist, skipping",
1287 rel->schemaname)));
1289 return;
1292 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1294 if (rentry->kind == rightkind)
1296 if (!missing_ok)
1298 ereport(ERROR,
1299 (errcode(rentry->nonexistent_code),
1300 errmsg(rentry->nonexistent_msg, rel->relname)));
1302 else
1304 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1305 break;
1310 Assert(rentry->kind != '\0'); /* Should be impossible */
1314 * Emit the right error message for a "DROP" command issued on a
1315 * relation of the wrong type
1317 static void
1318 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1320 const struct dropmsgstrings *rentry;
1321 const struct dropmsgstrings *wentry;
1323 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1324 if (rentry->kind == rightkind)
1325 break;
1326 Assert(rentry->kind != '\0');
1328 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1329 if (wentry->kind == wrongkind)
1330 break;
1331 /* wrongkind could be something we don't have in our table... */
1333 ereport(ERROR,
1334 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1335 errmsg(rentry->nota_msg, relname),
1336 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1340 * RemoveRelations
1341 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1342 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1344 void
1345 RemoveRelations(DropStmt *drop)
1347 ObjectAddresses *objects;
1348 char relkind;
1349 ListCell *cell;
1350 int flags = 0;
1351 LOCKMODE lockmode = AccessExclusiveLock;
1353 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1354 if (drop->concurrent)
1357 * Note that for temporary relations this lock may get upgraded later
1358 * on, but as no other session can access a temporary relation, this
1359 * is actually fine.
1361 lockmode = ShareUpdateExclusiveLock;
1362 Assert(drop->removeType == OBJECT_INDEX);
1363 if (list_length(drop->objects) != 1)
1364 ereport(ERROR,
1365 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1366 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1367 if (drop->behavior == DROP_CASCADE)
1368 ereport(ERROR,
1369 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1370 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1374 * First we identify all the relations, then we delete them in a single
1375 * performMultipleDeletions() call. This is to avoid unwanted DROP
1376 * RESTRICT errors if one of the relations depends on another.
1379 /* Determine required relkind */
1380 switch (drop->removeType)
1382 case OBJECT_TABLE:
1383 relkind = RELKIND_RELATION;
1384 break;
1386 case OBJECT_INDEX:
1387 relkind = RELKIND_INDEX;
1388 break;
1390 case OBJECT_SEQUENCE:
1391 relkind = RELKIND_SEQUENCE;
1392 break;
1394 case OBJECT_VIEW:
1395 relkind = RELKIND_VIEW;
1396 break;
1398 case OBJECT_MATVIEW:
1399 relkind = RELKIND_MATVIEW;
1400 break;
1402 case OBJECT_FOREIGN_TABLE:
1403 relkind = RELKIND_FOREIGN_TABLE;
1404 break;
1406 default:
1407 elog(ERROR, "unrecognized drop object type: %d",
1408 (int) drop->removeType);
1409 relkind = 0; /* keep compiler quiet */
1410 break;
1413 /* Lock and validate each relation; build a list of object addresses */
1414 objects = new_object_addresses();
1416 foreach(cell, drop->objects)
1418 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1419 Oid relOid;
1420 ObjectAddress obj;
1421 struct DropRelationCallbackState state;
1424 * These next few steps are a great deal like relation_openrv, but we
1425 * don't bother building a relcache entry since we don't need it.
1427 * Check for shared-cache-inval messages before trying to access the
1428 * relation. This is needed to cover the case where the name
1429 * identifies a rel that has been dropped and recreated since the
1430 * start of our transaction: if we don't flush the old syscache entry,
1431 * then we'll latch onto that entry and suffer an error later.
1433 AcceptInvalidationMessages();
1435 /* Look up the appropriate relation using namespace search. */
1436 state.expected_relkind = relkind;
1437 state.heap_lockmode = drop->concurrent ?
1438 ShareUpdateExclusiveLock : AccessExclusiveLock;
1439 /* We must initialize these fields to show that no locks are held: */
1440 state.heapOid = InvalidOid;
1441 state.partParentOid = InvalidOid;
1443 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1444 RangeVarCallbackForDropRelation,
1445 (void *) &state);
1447 /* Not there? */
1448 if (!OidIsValid(relOid))
1450 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1451 continue;
1455 * Decide if concurrent mode needs to be used here or not. The
1456 * callback retrieved the rel's persistence for us.
1458 if (drop->concurrent &&
1459 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1461 Assert(list_length(drop->objects) == 1 &&
1462 drop->removeType == OBJECT_INDEX);
1463 flags |= PERFORM_DELETION_CONCURRENTLY;
1467 * Concurrent index drop cannot be used with partitioned indexes,
1468 * either.
1470 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1471 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1472 ereport(ERROR,
1473 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1474 errmsg("cannot drop partitioned index \"%s\" concurrently",
1475 rel->relname)));
1478 * If we're told to drop a partitioned index, we must acquire lock on
1479 * all the children of its parent partitioned table before proceeding.
1480 * Otherwise we'd try to lock the child index partitions before their
1481 * tables, leading to potential deadlock against other sessions that
1482 * will lock those objects in the other order.
1484 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1485 (void) find_all_inheritors(state.heapOid,
1486 state.heap_lockmode,
1487 NULL);
1489 /* OK, we're ready to delete this one */
1490 obj.classId = RelationRelationId;
1491 obj.objectId = relOid;
1492 obj.objectSubId = 0;
1494 add_exact_object_address(&obj, objects);
1497 performMultipleDeletions(objects, drop->behavior, flags);
1499 free_object_addresses(objects);
1503 * Before acquiring a table lock, check whether we have sufficient rights.
1504 * In the case of DROP INDEX, also try to lock the table before the index.
1505 * Also, if the table to be dropped is a partition, we try to lock the parent
1506 * first.
1508 static void
1509 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1510 void *arg)
1512 HeapTuple tuple;
1513 struct DropRelationCallbackState *state;
1514 char expected_relkind;
1515 bool is_partition;
1516 Form_pg_class classform;
1517 LOCKMODE heap_lockmode;
1518 bool invalid_system_index = false;
1520 state = (struct DropRelationCallbackState *) arg;
1521 heap_lockmode = state->heap_lockmode;
1524 * If we previously locked some other index's heap, and the name we're
1525 * looking up no longer refers to that relation, release the now-useless
1526 * lock.
1528 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1530 UnlockRelationOid(state->heapOid, heap_lockmode);
1531 state->heapOid = InvalidOid;
1535 * Similarly, if we previously locked some other partition's heap, and the
1536 * name we're looking up no longer refers to that relation, release the
1537 * now-useless lock.
1539 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1541 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1542 state->partParentOid = InvalidOid;
1545 /* Didn't find a relation, so no need for locking or permission checks. */
1546 if (!OidIsValid(relOid))
1547 return;
1549 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1550 if (!HeapTupleIsValid(tuple))
1551 return; /* concurrently dropped, so nothing to do */
1552 classform = (Form_pg_class) GETSTRUCT(tuple);
1553 is_partition = classform->relispartition;
1555 /* Pass back some data to save lookups in RemoveRelations */
1556 state->actual_relkind = classform->relkind;
1557 state->actual_relpersistence = classform->relpersistence;
1560 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1561 * but RemoveRelations() can only pass one relkind for a given relation.
1562 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1563 * That means we must be careful before giving the wrong type error when
1564 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1565 * exists with indexes.
1567 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1568 expected_relkind = RELKIND_RELATION;
1569 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1570 expected_relkind = RELKIND_INDEX;
1571 else
1572 expected_relkind = classform->relkind;
1574 if (state->expected_relkind != expected_relkind)
1575 DropErrorMsgWrongType(rel->relname, classform->relkind,
1576 state->expected_relkind);
1578 /* Allow DROP to either table owner or schema owner */
1579 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1580 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1581 aclcheck_error(ACLCHECK_NOT_OWNER,
1582 get_relkind_objtype(classform->relkind),
1583 rel->relname);
1586 * Check the case of a system index that might have been invalidated by a
1587 * failed concurrent process and allow its drop. For the time being, this
1588 * only concerns indexes of toast relations that became invalid during a
1589 * REINDEX CONCURRENTLY process.
1591 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1593 HeapTuple locTuple;
1594 Form_pg_index indexform;
1595 bool indisvalid;
1597 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1598 if (!HeapTupleIsValid(locTuple))
1600 ReleaseSysCache(tuple);
1601 return;
1604 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1605 indisvalid = indexform->indisvalid;
1606 ReleaseSysCache(locTuple);
1608 /* Mark object as being an invalid index of system catalogs */
1609 if (!indisvalid)
1610 invalid_system_index = true;
1613 /* In the case of an invalid index, it is fine to bypass this check */
1614 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1615 ereport(ERROR,
1616 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1617 errmsg("permission denied: \"%s\" is a system catalog",
1618 rel->relname)));
1620 ReleaseSysCache(tuple);
1623 * In DROP INDEX, attempt to acquire lock on the parent table before
1624 * locking the index. index_drop() will need this anyway, and since
1625 * regular queries lock tables before their indexes, we risk deadlock if
1626 * we do it the other way around. No error if we don't find a pg_index
1627 * entry, though --- the relation may have been dropped. Note that this
1628 * code will execute for either plain or partitioned indexes.
1630 if (expected_relkind == RELKIND_INDEX &&
1631 relOid != oldRelOid)
1633 state->heapOid = IndexGetRelation(relOid, true);
1634 if (OidIsValid(state->heapOid))
1635 LockRelationOid(state->heapOid, heap_lockmode);
1639 * Similarly, if the relation is a partition, we must acquire lock on its
1640 * parent before locking the partition. That's because queries lock the
1641 * parent before its partitions, so we risk deadlock if we do it the other
1642 * way around.
1644 if (is_partition && relOid != oldRelOid)
1646 state->partParentOid = get_partition_parent(relOid, true);
1647 if (OidIsValid(state->partParentOid))
1648 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1653 * ExecuteTruncate
1654 * Executes a TRUNCATE command.
1656 * This is a multi-relation truncate. We first open and grab exclusive
1657 * lock on all relations involved, checking permissions and otherwise
1658 * verifying that the relation is OK for truncation. Note that if relations
1659 * are foreign tables, at this stage, we have not yet checked that their
1660 * foreign data in external data sources are OK for truncation. These are
1661 * checked when foreign data are actually truncated later. In CASCADE mode,
1662 * relations having FK references to the targeted relations are automatically
1663 * added to the group; in RESTRICT mode, we check that all FK references are
1664 * internal to the group that's being truncated. Finally all the relations
1665 * are truncated and reindexed.
1667 void
1668 ExecuteTruncate(TruncateStmt *stmt)
1670 List *rels = NIL;
1671 List *relids = NIL;
1672 List *relids_logged = NIL;
1673 ListCell *cell;
1676 * Open, exclusive-lock, and check all the explicitly-specified relations
1678 foreach(cell, stmt->relations)
1680 RangeVar *rv = lfirst(cell);
1681 Relation rel;
1682 bool recurse = rv->inh;
1683 Oid myrelid;
1684 LOCKMODE lockmode = AccessExclusiveLock;
1686 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1687 0, RangeVarCallbackForTruncate,
1688 NULL);
1690 /* don't throw error for "TRUNCATE foo, foo" */
1691 if (list_member_oid(relids, myrelid))
1692 continue;
1694 /* open the relation, we already hold a lock on it */
1695 rel = table_open(myrelid, NoLock);
1698 * RangeVarGetRelidExtended() has done most checks with its callback,
1699 * but other checks with the now-opened Relation remain.
1701 truncate_check_activity(rel);
1703 rels = lappend(rels, rel);
1704 relids = lappend_oid(relids, myrelid);
1706 /* Log this relation only if needed for logical decoding */
1707 if (RelationIsLogicallyLogged(rel))
1708 relids_logged = lappend_oid(relids_logged, myrelid);
1710 if (recurse)
1712 ListCell *child;
1713 List *children;
1715 children = find_all_inheritors(myrelid, lockmode, NULL);
1717 foreach(child, children)
1719 Oid childrelid = lfirst_oid(child);
1721 if (list_member_oid(relids, childrelid))
1722 continue;
1724 /* find_all_inheritors already got lock */
1725 rel = table_open(childrelid, NoLock);
1728 * It is possible that the parent table has children that are
1729 * temp tables of other backends. We cannot safely access
1730 * such tables (because of buffering issues), and the best
1731 * thing to do is to silently ignore them. Note that this
1732 * check is the same as one of the checks done in
1733 * truncate_check_activity() called below, still it is kept
1734 * here for simplicity.
1736 if (RELATION_IS_OTHER_TEMP(rel))
1738 table_close(rel, lockmode);
1739 continue;
1743 * Inherited TRUNCATE commands perform access permission
1744 * checks on the parent table only. So we skip checking the
1745 * children's permissions and don't call
1746 * truncate_check_perms() here.
1748 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1749 truncate_check_activity(rel);
1751 rels = lappend(rels, rel);
1752 relids = lappend_oid(relids, childrelid);
1754 /* Log this relation only if needed for logical decoding */
1755 if (RelationIsLogicallyLogged(rel))
1756 relids_logged = lappend_oid(relids_logged, childrelid);
1759 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1760 ereport(ERROR,
1761 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1762 errmsg("cannot truncate only a partitioned table"),
1763 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1766 ExecuteTruncateGuts(rels, relids, relids_logged,
1767 stmt->behavior, stmt->restart_seqs, false);
1769 /* And close the rels */
1770 foreach(cell, rels)
1772 Relation rel = (Relation) lfirst(cell);
1774 table_close(rel, NoLock);
1779 * ExecuteTruncateGuts
1781 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1782 * command (see above) as well as replication subscribers that execute a
1783 * replicated TRUNCATE action.
1785 * explicit_rels is the list of Relations to truncate that the command
1786 * specified. relids is the list of Oids corresponding to explicit_rels.
1787 * relids_logged is the list of Oids (a subset of relids) that require
1788 * WAL-logging. This is all a bit redundant, but the existing callers have
1789 * this information handy in this form.
1791 void
1792 ExecuteTruncateGuts(List *explicit_rels,
1793 List *relids,
1794 List *relids_logged,
1795 DropBehavior behavior, bool restart_seqs,
1796 bool run_as_table_owner)
1798 List *rels;
1799 List *seq_relids = NIL;
1800 HTAB *ft_htab = NULL;
1801 EState *estate;
1802 ResultRelInfo *resultRelInfos;
1803 ResultRelInfo *resultRelInfo;
1804 SubTransactionId mySubid;
1805 ListCell *cell;
1806 Oid *logrelids;
1809 * Check the explicitly-specified relations.
1811 * In CASCADE mode, suck in all referencing relations as well. This
1812 * requires multiple iterations to find indirectly-dependent relations. At
1813 * each phase, we need to exclusive-lock new rels before looking for their
1814 * dependencies, else we might miss something. Also, we check each rel as
1815 * soon as we open it, to avoid a faux pas such as holding lock for a long
1816 * time on a rel we have no permissions for.
1818 rels = list_copy(explicit_rels);
1819 if (behavior == DROP_CASCADE)
1821 for (;;)
1823 List *newrelids;
1825 newrelids = heap_truncate_find_FKs(relids);
1826 if (newrelids == NIL)
1827 break; /* nothing else to add */
1829 foreach(cell, newrelids)
1831 Oid relid = lfirst_oid(cell);
1832 Relation rel;
1834 rel = table_open(relid, AccessExclusiveLock);
1835 ereport(NOTICE,
1836 (errmsg("truncate cascades to table \"%s\"",
1837 RelationGetRelationName(rel))));
1838 truncate_check_rel(relid, rel->rd_rel);
1839 truncate_check_perms(relid, rel->rd_rel);
1840 truncate_check_activity(rel);
1841 rels = lappend(rels, rel);
1842 relids = lappend_oid(relids, relid);
1844 /* Log this relation only if needed for logical decoding */
1845 if (RelationIsLogicallyLogged(rel))
1846 relids_logged = lappend_oid(relids_logged, relid);
1852 * Check foreign key references. In CASCADE mode, this should be
1853 * unnecessary since we just pulled in all the references; but as a
1854 * cross-check, do it anyway if in an Assert-enabled build.
1856 #ifdef USE_ASSERT_CHECKING
1857 heap_truncate_check_FKs(rels, false);
1858 #else
1859 if (behavior == DROP_RESTRICT)
1860 heap_truncate_check_FKs(rels, false);
1861 #endif
1864 * If we are asked to restart sequences, find all the sequences, lock them
1865 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1866 * We want to do this early since it's pointless to do all the truncation
1867 * work only to fail on sequence permissions.
1869 if (restart_seqs)
1871 foreach(cell, rels)
1873 Relation rel = (Relation) lfirst(cell);
1874 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1875 ListCell *seqcell;
1877 foreach(seqcell, seqlist)
1879 Oid seq_relid = lfirst_oid(seqcell);
1880 Relation seq_rel;
1882 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1884 /* This check must match AlterSequence! */
1885 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
1886 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1887 RelationGetRelationName(seq_rel));
1889 seq_relids = lappend_oid(seq_relids, seq_relid);
1891 relation_close(seq_rel, NoLock);
1896 /* Prepare to catch AFTER triggers. */
1897 AfterTriggerBeginQuery();
1900 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1901 * each relation. We don't need to call ExecOpenIndices, though.
1903 * We put the ResultRelInfos in the es_opened_result_relations list, even
1904 * though we don't have a range table and don't populate the
1905 * es_result_relations array. That's a bit bogus, but it's enough to make
1906 * ExecGetTriggerResultRel() find them.
1908 estate = CreateExecutorState();
1909 resultRelInfos = (ResultRelInfo *)
1910 palloc(list_length(rels) * sizeof(ResultRelInfo));
1911 resultRelInfo = resultRelInfos;
1912 foreach(cell, rels)
1914 Relation rel = (Relation) lfirst(cell);
1916 InitResultRelInfo(resultRelInfo,
1917 rel,
1918 0, /* dummy rangetable index */
1919 NULL,
1921 estate->es_opened_result_relations =
1922 lappend(estate->es_opened_result_relations, resultRelInfo);
1923 resultRelInfo++;
1927 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
1928 * truncating (this is because one of them might throw an error). Also, if
1929 * we were to allow them to prevent statement execution, that would need
1930 * to be handled here.
1932 resultRelInfo = resultRelInfos;
1933 foreach(cell, rels)
1935 UserContext ucxt;
1937 if (run_as_table_owner)
1938 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
1939 &ucxt);
1940 ExecBSTruncateTriggers(estate, resultRelInfo);
1941 if (run_as_table_owner)
1942 RestoreUserContext(&ucxt);
1943 resultRelInfo++;
1947 * OK, truncate each table.
1949 mySubid = GetCurrentSubTransactionId();
1951 foreach(cell, rels)
1953 Relation rel = (Relation) lfirst(cell);
1955 /* Skip partitioned tables as there is nothing to do */
1956 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1957 continue;
1960 * Build the lists of foreign tables belonging to each foreign server
1961 * and pass each list to the foreign data wrapper's callback function,
1962 * so that each server can truncate its all foreign tables in bulk.
1963 * Each list is saved as a single entry in a hash table that uses the
1964 * server OID as lookup key.
1966 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1968 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
1969 bool found;
1970 ForeignTruncateInfo *ft_info;
1972 /* First time through, initialize hashtable for foreign tables */
1973 if (!ft_htab)
1975 HASHCTL hctl;
1977 memset(&hctl, 0, sizeof(HASHCTL));
1978 hctl.keysize = sizeof(Oid);
1979 hctl.entrysize = sizeof(ForeignTruncateInfo);
1980 hctl.hcxt = CurrentMemoryContext;
1982 ft_htab = hash_create("TRUNCATE for Foreign Tables",
1983 32, /* start small and extend */
1984 &hctl,
1985 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
1988 /* Find or create cached entry for the foreign table */
1989 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
1990 if (!found)
1992 ft_info->serverid = serverid;
1993 ft_info->rels = NIL;
1997 * Save the foreign table in the entry of the server that the
1998 * foreign table belongs to.
2000 ft_info->rels = lappend(ft_info->rels, rel);
2001 continue;
2005 * Normally, we need a transaction-safe truncation here. However, if
2006 * the table was either created in the current (sub)transaction or has
2007 * a new relfilenumber in the current (sub)transaction, then we can
2008 * just truncate it in-place, because a rollback would cause the whole
2009 * table or the current physical file to be thrown away anyway.
2011 if (rel->rd_createSubid == mySubid ||
2012 rel->rd_newRelfilelocatorSubid == mySubid)
2014 /* Immediate, non-rollbackable truncation is OK */
2015 heap_truncate_one_rel(rel);
2017 else
2019 Oid heap_relid;
2020 Oid toast_relid;
2021 ReindexParams reindex_params = {0};
2024 * This effectively deletes all rows in the table, and may be done
2025 * in a serializable transaction. In that case we must record a
2026 * rw-conflict in to this transaction from each transaction
2027 * holding a predicate lock on the table.
2029 CheckTableForSerializableConflictIn(rel);
2032 * Need the full transaction-safe pushups.
2034 * Create a new empty storage file for the relation, and assign it
2035 * as the relfilenumber value. The old storage file is scheduled
2036 * for deletion at commit.
2038 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2040 heap_relid = RelationGetRelid(rel);
2043 * The same for the toast table, if any.
2045 toast_relid = rel->rd_rel->reltoastrelid;
2046 if (OidIsValid(toast_relid))
2048 Relation toastrel = relation_open(toast_relid,
2049 AccessExclusiveLock);
2051 RelationSetNewRelfilenumber(toastrel,
2052 toastrel->rd_rel->relpersistence);
2053 table_close(toastrel, NoLock);
2057 * Reconstruct the indexes to match, and we're done.
2059 reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST,
2060 &reindex_params);
2063 pgstat_count_truncate(rel);
2066 /* Now go through the hash table, and truncate foreign tables */
2067 if (ft_htab)
2069 ForeignTruncateInfo *ft_info;
2070 HASH_SEQ_STATUS seq;
2072 hash_seq_init(&seq, ft_htab);
2074 PG_TRY();
2076 while ((ft_info = hash_seq_search(&seq)) != NULL)
2078 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2080 /* truncate_check_rel() has checked that already */
2081 Assert(routine->ExecForeignTruncate != NULL);
2083 routine->ExecForeignTruncate(ft_info->rels,
2084 behavior,
2085 restart_seqs);
2088 PG_FINALLY();
2090 hash_destroy(ft_htab);
2092 PG_END_TRY();
2096 * Restart owned sequences if we were asked to.
2098 foreach(cell, seq_relids)
2100 Oid seq_relid = lfirst_oid(cell);
2102 ResetSequence(seq_relid);
2106 * Write a WAL record to allow this set of actions to be logically
2107 * decoded.
2109 * Assemble an array of relids so we can write a single WAL record for the
2110 * whole action.
2112 if (relids_logged != NIL)
2114 xl_heap_truncate xlrec;
2115 int i = 0;
2117 /* should only get here if wal_level >= logical */
2118 Assert(XLogLogicalInfoActive());
2120 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2121 foreach(cell, relids_logged)
2122 logrelids[i++] = lfirst_oid(cell);
2124 xlrec.dbId = MyDatabaseId;
2125 xlrec.nrelids = list_length(relids_logged);
2126 xlrec.flags = 0;
2127 if (behavior == DROP_CASCADE)
2128 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2129 if (restart_seqs)
2130 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2132 XLogBeginInsert();
2133 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2134 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2136 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2138 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2142 * Process all AFTER STATEMENT TRUNCATE triggers.
2144 resultRelInfo = resultRelInfos;
2145 foreach(cell, rels)
2147 UserContext ucxt;
2149 if (run_as_table_owner)
2150 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2151 &ucxt);
2152 ExecASTruncateTriggers(estate, resultRelInfo);
2153 if (run_as_table_owner)
2154 RestoreUserContext(&ucxt);
2155 resultRelInfo++;
2158 /* Handle queued AFTER triggers */
2159 AfterTriggerEndQuery(estate);
2161 /* We can clean up the EState now */
2162 FreeExecutorState(estate);
2165 * Close any rels opened by CASCADE (can't do this while EState still
2166 * holds refs)
2168 rels = list_difference_ptr(rels, explicit_rels);
2169 foreach(cell, rels)
2171 Relation rel = (Relation) lfirst(cell);
2173 table_close(rel, NoLock);
2178 * Check that a given relation is safe to truncate. Subroutine for
2179 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2181 static void
2182 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2184 char *relname = NameStr(reltuple->relname);
2187 * Only allow truncate on regular tables, foreign tables using foreign
2188 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2189 * latter are only being included here for the following checks; no
2190 * physical truncation will occur in their case.).
2192 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2194 Oid serverid = GetForeignServerIdByRelId(relid);
2195 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2197 if (!fdwroutine->ExecForeignTruncate)
2198 ereport(ERROR,
2199 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2200 errmsg("cannot truncate foreign table \"%s\"",
2201 relname)));
2203 else if (reltuple->relkind != RELKIND_RELATION &&
2204 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2205 ereport(ERROR,
2206 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2207 errmsg("\"%s\" is not a table", relname)));
2210 * Most system catalogs can't be truncated at all, or at least not unless
2211 * allow_system_table_mods=on. As an exception, however, we allow
2212 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2213 * to change its relfilenode to match the old cluster, and allowing a
2214 * TRUNCATE command to be executed is the easiest way of doing that.
2216 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2217 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2218 ereport(ERROR,
2219 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2220 errmsg("permission denied: \"%s\" is a system catalog",
2221 relname)));
2223 InvokeObjectTruncateHook(relid);
2227 * Check that current user has the permission to truncate given relation.
2229 static void
2230 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2232 char *relname = NameStr(reltuple->relname);
2233 AclResult aclresult;
2235 /* Permissions checks */
2236 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2237 if (aclresult != ACLCHECK_OK)
2238 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2239 relname);
2243 * Set of extra sanity checks to check if a given relation is safe to
2244 * truncate. This is split with truncate_check_rel() as
2245 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2247 static void
2248 truncate_check_activity(Relation rel)
2251 * Don't allow truncate on temp tables of other backends ... their local
2252 * buffer manager is not going to cope.
2254 if (RELATION_IS_OTHER_TEMP(rel))
2255 ereport(ERROR,
2256 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2257 errmsg("cannot truncate temporary tables of other sessions")));
2260 * Also check for active uses of the relation in the current transaction,
2261 * including open scans and pending AFTER trigger events.
2263 CheckTableNotInUse(rel, "TRUNCATE");
2267 * storage_name
2268 * returns the name corresponding to a typstorage/attstorage enum value
2270 static const char *
2271 storage_name(char c)
2273 switch (c)
2275 case TYPSTORAGE_PLAIN:
2276 return "PLAIN";
2277 case TYPSTORAGE_EXTERNAL:
2278 return "EXTERNAL";
2279 case TYPSTORAGE_EXTENDED:
2280 return "EXTENDED";
2281 case TYPSTORAGE_MAIN:
2282 return "MAIN";
2283 default:
2284 return "???";
2288 /*----------
2289 * MergeAttributes
2290 * Returns new schema given initial schema and superclasses.
2292 * Input arguments:
2293 * 'schema' is the column/attribute definition for the table. (It's a list
2294 * of ColumnDef's.) It is destructively changed.
2295 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2296 * 'relpersistence' is the persistence type of the table.
2297 * 'is_partition' tells if the table is a partition.
2299 * Output arguments:
2300 * 'supconstr' receives a list of constraints belonging to the parents,
2301 * updated as necessary to be valid for the child.
2303 * Return value:
2304 * Completed schema list.
2306 * Notes:
2307 * The order in which the attributes are inherited is very important.
2308 * Intuitively, the inherited attributes should come first. If a table
2309 * inherits from multiple parents, the order of those attributes are
2310 * according to the order of the parents specified in CREATE TABLE.
2312 * Here's an example:
2314 * create table person (name text, age int4, location point);
2315 * create table emp (salary int4, manager text) inherits(person);
2316 * create table student (gpa float8) inherits (person);
2317 * create table stud_emp (percent int4) inherits (emp, student);
2319 * The order of the attributes of stud_emp is:
2321 * person {1:name, 2:age, 3:location}
2322 * / \
2323 * {6:gpa} student emp {4:salary, 5:manager}
2324 * \ /
2325 * stud_emp {7:percent}
2327 * If the same attribute name appears multiple times, then it appears
2328 * in the result table in the proper location for its first appearance.
2330 * Constraints (including NOT NULL constraints) for the child table
2331 * are the union of all relevant constraints, from both the child schema
2332 * and parent tables.
2334 * The default value for a child column is defined as:
2335 * (1) If the child schema specifies a default, that value is used.
2336 * (2) If neither the child nor any parent specifies a default, then
2337 * the column will not have a default.
2338 * (3) If conflicting defaults are inherited from different parents
2339 * (and not overridden by the child), an error is raised.
2340 * (4) Otherwise the inherited default is used.
2342 * Note that the default-value infrastructure is used for generated
2343 * columns' expressions too, so most of the preceding paragraph applies
2344 * to generation expressions too. We insist that a child column be
2345 * generated if and only if its parent(s) are, but it need not have
2346 * the same generation expression.
2347 *----------
2349 static List *
2350 MergeAttributes(List *schema, List *supers, char relpersistence,
2351 bool is_partition, List **supconstr)
2353 List *inhSchema = NIL;
2354 List *constraints = NIL;
2355 bool have_bogus_defaults = false;
2356 int child_attno;
2357 static Node bogus_marker = {0}; /* marks conflicting defaults */
2358 List *saved_schema = NIL;
2359 ListCell *entry;
2362 * Check for and reject tables with too many columns. We perform this
2363 * check relatively early for two reasons: (a) we don't run the risk of
2364 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2365 * okay if we're processing <= 1600 columns, but could take minutes to
2366 * execute if the user attempts to create a table with hundreds of
2367 * thousands of columns.
2369 * Note that we also need to check that we do not exceed this figure after
2370 * including columns from inherited relations.
2372 if (list_length(schema) > MaxHeapAttributeNumber)
2373 ereport(ERROR,
2374 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2375 errmsg("tables can have at most %d columns",
2376 MaxHeapAttributeNumber)));
2379 * Check for duplicate names in the explicit list of attributes.
2381 * Although we might consider merging such entries in the same way that we
2382 * handle name conflicts for inherited attributes, it seems to make more
2383 * sense to assume such conflicts are errors.
2385 * We don't use foreach() here because we have two nested loops over the
2386 * schema list, with possible element deletions in the inner one. If we
2387 * used foreach_delete_current() it could only fix up the state of one of
2388 * the loops, so it seems cleaner to use looping over list indexes for
2389 * both loops. Note that any deletion will happen beyond where the outer
2390 * loop is, so its index never needs adjustment.
2392 for (int coldefpos = 0; coldefpos < list_length(schema); coldefpos++)
2394 ColumnDef *coldef = list_nth_node(ColumnDef, schema, coldefpos);
2396 if (!is_partition && coldef->typeName == NULL)
2399 * Typed table column option that does not belong to a column from
2400 * the type. This works because the columns from the type come
2401 * first in the list. (We omit this check for partition column
2402 * lists; those are processed separately below.)
2404 ereport(ERROR,
2405 (errcode(ERRCODE_UNDEFINED_COLUMN),
2406 errmsg("column \"%s\" does not exist",
2407 coldef->colname)));
2410 /* restpos scans all entries beyond coldef; incr is in loop body */
2411 for (int restpos = coldefpos + 1; restpos < list_length(schema);)
2413 ColumnDef *restdef = list_nth_node(ColumnDef, schema, restpos);
2415 if (strcmp(coldef->colname, restdef->colname) == 0)
2417 if (coldef->is_from_type)
2420 * merge the column options into the column from the type
2422 coldef->is_not_null = restdef->is_not_null;
2423 coldef->raw_default = restdef->raw_default;
2424 coldef->cooked_default = restdef->cooked_default;
2425 coldef->constraints = restdef->constraints;
2426 coldef->is_from_type = false;
2427 schema = list_delete_nth_cell(schema, restpos);
2429 else
2430 ereport(ERROR,
2431 (errcode(ERRCODE_DUPLICATE_COLUMN),
2432 errmsg("column \"%s\" specified more than once",
2433 coldef->colname)));
2435 else
2436 restpos++;
2441 * In case of a partition, there are no new column definitions, only dummy
2442 * ColumnDefs created for column constraints. Set them aside for now and
2443 * process them at the end.
2445 if (is_partition)
2447 saved_schema = schema;
2448 schema = NIL;
2452 * Scan the parents left-to-right, and merge their attributes to form a
2453 * list of inherited attributes (inhSchema).
2455 child_attno = 0;
2456 foreach(entry, supers)
2458 Oid parent = lfirst_oid(entry);
2459 Relation relation;
2460 TupleDesc tupleDesc;
2461 TupleConstr *constr;
2462 AttrMap *newattmap;
2463 List *inherited_defaults;
2464 List *cols_with_defaults;
2465 AttrNumber parent_attno;
2466 ListCell *lc1;
2467 ListCell *lc2;
2469 /* caller already got lock */
2470 relation = table_open(parent, NoLock);
2473 * Check for active uses of the parent partitioned table in the
2474 * current transaction, such as being used in some manner by an
2475 * enclosing command.
2477 if (is_partition)
2478 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2481 * We do not allow partitioned tables and partitions to participate in
2482 * regular inheritance.
2484 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
2485 !is_partition)
2486 ereport(ERROR,
2487 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2488 errmsg("cannot inherit from partitioned table \"%s\"",
2489 RelationGetRelationName(relation))));
2490 if (relation->rd_rel->relispartition && !is_partition)
2491 ereport(ERROR,
2492 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2493 errmsg("cannot inherit from partition \"%s\"",
2494 RelationGetRelationName(relation))));
2496 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2497 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2498 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2499 ereport(ERROR,
2500 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2501 errmsg("inherited relation \"%s\" is not a table or foreign table",
2502 RelationGetRelationName(relation))));
2505 * If the parent is permanent, so must be all of its partitions. Note
2506 * that inheritance allows that case.
2508 if (is_partition &&
2509 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2510 relpersistence == RELPERSISTENCE_TEMP)
2511 ereport(ERROR,
2512 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2513 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2514 RelationGetRelationName(relation))));
2516 /* Permanent rels cannot inherit from temporary ones */
2517 if (relpersistence != RELPERSISTENCE_TEMP &&
2518 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2519 ereport(ERROR,
2520 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2521 errmsg(!is_partition
2522 ? "cannot inherit from temporary relation \"%s\""
2523 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2524 RelationGetRelationName(relation))));
2526 /* If existing rel is temp, it must belong to this session */
2527 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2528 !relation->rd_islocaltemp)
2529 ereport(ERROR,
2530 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2531 errmsg(!is_partition
2532 ? "cannot inherit from temporary relation of another session"
2533 : "cannot create as partition of temporary relation of another session")));
2536 * We should have an UNDER permission flag for this, but for now,
2537 * demand that creator of a child table own the parent.
2539 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2540 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2541 RelationGetRelationName(relation));
2543 tupleDesc = RelationGetDescr(relation);
2544 constr = tupleDesc->constr;
2547 * newattmap->attnums[] will contain the child-table attribute numbers
2548 * for the attributes of this parent table. (They are not the same
2549 * for parents after the first one, nor if we have dropped columns.)
2551 newattmap = make_attrmap(tupleDesc->natts);
2553 /* We can't process inherited defaults until newattmap is complete. */
2554 inherited_defaults = cols_with_defaults = NIL;
2556 for (parent_attno = 1; parent_attno <= tupleDesc->natts;
2557 parent_attno++)
2559 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2560 parent_attno - 1);
2561 char *attributeName = NameStr(attribute->attname);
2562 int exist_attno;
2563 ColumnDef *def;
2566 * Ignore dropped columns in the parent.
2568 if (attribute->attisdropped)
2569 continue; /* leave newattmap->attnums entry as zero */
2572 * Does it conflict with some previously inherited column?
2574 exist_attno = findAttrByName(attributeName, inhSchema);
2575 if (exist_attno > 0)
2577 Oid defTypeId;
2578 int32 deftypmod;
2579 Oid defCollId;
2582 * Yes, try to merge the two column definitions.
2584 ereport(NOTICE,
2585 (errmsg("merging multiple inherited definitions of column \"%s\"",
2586 attributeName)));
2587 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2590 * Must have the same type and typmod
2592 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2593 if (defTypeId != attribute->atttypid ||
2594 deftypmod != attribute->atttypmod)
2595 ereport(ERROR,
2596 (errcode(ERRCODE_DATATYPE_MISMATCH),
2597 errmsg("inherited column \"%s\" has a type conflict",
2598 attributeName),
2599 errdetail("%s versus %s",
2600 format_type_with_typemod(defTypeId,
2601 deftypmod),
2602 format_type_with_typemod(attribute->atttypid,
2603 attribute->atttypmod))));
2606 * Must have the same collation
2608 defCollId = GetColumnDefCollation(NULL, def, defTypeId);
2609 if (defCollId != attribute->attcollation)
2610 ereport(ERROR,
2611 (errcode(ERRCODE_COLLATION_MISMATCH),
2612 errmsg("inherited column \"%s\" has a collation conflict",
2613 attributeName),
2614 errdetail("\"%s\" versus \"%s\"",
2615 get_collation_name(defCollId),
2616 get_collation_name(attribute->attcollation))));
2619 * Copy/check storage parameter
2621 if (def->storage == 0)
2622 def->storage = attribute->attstorage;
2623 else if (def->storage != attribute->attstorage)
2624 ereport(ERROR,
2625 (errcode(ERRCODE_DATATYPE_MISMATCH),
2626 errmsg("inherited column \"%s\" has a storage parameter conflict",
2627 attributeName),
2628 errdetail("%s versus %s",
2629 storage_name(def->storage),
2630 storage_name(attribute->attstorage))));
2633 * Copy/check compression parameter
2635 if (CompressionMethodIsValid(attribute->attcompression))
2637 const char *compression =
2638 GetCompressionMethodName(attribute->attcompression);
2640 if (def->compression == NULL)
2641 def->compression = pstrdup(compression);
2642 else if (strcmp(def->compression, compression) != 0)
2643 ereport(ERROR,
2644 (errcode(ERRCODE_DATATYPE_MISMATCH),
2645 errmsg("column \"%s\" has a compression method conflict",
2646 attributeName),
2647 errdetail("%s versus %s", def->compression, compression)));
2651 * Merge of NOT NULL constraints = OR 'em together
2653 def->is_not_null |= attribute->attnotnull;
2656 * Check for GENERATED conflicts
2658 if (def->generated != attribute->attgenerated)
2659 ereport(ERROR,
2660 (errcode(ERRCODE_DATATYPE_MISMATCH),
2661 errmsg("inherited column \"%s\" has a generation conflict",
2662 attributeName)));
2665 * Default and other constraints are handled below
2668 def->inhcount++;
2669 if (def->inhcount < 0)
2670 ereport(ERROR,
2671 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
2672 errmsg("too many inheritance parents"));
2674 newattmap->attnums[parent_attno - 1] = exist_attno;
2676 else
2679 * No, create a new inherited column
2681 def = makeNode(ColumnDef);
2682 def->colname = pstrdup(attributeName);
2683 def->typeName = makeTypeNameFromOid(attribute->atttypid,
2684 attribute->atttypmod);
2685 def->inhcount = 1;
2686 def->is_local = false;
2687 def->is_not_null = attribute->attnotnull;
2688 def->is_from_type = false;
2689 def->storage = attribute->attstorage;
2690 def->raw_default = NULL;
2691 def->cooked_default = NULL;
2692 def->generated = attribute->attgenerated;
2693 def->collClause = NULL;
2694 def->collOid = attribute->attcollation;
2695 def->constraints = NIL;
2696 def->location = -1;
2697 if (CompressionMethodIsValid(attribute->attcompression))
2698 def->compression =
2699 pstrdup(GetCompressionMethodName(attribute->attcompression));
2700 else
2701 def->compression = NULL;
2702 inhSchema = lappend(inhSchema, def);
2703 newattmap->attnums[parent_attno - 1] = ++child_attno;
2707 * Locate default/generation expression if any
2709 if (attribute->atthasdef)
2711 Node *this_default = NULL;
2713 /* Find default in constraint structure */
2714 if (constr != NULL)
2716 AttrDefault *attrdef = constr->defval;
2718 for (int i = 0; i < constr->num_defval; i++)
2720 if (attrdef[i].adnum == parent_attno)
2722 this_default = stringToNode(attrdef[i].adbin);
2723 break;
2727 if (this_default == NULL)
2728 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2729 parent_attno, RelationGetRelationName(relation));
2732 * If it's a GENERATED default, it might contain Vars that
2733 * need to be mapped to the inherited column(s)' new numbers.
2734 * We can't do that till newattmap is ready, so just remember
2735 * all the inherited default expressions for the moment.
2737 inherited_defaults = lappend(inherited_defaults, this_default);
2738 cols_with_defaults = lappend(cols_with_defaults, def);
2743 * Now process any inherited default expressions, adjusting attnos
2744 * using the completed newattmap map.
2746 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2748 Node *this_default = (Node *) lfirst(lc1);
2749 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2750 bool found_whole_row;
2752 /* Adjust Vars to match new table's column numbering */
2753 this_default = map_variable_attnos(this_default,
2754 1, 0,
2755 newattmap,
2756 InvalidOid, &found_whole_row);
2759 * For the moment we have to reject whole-row variables. We could
2760 * convert them, if we knew the new table's rowtype OID, but that
2761 * hasn't been assigned yet. (A variable could only appear in a
2762 * generation expression, so the error message is correct.)
2764 if (found_whole_row)
2765 ereport(ERROR,
2766 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2767 errmsg("cannot convert whole-row table reference"),
2768 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2769 def->colname,
2770 RelationGetRelationName(relation))));
2773 * If we already had a default from some prior parent, check to
2774 * see if they are the same. If so, no problem; if not, mark the
2775 * column as having a bogus default. Below, we will complain if
2776 * the bogus default isn't overridden by the child schema.
2778 Assert(def->raw_default == NULL);
2779 if (def->cooked_default == NULL)
2780 def->cooked_default = this_default;
2781 else if (!equal(def->cooked_default, this_default))
2783 def->cooked_default = &bogus_marker;
2784 have_bogus_defaults = true;
2789 * Now copy the CHECK constraints of this parent, adjusting attnos
2790 * using the completed newattmap map. Identically named constraints
2791 * are merged if possible, else we throw error.
2793 if (constr && constr->num_check > 0)
2795 ConstrCheck *check = constr->check;
2796 int i;
2798 for (i = 0; i < constr->num_check; i++)
2800 char *name = check[i].ccname;
2801 Node *expr;
2802 bool found_whole_row;
2804 /* ignore if the constraint is non-inheritable */
2805 if (check[i].ccnoinherit)
2806 continue;
2808 /* Adjust Vars to match new table's column numbering */
2809 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2810 1, 0,
2811 newattmap,
2812 InvalidOid, &found_whole_row);
2815 * For the moment we have to reject whole-row variables. We
2816 * could convert them, if we knew the new table's rowtype OID,
2817 * but that hasn't been assigned yet.
2819 if (found_whole_row)
2820 ereport(ERROR,
2821 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2822 errmsg("cannot convert whole-row table reference"),
2823 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2824 name,
2825 RelationGetRelationName(relation))));
2827 /* check for duplicate */
2828 if (!MergeCheckConstraint(constraints, name, expr))
2830 /* nope, this is a new one */
2831 CookedConstraint *cooked;
2833 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
2834 cooked->contype = CONSTR_CHECK;
2835 cooked->conoid = InvalidOid; /* until created */
2836 cooked->name = pstrdup(name);
2837 cooked->attnum = 0; /* not used for constraints */
2838 cooked->expr = expr;
2839 cooked->skip_validation = false;
2840 cooked->is_local = false;
2841 cooked->inhcount = 1;
2842 cooked->is_no_inherit = false;
2843 constraints = lappend(constraints, cooked);
2848 free_attrmap(newattmap);
2851 * Close the parent rel, but keep our lock on it until xact commit.
2852 * That will prevent someone else from deleting or ALTERing the parent
2853 * before the child is committed.
2855 table_close(relation, NoLock);
2859 * If we had no inherited attributes, the result schema is just the
2860 * explicitly declared columns. Otherwise, we need to merge the declared
2861 * columns into the inherited schema list. Although, we never have any
2862 * explicitly declared columns if the table is a partition.
2864 if (inhSchema != NIL)
2866 int schema_attno = 0;
2868 foreach(entry, schema)
2870 ColumnDef *newdef = lfirst(entry);
2871 char *attributeName = newdef->colname;
2872 int exist_attno;
2874 schema_attno++;
2877 * Does it conflict with some previously inherited column?
2879 exist_attno = findAttrByName(attributeName, inhSchema);
2880 if (exist_attno > 0)
2882 ColumnDef *def;
2883 Oid defTypeId,
2884 newTypeId;
2885 int32 deftypmod,
2886 newtypmod;
2887 Oid defcollid,
2888 newcollid;
2891 * Partitions have only one parent and have no column
2892 * definitions of their own, so conflict should never occur.
2894 Assert(!is_partition);
2897 * Yes, try to merge the two column definitions.
2899 if (exist_attno == schema_attno)
2900 ereport(NOTICE,
2901 (errmsg("merging column \"%s\" with inherited definition",
2902 attributeName)));
2903 else
2904 ereport(NOTICE,
2905 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
2906 errdetail("User-specified column moved to the position of the inherited column.")));
2907 def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
2910 * Must have the same type and typmod
2912 typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
2913 typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
2914 if (defTypeId != newTypeId || deftypmod != newtypmod)
2915 ereport(ERROR,
2916 (errcode(ERRCODE_DATATYPE_MISMATCH),
2917 errmsg("column \"%s\" has a type conflict",
2918 attributeName),
2919 errdetail("%s versus %s",
2920 format_type_with_typemod(defTypeId,
2921 deftypmod),
2922 format_type_with_typemod(newTypeId,
2923 newtypmod))));
2926 * Must have the same collation
2928 defcollid = GetColumnDefCollation(NULL, def, defTypeId);
2929 newcollid = GetColumnDefCollation(NULL, newdef, newTypeId);
2930 if (defcollid != newcollid)
2931 ereport(ERROR,
2932 (errcode(ERRCODE_COLLATION_MISMATCH),
2933 errmsg("column \"%s\" has a collation conflict",
2934 attributeName),
2935 errdetail("\"%s\" versus \"%s\"",
2936 get_collation_name(defcollid),
2937 get_collation_name(newcollid))));
2940 * Identity is never inherited. The new column can have an
2941 * identity definition, so we always just take that one.
2943 def->identity = newdef->identity;
2946 * Copy storage parameter
2948 if (def->storage == 0)
2949 def->storage = newdef->storage;
2950 else if (newdef->storage != 0 && def->storage != newdef->storage)
2951 ereport(ERROR,
2952 (errcode(ERRCODE_DATATYPE_MISMATCH),
2953 errmsg("column \"%s\" has a storage parameter conflict",
2954 attributeName),
2955 errdetail("%s versus %s",
2956 storage_name(def->storage),
2957 storage_name(newdef->storage))));
2960 * Copy compression parameter
2962 if (def->compression == NULL)
2963 def->compression = newdef->compression;
2964 else if (newdef->compression != NULL)
2966 if (strcmp(def->compression, newdef->compression) != 0)
2967 ereport(ERROR,
2968 (errcode(ERRCODE_DATATYPE_MISMATCH),
2969 errmsg("column \"%s\" has a compression method conflict",
2970 attributeName),
2971 errdetail("%s versus %s", def->compression, newdef->compression)));
2975 * Merge of NOT NULL constraints = OR 'em together
2977 def->is_not_null |= newdef->is_not_null;
2980 * Check for conflicts related to generated columns.
2982 * If the parent column is generated, the child column will be
2983 * made a generated column if it isn't already. If it is a
2984 * generated column, we'll take its generation expression in
2985 * preference to the parent's. We must check that the child
2986 * column doesn't specify a default value or identity, which
2987 * matches the rules for a single column in parse_utilcmd.c.
2989 * Conversely, if the parent column is not generated, the
2990 * child column can't be either. (We used to allow that, but
2991 * it results in being able to override the generation
2992 * expression via UPDATEs through the parent.)
2994 if (def->generated)
2996 if (newdef->raw_default && !newdef->generated)
2997 ereport(ERROR,
2998 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2999 errmsg("column \"%s\" inherits from generated column but specifies default",
3000 def->colname)));
3001 if (newdef->identity)
3002 ereport(ERROR,
3003 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3004 errmsg("column \"%s\" inherits from generated column but specifies identity",
3005 def->colname)));
3007 else
3009 if (newdef->generated)
3010 ereport(ERROR,
3011 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3012 errmsg("child column \"%s\" specifies generation expression",
3013 def->colname),
3014 errhint("A child table column cannot be generated unless its parent column is.")));
3018 * If new def has a default, override previous default
3020 if (newdef->raw_default != NULL)
3022 def->raw_default = newdef->raw_default;
3023 def->cooked_default = newdef->cooked_default;
3026 /* Mark the column as locally defined */
3027 def->is_local = true;
3029 else
3032 * No, attach new column to result schema
3034 inhSchema = lappend(inhSchema, newdef);
3038 schema = inhSchema;
3041 * Check that we haven't exceeded the legal # of columns after merging
3042 * in inherited columns.
3044 if (list_length(schema) > MaxHeapAttributeNumber)
3045 ereport(ERROR,
3046 (errcode(ERRCODE_TOO_MANY_COLUMNS),
3047 errmsg("tables can have at most %d columns",
3048 MaxHeapAttributeNumber)));
3052 * Now that we have the column definition list for a partition, we can
3053 * check whether the columns referenced in the column constraint specs
3054 * actually exist. Also, we merge parent's NOT NULL constraints and
3055 * defaults into each corresponding column definition.
3057 if (is_partition)
3059 foreach(entry, saved_schema)
3061 ColumnDef *restdef = lfirst(entry);
3062 bool found = false;
3063 ListCell *l;
3065 foreach(l, schema)
3067 ColumnDef *coldef = lfirst(l);
3069 if (strcmp(coldef->colname, restdef->colname) == 0)
3071 found = true;
3072 coldef->is_not_null |= restdef->is_not_null;
3075 * Check for conflicts related to generated columns.
3077 * Same rules as above: generated-ness has to match the
3078 * parent, but the contents of the generation expression
3079 * can be different.
3081 if (coldef->generated)
3083 if (restdef->raw_default && !restdef->generated)
3084 ereport(ERROR,
3085 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3086 errmsg("column \"%s\" inherits from generated column but specifies default",
3087 restdef->colname)));
3088 if (restdef->identity)
3089 ereport(ERROR,
3090 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3091 errmsg("column \"%s\" inherits from generated column but specifies identity",
3092 restdef->colname)));
3094 else
3096 if (restdef->generated)
3097 ereport(ERROR,
3098 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3099 errmsg("child column \"%s\" specifies generation expression",
3100 restdef->colname),
3101 errhint("A child table column cannot be generated unless its parent column is.")));
3105 * Override the parent's default value for this column
3106 * (coldef->cooked_default) with the partition's local
3107 * definition (restdef->raw_default), if there's one. It
3108 * should be physically impossible to get a cooked default
3109 * in the local definition or a raw default in the
3110 * inherited definition, but make sure they're nulls, for
3111 * future-proofing.
3113 Assert(restdef->cooked_default == NULL);
3114 Assert(coldef->raw_default == NULL);
3115 if (restdef->raw_default)
3117 coldef->raw_default = restdef->raw_default;
3118 coldef->cooked_default = NULL;
3123 /* complain for constraints on columns not in parent */
3124 if (!found)
3125 ereport(ERROR,
3126 (errcode(ERRCODE_UNDEFINED_COLUMN),
3127 errmsg("column \"%s\" does not exist",
3128 restdef->colname)));
3133 * If we found any conflicting parent default values, check to make sure
3134 * they were overridden by the child.
3136 if (have_bogus_defaults)
3138 foreach(entry, schema)
3140 ColumnDef *def = lfirst(entry);
3142 if (def->cooked_default == &bogus_marker)
3144 if (def->generated)
3145 ereport(ERROR,
3146 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3147 errmsg("column \"%s\" inherits conflicting generation expressions",
3148 def->colname),
3149 errhint("To resolve the conflict, specify a generation expression explicitly.")));
3150 else
3151 ereport(ERROR,
3152 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3153 errmsg("column \"%s\" inherits conflicting default values",
3154 def->colname),
3155 errhint("To resolve the conflict, specify a default explicitly.")));
3160 *supconstr = constraints;
3161 return schema;
3166 * MergeCheckConstraint
3167 * Try to merge an inherited CHECK constraint with previous ones
3169 * If we inherit identically-named constraints from multiple parents, we must
3170 * merge them, or throw an error if they don't have identical definitions.
3172 * constraints is a list of CookedConstraint structs for previous constraints.
3174 * Returns true if merged (constraint is a duplicate), or false if it's
3175 * got a so-far-unique name, or throws error if conflict.
3177 static bool
3178 MergeCheckConstraint(List *constraints, char *name, Node *expr)
3180 ListCell *lc;
3182 foreach(lc, constraints)
3184 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3186 Assert(ccon->contype == CONSTR_CHECK);
3188 /* Non-matching names never conflict */
3189 if (strcmp(ccon->name, name) != 0)
3190 continue;
3192 if (equal(expr, ccon->expr))
3194 /* OK to merge */
3195 ccon->inhcount++;
3196 if (ccon->inhcount < 0)
3197 ereport(ERROR,
3198 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3199 errmsg("too many inheritance parents"));
3200 return true;
3203 ereport(ERROR,
3204 (errcode(ERRCODE_DUPLICATE_OBJECT),
3205 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3206 name)));
3209 return false;
3214 * StoreCatalogInheritance
3215 * Updates the system catalogs with proper inheritance information.
3217 * supers is a list of the OIDs of the new relation's direct ancestors.
3219 static void
3220 StoreCatalogInheritance(Oid relationId, List *supers,
3221 bool child_is_partition)
3223 Relation relation;
3224 int32 seqNumber;
3225 ListCell *entry;
3228 * sanity checks
3230 Assert(OidIsValid(relationId));
3232 if (supers == NIL)
3233 return;
3236 * Store INHERITS information in pg_inherits using direct ancestors only.
3237 * Also enter dependencies on the direct ancestors, and make sure they are
3238 * marked with relhassubclass = true.
3240 * (Once upon a time, both direct and indirect ancestors were found here
3241 * and then entered into pg_ipl. Since that catalog doesn't exist
3242 * anymore, there's no need to look for indirect ancestors.)
3244 relation = table_open(InheritsRelationId, RowExclusiveLock);
3246 seqNumber = 1;
3247 foreach(entry, supers)
3249 Oid parentOid = lfirst_oid(entry);
3251 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3252 child_is_partition);
3253 seqNumber++;
3256 table_close(relation, RowExclusiveLock);
3260 * Make catalog entries showing relationId as being an inheritance child
3261 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3263 static void
3264 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3265 int32 seqNumber, Relation inhRelation,
3266 bool child_is_partition)
3268 ObjectAddress childobject,
3269 parentobject;
3271 /* store the pg_inherits row */
3272 StoreSingleInheritance(relationId, parentOid, seqNumber);
3275 * Store a dependency too
3277 parentobject.classId = RelationRelationId;
3278 parentobject.objectId = parentOid;
3279 parentobject.objectSubId = 0;
3280 childobject.classId = RelationRelationId;
3281 childobject.objectId = relationId;
3282 childobject.objectSubId = 0;
3284 recordDependencyOn(&childobject, &parentobject,
3285 child_dependency_type(child_is_partition));
3288 * Post creation hook of this inheritance. Since object_access_hook
3289 * doesn't take multiple object identifiers, we relay oid of parent
3290 * relation using auxiliary_id argument.
3292 InvokeObjectPostAlterHookArg(InheritsRelationId,
3293 relationId, 0,
3294 parentOid, false);
3297 * Mark the parent as having subclasses.
3299 SetRelationHasSubclass(parentOid, true);
3303 * Look for an existing schema entry with the given name.
3305 * Returns the index (starting with 1) if attribute already exists in schema,
3306 * 0 if it doesn't.
3308 static int
3309 findAttrByName(const char *attributeName, List *schema)
3311 ListCell *s;
3312 int i = 1;
3314 foreach(s, schema)
3316 ColumnDef *def = lfirst(s);
3318 if (strcmp(attributeName, def->colname) == 0)
3319 return i;
3321 i++;
3323 return 0;
3328 * SetRelationHasSubclass
3329 * Set the value of the relation's relhassubclass field in pg_class.
3331 * NOTE: caller must be holding an appropriate lock on the relation.
3332 * ShareUpdateExclusiveLock is sufficient.
3334 * NOTE: an important side-effect of this operation is that an SI invalidation
3335 * message is sent out to all backends --- including me --- causing plans
3336 * referencing the relation to be rebuilt with the new list of children.
3337 * This must happen even if we find that no change is needed in the pg_class
3338 * row.
3340 void
3341 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3343 Relation relationRelation;
3344 HeapTuple tuple;
3345 Form_pg_class classtuple;
3348 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3350 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3351 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3352 if (!HeapTupleIsValid(tuple))
3353 elog(ERROR, "cache lookup failed for relation %u", relationId);
3354 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3356 if (classtuple->relhassubclass != relhassubclass)
3358 classtuple->relhassubclass = relhassubclass;
3359 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3361 else
3363 /* no need to change tuple, but force relcache rebuild anyway */
3364 CacheInvalidateRelcacheByTuple(tuple);
3367 heap_freetuple(tuple);
3368 table_close(relationRelation, RowExclusiveLock);
3372 * CheckRelationTableSpaceMove
3373 * Check if relation can be moved to new tablespace.
3375 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3377 * Returns true if the relation can be moved to the new tablespace; raises
3378 * an error if it is not possible to do the move; returns false if the move
3379 * would have no effect.
3381 bool
3382 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3384 Oid oldTableSpaceId;
3387 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3388 * stored as 0.
3390 oldTableSpaceId = rel->rd_rel->reltablespace;
3391 if (newTableSpaceId == oldTableSpaceId ||
3392 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3393 return false;
3396 * We cannot support moving mapped relations into different tablespaces.
3397 * (In particular this eliminates all shared catalogs.)
3399 if (RelationIsMapped(rel))
3400 ereport(ERROR,
3401 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3402 errmsg("cannot move system relation \"%s\"",
3403 RelationGetRelationName(rel))));
3405 /* Cannot move a non-shared relation into pg_global */
3406 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3407 ereport(ERROR,
3408 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3409 errmsg("only shared relations can be placed in pg_global tablespace")));
3412 * Do not allow moving temp tables of other backends ... their local
3413 * buffer manager is not going to cope.
3415 if (RELATION_IS_OTHER_TEMP(rel))
3416 ereport(ERROR,
3417 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3418 errmsg("cannot move temporary tables of other sessions")));
3420 return true;
3424 * SetRelationTableSpace
3425 * Set new reltablespace and relfilenumber in pg_class entry.
3427 * newTableSpaceId is the new tablespace for the relation, and
3428 * newRelFilenumber its new filenumber. If newRelFilenumber is
3429 * InvalidRelFileNumber, this field is not updated.
3431 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3433 * The caller of this routine had better check if a relation can be
3434 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3435 * first, and is responsible for making the change visible with
3436 * CommandCounterIncrement().
3438 void
3439 SetRelationTableSpace(Relation rel,
3440 Oid newTableSpaceId,
3441 RelFileNumber newRelFilenumber)
3443 Relation pg_class;
3444 HeapTuple tuple;
3445 Form_pg_class rd_rel;
3446 Oid reloid = RelationGetRelid(rel);
3448 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3450 /* Get a modifiable copy of the relation's pg_class row. */
3451 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3453 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3454 if (!HeapTupleIsValid(tuple))
3455 elog(ERROR, "cache lookup failed for relation %u", reloid);
3456 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3458 /* Update the pg_class row. */
3459 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3460 InvalidOid : newTableSpaceId;
3461 if (RelFileNumberIsValid(newRelFilenumber))
3462 rd_rel->relfilenode = newRelFilenumber;
3463 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3466 * Record dependency on tablespace. This is only required for relations
3467 * that have no physical storage.
3469 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3470 changeDependencyOnTablespace(RelationRelationId, reloid,
3471 rd_rel->reltablespace);
3473 heap_freetuple(tuple);
3474 table_close(pg_class, RowExclusiveLock);
3478 * renameatt_check - basic sanity checks before attribute rename
3480 static void
3481 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3483 char relkind = classform->relkind;
3485 if (classform->reloftype && !recursing)
3486 ereport(ERROR,
3487 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3488 errmsg("cannot rename column of typed table")));
3491 * Renaming the columns of sequences or toast tables doesn't actually
3492 * break anything from the system's point of view, since internal
3493 * references are by attnum. But it doesn't seem right to allow users to
3494 * change names that are hardcoded into the system, hence the following
3495 * restriction.
3497 if (relkind != RELKIND_RELATION &&
3498 relkind != RELKIND_VIEW &&
3499 relkind != RELKIND_MATVIEW &&
3500 relkind != RELKIND_COMPOSITE_TYPE &&
3501 relkind != RELKIND_INDEX &&
3502 relkind != RELKIND_PARTITIONED_INDEX &&
3503 relkind != RELKIND_FOREIGN_TABLE &&
3504 relkind != RELKIND_PARTITIONED_TABLE)
3505 ereport(ERROR,
3506 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3507 errmsg("cannot rename columns of relation \"%s\"",
3508 NameStr(classform->relname)),
3509 errdetail_relkind_not_supported(relkind)));
3512 * permissions checking. only the owner of a class can change its schema.
3514 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3515 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3516 NameStr(classform->relname));
3517 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3518 ereport(ERROR,
3519 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3520 errmsg("permission denied: \"%s\" is a system catalog",
3521 NameStr(classform->relname))));
3525 * renameatt_internal - workhorse for renameatt
3527 * Return value is the attribute number in the 'myrelid' relation.
3529 static AttrNumber
3530 renameatt_internal(Oid myrelid,
3531 const char *oldattname,
3532 const char *newattname,
3533 bool recurse,
3534 bool recursing,
3535 int expected_parents,
3536 DropBehavior behavior)
3538 Relation targetrelation;
3539 Relation attrelation;
3540 HeapTuple atttup;
3541 Form_pg_attribute attform;
3542 AttrNumber attnum;
3545 * Grab an exclusive lock on the target table, which we will NOT release
3546 * until end of transaction.
3548 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3549 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3552 * if the 'recurse' flag is set then we are supposed to rename this
3553 * attribute in all classes that inherit from 'relname' (as well as in
3554 * 'relname').
3556 * any permissions or problems with duplicate attributes will cause the
3557 * whole transaction to abort, which is what we want -- all or nothing.
3559 if (recurse)
3561 List *child_oids,
3562 *child_numparents;
3563 ListCell *lo,
3564 *li;
3567 * we need the number of parents for each child so that the recursive
3568 * calls to renameatt() can determine whether there are any parents
3569 * outside the inheritance hierarchy being processed.
3571 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3572 &child_numparents);
3575 * find_all_inheritors does the recursive search of the inheritance
3576 * hierarchy, so all we have to do is process all of the relids in the
3577 * list that it returns.
3579 forboth(lo, child_oids, li, child_numparents)
3581 Oid childrelid = lfirst_oid(lo);
3582 int numparents = lfirst_int(li);
3584 if (childrelid == myrelid)
3585 continue;
3586 /* note we need not recurse again */
3587 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3590 else
3593 * If we are told not to recurse, there had better not be any child
3594 * tables; else the rename would put them out of step.
3596 * expected_parents will only be 0 if we are not already recursing.
3598 if (expected_parents == 0 &&
3599 find_inheritance_children(myrelid, NoLock) != NIL)
3600 ereport(ERROR,
3601 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3602 errmsg("inherited column \"%s\" must be renamed in child tables too",
3603 oldattname)));
3606 /* rename attributes in typed tables of composite type */
3607 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3609 List *child_oids;
3610 ListCell *lo;
3612 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3613 RelationGetRelationName(targetrelation),
3614 behavior);
3616 foreach(lo, child_oids)
3617 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3620 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3622 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3623 if (!HeapTupleIsValid(atttup))
3624 ereport(ERROR,
3625 (errcode(ERRCODE_UNDEFINED_COLUMN),
3626 errmsg("column \"%s\" does not exist",
3627 oldattname)));
3628 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3630 attnum = attform->attnum;
3631 if (attnum <= 0)
3632 ereport(ERROR,
3633 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3634 errmsg("cannot rename system column \"%s\"",
3635 oldattname)));
3638 * if the attribute is inherited, forbid the renaming. if this is a
3639 * top-level call to renameatt(), then expected_parents will be 0, so the
3640 * effect of this code will be to prohibit the renaming if the attribute
3641 * is inherited at all. if this is a recursive call to renameatt(),
3642 * expected_parents will be the number of parents the current relation has
3643 * within the inheritance hierarchy being processed, so we'll prohibit the
3644 * renaming only if there are additional parents from elsewhere.
3646 if (attform->attinhcount > expected_parents)
3647 ereport(ERROR,
3648 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3649 errmsg("cannot rename inherited column \"%s\"",
3650 oldattname)));
3652 /* new name should not already exist */
3653 (void) check_for_column_name_collision(targetrelation, newattname, false);
3655 /* apply the update */
3656 namestrcpy(&(attform->attname), newattname);
3658 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3660 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3662 heap_freetuple(atttup);
3664 table_close(attrelation, RowExclusiveLock);
3666 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3668 return attnum;
3672 * Perform permissions and integrity checks before acquiring a relation lock.
3674 static void
3675 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3676 void *arg)
3678 HeapTuple tuple;
3679 Form_pg_class form;
3681 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3682 if (!HeapTupleIsValid(tuple))
3683 return; /* concurrently dropped */
3684 form = (Form_pg_class) GETSTRUCT(tuple);
3685 renameatt_check(relid, form, false);
3686 ReleaseSysCache(tuple);
3690 * renameatt - changes the name of an attribute in a relation
3692 * The returned ObjectAddress is that of the renamed column.
3694 ObjectAddress
3695 renameatt(RenameStmt *stmt)
3697 Oid relid;
3698 AttrNumber attnum;
3699 ObjectAddress address;
3701 /* lock level taken here should match renameatt_internal */
3702 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3703 stmt->missing_ok ? RVR_MISSING_OK : 0,
3704 RangeVarCallbackForRenameAttribute,
3705 NULL);
3707 if (!OidIsValid(relid))
3709 ereport(NOTICE,
3710 (errmsg("relation \"%s\" does not exist, skipping",
3711 stmt->relation->relname)));
3712 return InvalidObjectAddress;
3715 attnum =
3716 renameatt_internal(relid,
3717 stmt->subname, /* old att name */
3718 stmt->newname, /* new att name */
3719 stmt->relation->inh, /* recursive? */
3720 false, /* recursing? */
3721 0, /* expected inhcount */
3722 stmt->behavior);
3724 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3726 return address;
3730 * same logic as renameatt_internal
3732 static ObjectAddress
3733 rename_constraint_internal(Oid myrelid,
3734 Oid mytypid,
3735 const char *oldconname,
3736 const char *newconname,
3737 bool recurse,
3738 bool recursing,
3739 int expected_parents)
3741 Relation targetrelation = NULL;
3742 Oid constraintOid;
3743 HeapTuple tuple;
3744 Form_pg_constraint con;
3745 ObjectAddress address;
3747 Assert(!myrelid || !mytypid);
3749 if (mytypid)
3751 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3753 else
3755 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3758 * don't tell it whether we're recursing; we allow changing typed
3759 * tables here
3761 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3763 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3766 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3767 if (!HeapTupleIsValid(tuple))
3768 elog(ERROR, "cache lookup failed for constraint %u",
3769 constraintOid);
3770 con = (Form_pg_constraint) GETSTRUCT(tuple);
3772 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3774 if (recurse)
3776 List *child_oids,
3777 *child_numparents;
3778 ListCell *lo,
3779 *li;
3781 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3782 &child_numparents);
3784 forboth(lo, child_oids, li, child_numparents)
3786 Oid childrelid = lfirst_oid(lo);
3787 int numparents = lfirst_int(li);
3789 if (childrelid == myrelid)
3790 continue;
3792 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3795 else
3797 if (expected_parents == 0 &&
3798 find_inheritance_children(myrelid, NoLock) != NIL)
3799 ereport(ERROR,
3800 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3801 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3802 oldconname)));
3805 if (con->coninhcount > expected_parents)
3806 ereport(ERROR,
3807 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3808 errmsg("cannot rename inherited constraint \"%s\"",
3809 oldconname)));
3812 if (con->conindid
3813 && (con->contype == CONSTRAINT_PRIMARY
3814 || con->contype == CONSTRAINT_UNIQUE
3815 || con->contype == CONSTRAINT_EXCLUSION))
3816 /* rename the index; this renames the constraint as well */
3817 RenameRelationInternal(con->conindid, newconname, false, true);
3818 else
3819 RenameConstraintById(constraintOid, newconname);
3821 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3823 ReleaseSysCache(tuple);
3825 if (targetrelation)
3828 * Invalidate relcache so as others can see the new constraint name.
3830 CacheInvalidateRelcache(targetrelation);
3832 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3835 return address;
3838 ObjectAddress
3839 RenameConstraint(RenameStmt *stmt)
3841 Oid relid = InvalidOid;
3842 Oid typid = InvalidOid;
3844 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3846 Relation rel;
3847 HeapTuple tup;
3849 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3850 rel = table_open(TypeRelationId, RowExclusiveLock);
3851 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
3852 if (!HeapTupleIsValid(tup))
3853 elog(ERROR, "cache lookup failed for type %u", typid);
3854 checkDomainOwner(tup);
3855 ReleaseSysCache(tup);
3856 table_close(rel, NoLock);
3858 else
3860 /* lock level taken here should match rename_constraint_internal */
3861 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3862 stmt->missing_ok ? RVR_MISSING_OK : 0,
3863 RangeVarCallbackForRenameAttribute,
3864 NULL);
3865 if (!OidIsValid(relid))
3867 ereport(NOTICE,
3868 (errmsg("relation \"%s\" does not exist, skipping",
3869 stmt->relation->relname)));
3870 return InvalidObjectAddress;
3874 return
3875 rename_constraint_internal(relid, typid,
3876 stmt->subname,
3877 stmt->newname,
3878 (stmt->relation &&
3879 stmt->relation->inh), /* recursive? */
3880 false, /* recursing? */
3881 0 /* expected inhcount */ );
3885 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
3886 * RENAME
3888 ObjectAddress
3889 RenameRelation(RenameStmt *stmt)
3891 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
3892 Oid relid;
3893 ObjectAddress address;
3896 * Grab an exclusive lock on the target table, index, sequence, view,
3897 * materialized view, or foreign table, which we will NOT release until
3898 * end of transaction.
3900 * Lock level used here should match RenameRelationInternal, to avoid lock
3901 * escalation. However, because ALTER INDEX can be used with any relation
3902 * type, we mustn't believe without verification.
3904 for (;;)
3906 LOCKMODE lockmode;
3907 char relkind;
3908 bool obj_is_index;
3910 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
3912 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
3913 stmt->missing_ok ? RVR_MISSING_OK : 0,
3914 RangeVarCallbackForAlterRelation,
3915 (void *) stmt);
3917 if (!OidIsValid(relid))
3919 ereport(NOTICE,
3920 (errmsg("relation \"%s\" does not exist, skipping",
3921 stmt->relation->relname)));
3922 return InvalidObjectAddress;
3926 * We allow mismatched statement and object types (e.g., ALTER INDEX
3927 * to rename a table), but we might've used the wrong lock level. If
3928 * that happens, retry with the correct lock level. We don't bother
3929 * if we already acquired AccessExclusiveLock with an index, however.
3931 relkind = get_rel_relkind(relid);
3932 obj_is_index = (relkind == RELKIND_INDEX ||
3933 relkind == RELKIND_PARTITIONED_INDEX);
3934 if (obj_is_index || is_index_stmt == obj_is_index)
3935 break;
3937 UnlockRelationOid(relid, lockmode);
3938 is_index_stmt = obj_is_index;
3941 /* Do the work */
3942 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
3944 ObjectAddressSet(address, RelationRelationId, relid);
3946 return address;
3950 * RenameRelationInternal - change the name of a relation
3952 void
3953 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
3955 Relation targetrelation;
3956 Relation relrelation; /* for RELATION relation */
3957 HeapTuple reltup;
3958 Form_pg_class relform;
3959 Oid namespaceId;
3962 * Grab a lock on the target relation, which we will NOT release until end
3963 * of transaction. We need at least a self-exclusive lock so that
3964 * concurrent DDL doesn't overwrite the rename if they start updating
3965 * while still seeing the old version. The lock also guards against
3966 * triggering relcache reloads in concurrent sessions, which might not
3967 * handle this information changing under them. For indexes, we can use a
3968 * reduced lock level because RelationReloadIndexInfo() handles indexes
3969 * specially.
3971 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
3972 namespaceId = RelationGetNamespace(targetrelation);
3975 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
3977 relrelation = table_open(RelationRelationId, RowExclusiveLock);
3979 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
3980 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
3981 elog(ERROR, "cache lookup failed for relation %u", myrelid);
3982 relform = (Form_pg_class) GETSTRUCT(reltup);
3984 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
3985 ereport(ERROR,
3986 (errcode(ERRCODE_DUPLICATE_TABLE),
3987 errmsg("relation \"%s\" already exists",
3988 newrelname)));
3991 * RenameRelation is careful not to believe the caller's idea of the
3992 * relation kind being handled. We don't have to worry about this, but
3993 * let's not be totally oblivious to it. We can process an index as
3994 * not-an-index, but not the other way around.
3996 Assert(!is_index ||
3997 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
3998 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4001 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4002 * because it's a copy...)
4004 namestrcpy(&(relform->relname), newrelname);
4006 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4008 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4009 InvalidOid, is_internal);
4011 heap_freetuple(reltup);
4012 table_close(relrelation, RowExclusiveLock);
4015 * Also rename the associated type, if any.
4017 if (OidIsValid(targetrelation->rd_rel->reltype))
4018 RenameTypeInternal(targetrelation->rd_rel->reltype,
4019 newrelname, namespaceId);
4022 * Also rename the associated constraint, if any.
4024 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4025 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4027 Oid constraintId = get_index_constraint(myrelid);
4029 if (OidIsValid(constraintId))
4030 RenameConstraintById(constraintId, newrelname);
4034 * Close rel, but keep lock!
4036 relation_close(targetrelation, NoLock);
4040 * ResetRelRewrite - reset relrewrite
4042 void
4043 ResetRelRewrite(Oid myrelid)
4045 Relation relrelation; /* for RELATION relation */
4046 HeapTuple reltup;
4047 Form_pg_class relform;
4050 * Find relation's pg_class tuple.
4052 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4054 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4055 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4056 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4057 relform = (Form_pg_class) GETSTRUCT(reltup);
4060 * Update pg_class tuple.
4062 relform->relrewrite = InvalidOid;
4064 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4066 heap_freetuple(reltup);
4067 table_close(relrelation, RowExclusiveLock);
4071 * Disallow ALTER TABLE (and similar commands) when the current backend has
4072 * any open reference to the target table besides the one just acquired by
4073 * the calling command; this implies there's an open cursor or active plan.
4074 * We need this check because our lock doesn't protect us against stomping
4075 * on our own foot, only other people's feet!
4077 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4078 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4079 * possibly be relaxed to only error out for certain types of alterations.
4080 * But the use-case for allowing any of these things is not obvious, so we
4081 * won't work hard at it for now.
4083 * We also reject these commands if there are any pending AFTER trigger events
4084 * for the rel. This is certainly necessary for the rewriting variants of
4085 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4086 * events would try to fetch the wrong tuples. It might be overly cautious
4087 * in other cases, but again it seems better to err on the side of paranoia.
4089 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4090 * we are worried about active indexscans on the index. The trigger-event
4091 * check can be skipped, since we are doing no damage to the parent table.
4093 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4095 void
4096 CheckTableNotInUse(Relation rel, const char *stmt)
4098 int expected_refcnt;
4100 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4101 if (rel->rd_refcnt != expected_refcnt)
4102 ereport(ERROR,
4103 (errcode(ERRCODE_OBJECT_IN_USE),
4104 /* translator: first %s is a SQL command, eg ALTER TABLE */
4105 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4106 stmt, RelationGetRelationName(rel))));
4108 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4109 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4110 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4111 ereport(ERROR,
4112 (errcode(ERRCODE_OBJECT_IN_USE),
4113 /* translator: first %s is a SQL command, eg ALTER TABLE */
4114 errmsg("cannot %s \"%s\" because it has pending trigger events",
4115 stmt, RelationGetRelationName(rel))));
4119 * AlterTableLookupRelation
4120 * Look up, and lock, the OID for the relation named by an alter table
4121 * statement.
4124 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4126 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4127 stmt->missing_ok ? RVR_MISSING_OK : 0,
4128 RangeVarCallbackForAlterRelation,
4129 (void *) stmt);
4133 * AlterTable
4134 * Execute ALTER TABLE, which can be a list of subcommands
4136 * ALTER TABLE is performed in three phases:
4137 * 1. Examine subcommands and perform pre-transformation checking.
4138 * 2. Validate and transform subcommands, and update system catalogs.
4139 * 3. Scan table(s) to check new constraints, and optionally recopy
4140 * the data into new table(s).
4141 * Phase 3 is not performed unless one or more of the subcommands requires
4142 * it. The intention of this design is to allow multiple independent
4143 * updates of the table schema to be performed with only one pass over the
4144 * data.
4146 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4147 * each table to be affected (there may be multiple affected tables if the
4148 * commands traverse a table inheritance hierarchy). Also we do preliminary
4149 * validation of the subcommands. Because earlier subcommands may change
4150 * the catalog state seen by later commands, there are limits to what can
4151 * be done in this phase. Generally, this phase acquires table locks,
4152 * checks permissions and relkind, and recurses to find child tables.
4154 * ATRewriteCatalogs performs phase 2 for each affected table.
4155 * Certain subcommands need to be performed before others to avoid
4156 * unnecessary conflicts; for example, DROP COLUMN should come before
4157 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4158 * lists, one for each logical "pass" of phase 2.
4160 * ATRewriteTables performs phase 3 for those tables that need it.
4162 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4163 * since phase 1 already does it. However, for certain subcommand types
4164 * it is only possible to determine how to recurse at phase 2 time; for
4165 * those cases, phase 1 sets the cmd->recurse flag.
4167 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4168 * the whole operation; we don't have to do anything special to clean up.
4170 * The caller must lock the relation, with an appropriate lock level
4171 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4172 * or higher. We pass the lock level down
4173 * so that we can apply it recursively to inherited tables. Note that the
4174 * lock level we want as we recurse might well be higher than required for
4175 * that specific subcommand. So we pass down the overall lock requirement,
4176 * rather than reassess it at lower levels.
4178 * The caller also provides a "context" which is to be passed back to
4179 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4180 * Some of the fields therein, such as the relid, are used here as well.
4182 void
4183 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4184 AlterTableUtilityContext *context)
4186 Relation rel;
4188 /* Caller is required to provide an adequate lock. */
4189 rel = relation_open(context->relid, NoLock);
4191 CheckTableNotInUse(rel, "ALTER TABLE");
4193 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4197 * AlterTableInternal
4199 * ALTER TABLE with target specified by OID
4201 * We do not reject if the relation is already open, because it's quite
4202 * likely that one or more layers of caller have it open. That means it
4203 * is unsafe to use this entry point for alterations that could break
4204 * existing query plans. On the assumption it's not used for such, we
4205 * don't have to reject pending AFTER triggers, either.
4207 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4208 * used for any subcommand types that require parse transformation or
4209 * could generate subcommands that have to be passed to ProcessUtility.
4211 void
4212 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4214 Relation rel;
4215 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4217 rel = relation_open(relid, lockmode);
4219 EventTriggerAlterTableRelid(relid);
4221 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4225 * AlterTableGetLockLevel
4227 * Sets the overall lock level required for the supplied list of subcommands.
4228 * Policy for doing this set according to needs of AlterTable(), see
4229 * comments there for overall explanation.
4231 * Function is called before and after parsing, so it must give same
4232 * answer each time it is called. Some subcommands are transformed
4233 * into other subcommand types, so the transform must never be made to a
4234 * lower lock level than previously assigned. All transforms are noted below.
4236 * Since this is called before we lock the table we cannot use table metadata
4237 * to influence the type of lock we acquire.
4239 * There should be no lockmodes hardcoded into the subcommand functions. All
4240 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4241 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4242 * and does not travel through this section of code and cannot be combined with
4243 * any of the subcommands given here.
4245 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4246 * so any changes that might affect SELECTs running on standbys need to use
4247 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4248 * have a solution for that also.
4250 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4251 * that takes a lock less than AccessExclusiveLock can change object definitions
4252 * while pg_dump is running. Be careful to check that the appropriate data is
4253 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4254 * otherwise we might end up with an inconsistent dump that can't restore.
4256 LOCKMODE
4257 AlterTableGetLockLevel(List *cmds)
4260 * This only works if we read catalog tables using MVCC snapshots.
4262 ListCell *lcmd;
4263 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4265 foreach(lcmd, cmds)
4267 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4268 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4270 switch (cmd->subtype)
4273 * These subcommands rewrite the heap, so require full locks.
4275 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4276 * to SELECT */
4277 case AT_SetAccessMethod: /* must rewrite heap */
4278 case AT_SetTableSpace: /* must rewrite heap */
4279 case AT_AlterColumnType: /* must rewrite heap */
4280 cmd_lockmode = AccessExclusiveLock;
4281 break;
4284 * These subcommands may require addition of toast tables. If
4285 * we add a toast table to a table currently being scanned, we
4286 * might miss data added to the new toast table by concurrent
4287 * insert transactions.
4289 case AT_SetStorage: /* may add toast tables, see
4290 * ATRewriteCatalogs() */
4291 cmd_lockmode = AccessExclusiveLock;
4292 break;
4295 * Removing constraints can affect SELECTs that have been
4296 * optimized assuming the constraint holds true. See also
4297 * CloneFkReferenced.
4299 case AT_DropConstraint: /* as DROP INDEX */
4300 case AT_DropNotNull: /* may change some SQL plans */
4301 cmd_lockmode = AccessExclusiveLock;
4302 break;
4305 * Subcommands that may be visible to concurrent SELECTs
4307 case AT_DropColumn: /* change visible to SELECT */
4308 case AT_AddColumnToView: /* CREATE VIEW */
4309 case AT_DropOids: /* used to equiv to DropColumn */
4310 case AT_EnableAlwaysRule: /* may change SELECT rules */
4311 case AT_EnableReplicaRule: /* may change SELECT rules */
4312 case AT_EnableRule: /* may change SELECT rules */
4313 case AT_DisableRule: /* may change SELECT rules */
4314 cmd_lockmode = AccessExclusiveLock;
4315 break;
4318 * Changing owner may remove implicit SELECT privileges
4320 case AT_ChangeOwner: /* change visible to SELECT */
4321 cmd_lockmode = AccessExclusiveLock;
4322 break;
4325 * Changing foreign table options may affect optimization.
4327 case AT_GenericOptions:
4328 case AT_AlterColumnGenericOptions:
4329 cmd_lockmode = AccessExclusiveLock;
4330 break;
4333 * These subcommands affect write operations only.
4335 case AT_EnableTrig:
4336 case AT_EnableAlwaysTrig:
4337 case AT_EnableReplicaTrig:
4338 case AT_EnableTrigAll:
4339 case AT_EnableTrigUser:
4340 case AT_DisableTrig:
4341 case AT_DisableTrigAll:
4342 case AT_DisableTrigUser:
4343 cmd_lockmode = ShareRowExclusiveLock;
4344 break;
4347 * These subcommands affect write operations only. XXX
4348 * Theoretically, these could be ShareRowExclusiveLock.
4350 case AT_ColumnDefault:
4351 case AT_CookedColumnDefault:
4352 case AT_AlterConstraint:
4353 case AT_AddIndex: /* from ADD CONSTRAINT */
4354 case AT_AddIndexConstraint:
4355 case AT_ReplicaIdentity:
4356 case AT_SetNotNull:
4357 case AT_EnableRowSecurity:
4358 case AT_DisableRowSecurity:
4359 case AT_ForceRowSecurity:
4360 case AT_NoForceRowSecurity:
4361 case AT_AddIdentity:
4362 case AT_DropIdentity:
4363 case AT_SetIdentity:
4364 case AT_DropExpression:
4365 case AT_SetCompression:
4366 cmd_lockmode = AccessExclusiveLock;
4367 break;
4369 case AT_AddConstraint:
4370 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4371 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4372 if (IsA(cmd->def, Constraint))
4374 Constraint *con = (Constraint *) cmd->def;
4376 switch (con->contype)
4378 case CONSTR_EXCLUSION:
4379 case CONSTR_PRIMARY:
4380 case CONSTR_UNIQUE:
4383 * Cases essentially the same as CREATE INDEX. We
4384 * could reduce the lock strength to ShareLock if
4385 * we can work out how to allow concurrent catalog
4386 * updates. XXX Might be set down to
4387 * ShareRowExclusiveLock but requires further
4388 * analysis.
4390 cmd_lockmode = AccessExclusiveLock;
4391 break;
4392 case CONSTR_FOREIGN:
4395 * We add triggers to both tables when we add a
4396 * Foreign Key, so the lock level must be at least
4397 * as strong as CREATE TRIGGER.
4399 cmd_lockmode = ShareRowExclusiveLock;
4400 break;
4402 default:
4403 cmd_lockmode = AccessExclusiveLock;
4406 break;
4409 * These subcommands affect inheritance behaviour. Queries
4410 * started before us will continue to see the old inheritance
4411 * behaviour, while queries started after we commit will see
4412 * new behaviour. No need to prevent reads or writes to the
4413 * subtable while we hook it up though. Changing the TupDesc
4414 * may be a problem, so keep highest lock.
4416 case AT_AddInherit:
4417 case AT_DropInherit:
4418 cmd_lockmode = AccessExclusiveLock;
4419 break;
4422 * These subcommands affect implicit row type conversion. They
4423 * have affects similar to CREATE/DROP CAST on queries. don't
4424 * provide for invalidating parse trees as a result of such
4425 * changes, so we keep these at AccessExclusiveLock.
4427 case AT_AddOf:
4428 case AT_DropOf:
4429 cmd_lockmode = AccessExclusiveLock;
4430 break;
4433 * Only used by CREATE OR REPLACE VIEW which must conflict
4434 * with an SELECTs currently using the view.
4436 case AT_ReplaceRelOptions:
4437 cmd_lockmode = AccessExclusiveLock;
4438 break;
4441 * These subcommands affect general strategies for performance
4442 * and maintenance, though don't change the semantic results
4443 * from normal data reads and writes. Delaying an ALTER TABLE
4444 * behind currently active writes only delays the point where
4445 * the new strategy begins to take effect, so there is no
4446 * benefit in waiting. In this case the minimum restriction
4447 * applies: we don't currently allow concurrent catalog
4448 * updates.
4450 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4451 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4452 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4453 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4454 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4455 cmd_lockmode = ShareUpdateExclusiveLock;
4456 break;
4458 case AT_SetLogged:
4459 case AT_SetUnLogged:
4460 cmd_lockmode = AccessExclusiveLock;
4461 break;
4463 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4464 cmd_lockmode = ShareUpdateExclusiveLock;
4465 break;
4468 * Rel options are more complex than first appears. Options
4469 * are set here for tables, views and indexes; for historical
4470 * reasons these can all be used with ALTER TABLE, so we can't
4471 * decide between them using the basic grammar.
4473 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4474 * getTables() */
4475 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4476 * getTables() */
4477 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4478 break;
4480 case AT_AttachPartition:
4481 cmd_lockmode = ShareUpdateExclusiveLock;
4482 break;
4484 case AT_DetachPartition:
4485 if (((PartitionCmd *) cmd->def)->concurrent)
4486 cmd_lockmode = ShareUpdateExclusiveLock;
4487 else
4488 cmd_lockmode = AccessExclusiveLock;
4489 break;
4491 case AT_DetachPartitionFinalize:
4492 cmd_lockmode = ShareUpdateExclusiveLock;
4493 break;
4495 case AT_CheckNotNull:
4498 * This only examines the table's schema; but lock must be
4499 * strong enough to prevent concurrent DROP NOT NULL.
4501 cmd_lockmode = AccessShareLock;
4502 break;
4504 default: /* oops */
4505 elog(ERROR, "unrecognized alter table type: %d",
4506 (int) cmd->subtype);
4507 break;
4511 * Take the greatest lockmode from any subcommand
4513 if (cmd_lockmode > lockmode)
4514 lockmode = cmd_lockmode;
4517 return lockmode;
4521 * ATController provides top level control over the phases.
4523 * parsetree is passed in to allow it to be passed to event triggers
4524 * when requested.
4526 static void
4527 ATController(AlterTableStmt *parsetree,
4528 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4529 AlterTableUtilityContext *context)
4531 List *wqueue = NIL;
4532 ListCell *lcmd;
4534 /* Phase 1: preliminary examination of commands, create work queue */
4535 foreach(lcmd, cmds)
4537 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4539 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4542 /* Close the relation, but keep lock until commit */
4543 relation_close(rel, NoLock);
4545 /* Phase 2: update system catalogs */
4546 ATRewriteCatalogs(&wqueue, lockmode, context);
4548 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4549 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4553 * ATPrepCmd
4555 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4556 * recursion and permission checks.
4558 * Caller must have acquired appropriate lock type on relation already.
4559 * This lock should be held until commit.
4561 static void
4562 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4563 bool recurse, bool recursing, LOCKMODE lockmode,
4564 AlterTableUtilityContext *context)
4566 AlteredTableInfo *tab;
4567 int pass = AT_PASS_UNSET;
4569 /* Find or create work queue entry for this table */
4570 tab = ATGetQueueEntry(wqueue, rel);
4573 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4574 * partitions that are pending detach.
4576 if (rel->rd_rel->relispartition &&
4577 cmd->subtype != AT_DetachPartitionFinalize &&
4578 PartitionHasPendingDetach(RelationGetRelid(rel)))
4579 ereport(ERROR,
4580 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4581 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4582 RelationGetRelationName(rel)),
4583 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4586 * Copy the original subcommand for each table, so we can scribble on it.
4587 * This avoids conflicts when different child tables need to make
4588 * different parse transformations (for example, the same column may have
4589 * different column numbers in different children).
4591 cmd = copyObject(cmd);
4594 * Do permissions and relkind checking, recursion to child tables if
4595 * needed, and any additional phase-1 processing needed. (But beware of
4596 * adding any processing that looks at table details that another
4597 * subcommand could change. In some cases we reject multiple subcommands
4598 * that could try to change the same state in contrary ways.)
4600 switch (cmd->subtype)
4602 case AT_AddColumn: /* ADD COLUMN */
4603 ATSimplePermissions(cmd->subtype, rel,
4604 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4605 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4606 lockmode, context);
4607 /* Recursion occurs during execution phase */
4608 pass = AT_PASS_ADD_COL;
4609 break;
4610 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4611 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4612 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4613 lockmode, context);
4614 /* Recursion occurs during execution phase */
4615 pass = AT_PASS_ADD_COL;
4616 break;
4617 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4620 * We allow defaults on views so that INSERT into a view can have
4621 * default-ish behavior. This works because the rewriter
4622 * substitutes default values into INSERTs before it expands
4623 * rules.
4625 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4626 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4627 /* No command-specific prep needed */
4628 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4629 break;
4630 case AT_CookedColumnDefault: /* add a pre-cooked default */
4631 /* This is currently used only in CREATE TABLE */
4632 /* (so the permission check really isn't necessary) */
4633 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4634 /* This command never recurses */
4635 pass = AT_PASS_ADD_OTHERCONSTR;
4636 break;
4637 case AT_AddIdentity:
4638 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4639 /* This command never recurses */
4640 pass = AT_PASS_ADD_OTHERCONSTR;
4641 break;
4642 case AT_SetIdentity:
4643 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4644 /* This command never recurses */
4645 /* This should run after AddIdentity, so do it in MISC pass */
4646 pass = AT_PASS_MISC;
4647 break;
4648 case AT_DropIdentity:
4649 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4650 /* This command never recurses */
4651 pass = AT_PASS_DROP;
4652 break;
4653 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4654 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4655 ATPrepDropNotNull(rel, recurse, recursing);
4656 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4657 pass = AT_PASS_DROP;
4658 break;
4659 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4660 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4661 /* Need command-specific recursion decision */
4662 ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4663 lockmode, context);
4664 pass = AT_PASS_COL_ATTRS;
4665 break;
4666 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4667 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4668 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4669 /* No command-specific prep needed */
4670 pass = AT_PASS_COL_ATTRS;
4671 break;
4672 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4673 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4674 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4675 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4676 pass = AT_PASS_DROP;
4677 break;
4678 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4679 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4680 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4681 /* No command-specific prep needed */
4682 pass = AT_PASS_MISC;
4683 break;
4684 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4685 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4686 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4687 /* This command never recurses */
4688 pass = AT_PASS_MISC;
4689 break;
4690 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4691 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4692 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4693 /* No command-specific prep needed */
4694 pass = AT_PASS_MISC;
4695 break;
4696 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4697 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4698 /* This command never recurses */
4699 /* No command-specific prep needed */
4700 pass = AT_PASS_MISC;
4701 break;
4702 case AT_DropColumn: /* DROP COLUMN */
4703 ATSimplePermissions(cmd->subtype, rel,
4704 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4705 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4706 lockmode, context);
4707 /* Recursion occurs during execution phase */
4708 pass = AT_PASS_DROP;
4709 break;
4710 case AT_AddIndex: /* ADD INDEX */
4711 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4712 /* This command never recurses */
4713 /* No command-specific prep needed */
4714 pass = AT_PASS_ADD_INDEX;
4715 break;
4716 case AT_AddConstraint: /* ADD CONSTRAINT */
4717 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4718 /* Recursion occurs during execution phase */
4719 /* No command-specific prep needed except saving recurse flag */
4720 if (recurse)
4721 cmd->recurse = true;
4722 pass = AT_PASS_ADD_CONSTR;
4723 break;
4724 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4725 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4726 /* This command never recurses */
4727 /* No command-specific prep needed */
4728 pass = AT_PASS_ADD_INDEXCONSTR;
4729 break;
4730 case AT_DropConstraint: /* DROP CONSTRAINT */
4731 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4732 ATCheckPartitionsNotInUse(rel, lockmode);
4733 /* Other recursion occurs during execution phase */
4734 /* No command-specific prep needed except saving recurse flag */
4735 if (recurse)
4736 cmd->recurse = true;
4737 pass = AT_PASS_DROP;
4738 break;
4739 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4740 ATSimplePermissions(cmd->subtype, rel,
4741 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4742 /* See comments for ATPrepAlterColumnType */
4743 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4744 AT_PASS_UNSET, context);
4745 Assert(cmd != NULL);
4746 /* Performs own recursion */
4747 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4748 lockmode, context);
4749 pass = AT_PASS_ALTER_TYPE;
4750 break;
4751 case AT_AlterColumnGenericOptions:
4752 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4753 /* This command never recurses */
4754 /* No command-specific prep needed */
4755 pass = AT_PASS_MISC;
4756 break;
4757 case AT_ChangeOwner: /* ALTER OWNER */
4758 /* This command never recurses */
4759 /* No command-specific prep needed */
4760 pass = AT_PASS_MISC;
4761 break;
4762 case AT_ClusterOn: /* CLUSTER ON */
4763 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4764 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4765 /* These commands never recurse */
4766 /* No command-specific prep needed */
4767 pass = AT_PASS_MISC;
4768 break;
4769 case AT_SetLogged: /* SET LOGGED */
4770 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4771 if (tab->chgPersistence)
4772 ereport(ERROR,
4773 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4774 errmsg("cannot change persistence setting twice")));
4775 tab->chgPersistence = ATPrepChangePersistence(rel, true);
4776 /* force rewrite if necessary; see comment in ATRewriteTables */
4777 if (tab->chgPersistence)
4779 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4780 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4782 pass = AT_PASS_MISC;
4783 break;
4784 case AT_SetUnLogged: /* SET UNLOGGED */
4785 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4786 if (tab->chgPersistence)
4787 ereport(ERROR,
4788 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4789 errmsg("cannot change persistence setting twice")));
4790 tab->chgPersistence = ATPrepChangePersistence(rel, false);
4791 /* force rewrite if necessary; see comment in ATRewriteTables */
4792 if (tab->chgPersistence)
4794 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4795 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4797 pass = AT_PASS_MISC;
4798 break;
4799 case AT_DropOids: /* SET WITHOUT OIDS */
4800 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4801 pass = AT_PASS_DROP;
4802 break;
4803 case AT_SetAccessMethod: /* SET ACCESS METHOD */
4804 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4806 /* partitioned tables don't have an access method */
4807 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
4808 ereport(ERROR,
4809 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
4810 errmsg("cannot change access method of a partitioned table")));
4812 /* check if another access method change was already requested */
4813 if (OidIsValid(tab->newAccessMethod))
4814 ereport(ERROR,
4815 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4816 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
4818 ATPrepSetAccessMethod(tab, rel, cmd->name);
4819 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
4820 break;
4821 case AT_SetTableSpace: /* SET TABLESPACE */
4822 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
4823 ATT_PARTITIONED_INDEX);
4824 /* This command never recurses */
4825 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
4826 pass = AT_PASS_MISC; /* doesn't actually matter */
4827 break;
4828 case AT_SetRelOptions: /* SET (...) */
4829 case AT_ResetRelOptions: /* RESET (...) */
4830 case AT_ReplaceRelOptions: /* reset them all, then set just these */
4831 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
4832 /* This command never recurses */
4833 /* No command-specific prep needed */
4834 pass = AT_PASS_MISC;
4835 break;
4836 case AT_AddInherit: /* INHERIT */
4837 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4838 /* This command never recurses */
4839 ATPrepAddInherit(rel);
4840 pass = AT_PASS_MISC;
4841 break;
4842 case AT_DropInherit: /* NO INHERIT */
4843 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4844 /* This command never recurses */
4845 /* No command-specific prep needed */
4846 pass = AT_PASS_MISC;
4847 break;
4848 case AT_AlterConstraint: /* ALTER CONSTRAINT */
4849 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4850 /* Recursion occurs during execution phase */
4851 pass = AT_PASS_MISC;
4852 break;
4853 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
4854 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4855 /* Recursion occurs during execution phase */
4856 /* No command-specific prep needed except saving recurse flag */
4857 if (recurse)
4858 cmd->recurse = true;
4859 pass = AT_PASS_MISC;
4860 break;
4861 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
4862 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4863 pass = AT_PASS_MISC;
4864 /* This command never recurses */
4865 /* No command-specific prep needed */
4866 break;
4867 case AT_EnableTrig: /* ENABLE TRIGGER variants */
4868 case AT_EnableAlwaysTrig:
4869 case AT_EnableReplicaTrig:
4870 case AT_EnableTrigAll:
4871 case AT_EnableTrigUser:
4872 case AT_DisableTrig: /* DISABLE TRIGGER variants */
4873 case AT_DisableTrigAll:
4874 case AT_DisableTrigUser:
4875 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4876 /* Set up recursion for phase 2; no other prep needed */
4877 if (recurse)
4878 cmd->recurse = true;
4879 pass = AT_PASS_MISC;
4880 break;
4881 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
4882 case AT_EnableAlwaysRule:
4883 case AT_EnableReplicaRule:
4884 case AT_DisableRule:
4885 case AT_AddOf: /* OF */
4886 case AT_DropOf: /* NOT OF */
4887 case AT_EnableRowSecurity:
4888 case AT_DisableRowSecurity:
4889 case AT_ForceRowSecurity:
4890 case AT_NoForceRowSecurity:
4891 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4892 /* These commands never recurse */
4893 /* No command-specific prep needed */
4894 pass = AT_PASS_MISC;
4895 break;
4896 case AT_GenericOptions:
4897 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4898 /* No command-specific prep needed */
4899 pass = AT_PASS_MISC;
4900 break;
4901 case AT_AttachPartition:
4902 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
4903 /* No command-specific prep needed */
4904 pass = AT_PASS_MISC;
4905 break;
4906 case AT_DetachPartition:
4907 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4908 /* No command-specific prep needed */
4909 pass = AT_PASS_MISC;
4910 break;
4911 case AT_DetachPartitionFinalize:
4912 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4913 /* No command-specific prep needed */
4914 pass = AT_PASS_MISC;
4915 break;
4916 default: /* oops */
4917 elog(ERROR, "unrecognized alter table type: %d",
4918 (int) cmd->subtype);
4919 pass = AT_PASS_UNSET; /* keep compiler quiet */
4920 break;
4922 Assert(pass > AT_PASS_UNSET);
4924 /* Add the subcommand to the appropriate list for phase 2 */
4925 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
4929 * ATRewriteCatalogs
4931 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
4932 * dispatched in a "safe" execution order (designed to avoid unnecessary
4933 * conflicts).
4935 static void
4936 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
4937 AlterTableUtilityContext *context)
4939 int pass;
4940 ListCell *ltab;
4943 * We process all the tables "in parallel", one pass at a time. This is
4944 * needed because we may have to propagate work from one table to another
4945 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
4946 * re-adding of the foreign key constraint to the other table). Work can
4947 * only be propagated into later passes, however.
4949 for (pass = 0; pass < AT_NUM_PASSES; pass++)
4951 /* Go through each table that needs to be processed */
4952 foreach(ltab, *wqueue)
4954 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4955 List *subcmds = tab->subcmds[pass];
4956 ListCell *lcmd;
4958 if (subcmds == NIL)
4959 continue;
4962 * Open the relation and store it in tab. This allows subroutines
4963 * close and reopen, if necessary. Appropriate lock was obtained
4964 * by phase 1, needn't get it again.
4966 tab->rel = relation_open(tab->relid, NoLock);
4968 foreach(lcmd, subcmds)
4969 ATExecCmd(wqueue, tab,
4970 lfirst_node(AlterTableCmd, lcmd),
4971 lockmode, pass, context);
4974 * After the ALTER TYPE pass, do cleanup work (this is not done in
4975 * ATExecAlterColumnType since it should be done only once if
4976 * multiple columns of a table are altered).
4978 if (pass == AT_PASS_ALTER_TYPE)
4979 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
4981 if (tab->rel)
4983 relation_close(tab->rel, NoLock);
4984 tab->rel = NULL;
4989 /* Check to see if a toast table must be added. */
4990 foreach(ltab, *wqueue)
4992 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
4995 * If the table is source table of ATTACH PARTITION command, we did
4996 * not modify anything about it that will change its toasting
4997 * requirement, so no need to check.
4999 if (((tab->relkind == RELKIND_RELATION ||
5000 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5001 tab->partition_constraint == NULL) ||
5002 tab->relkind == RELKIND_MATVIEW)
5003 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5008 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5010 static void
5011 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5012 AlterTableCmd *cmd, LOCKMODE lockmode, int cur_pass,
5013 AlterTableUtilityContext *context)
5015 ObjectAddress address = InvalidObjectAddress;
5016 Relation rel = tab->rel;
5018 switch (cmd->subtype)
5020 case AT_AddColumn: /* ADD COLUMN */
5021 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5022 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5023 cmd->recurse, false,
5024 lockmode, cur_pass, context);
5025 break;
5026 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5027 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5028 break;
5029 case AT_CookedColumnDefault: /* add a pre-cooked default */
5030 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5031 break;
5032 case AT_AddIdentity:
5033 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5034 cur_pass, context);
5035 Assert(cmd != NULL);
5036 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode);
5037 break;
5038 case AT_SetIdentity:
5039 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5040 cur_pass, context);
5041 Assert(cmd != NULL);
5042 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode);
5043 break;
5044 case AT_DropIdentity:
5045 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode);
5046 break;
5047 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5048 address = ATExecDropNotNull(rel, cmd->name, lockmode);
5049 break;
5050 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5051 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
5052 break;
5053 case AT_CheckNotNull: /* check column is already marked NOT NULL */
5054 ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
5055 break;
5056 case AT_DropExpression:
5057 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5058 break;
5059 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5060 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5061 break;
5062 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5063 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5064 break;
5065 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5066 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5067 break;
5068 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5069 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5070 break;
5071 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5072 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5073 lockmode);
5074 break;
5075 case AT_DropColumn: /* DROP COLUMN */
5076 address = ATExecDropColumn(wqueue, rel, cmd->name,
5077 cmd->behavior, cmd->recurse, false,
5078 cmd->missing_ok, lockmode,
5079 NULL);
5080 break;
5081 case AT_AddIndex: /* ADD INDEX */
5082 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5083 lockmode);
5084 break;
5085 case AT_ReAddIndex: /* ADD INDEX */
5086 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5087 lockmode);
5088 break;
5089 case AT_ReAddStatistics: /* ADD STATISTICS */
5090 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5091 true, lockmode);
5092 break;
5093 case AT_AddConstraint: /* ADD CONSTRAINT */
5094 /* Transform the command only during initial examination */
5095 if (cur_pass == AT_PASS_ADD_CONSTR)
5096 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5097 cmd->recurse, lockmode,
5098 cur_pass, context);
5099 /* Depending on constraint type, might be no more work to do now */
5100 if (cmd != NULL)
5101 address =
5102 ATExecAddConstraint(wqueue, tab, rel,
5103 (Constraint *) cmd->def,
5104 cmd->recurse, false, lockmode);
5105 break;
5106 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5107 address =
5108 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5109 true, true, lockmode);
5110 break;
5111 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5112 * constraint */
5113 address =
5114 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5115 ((AlterDomainStmt *) cmd->def)->def,
5116 NULL);
5117 break;
5118 case AT_ReAddComment: /* Re-add existing comment */
5119 address = CommentObject((CommentStmt *) cmd->def);
5120 break;
5121 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5122 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5123 lockmode);
5124 break;
5125 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5126 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5127 break;
5128 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5129 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5130 false, lockmode);
5131 break;
5132 case AT_DropConstraint: /* DROP CONSTRAINT */
5133 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5134 cmd->recurse, false,
5135 cmd->missing_ok, lockmode);
5136 break;
5137 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5138 /* parse transformation was done earlier */
5139 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5140 break;
5141 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5142 address =
5143 ATExecAlterColumnGenericOptions(rel, cmd->name,
5144 (List *) cmd->def, lockmode);
5145 break;
5146 case AT_ChangeOwner: /* ALTER OWNER */
5147 ATExecChangeOwner(RelationGetRelid(rel),
5148 get_rolespec_oid(cmd->newowner, false),
5149 false, lockmode);
5150 break;
5151 case AT_ClusterOn: /* CLUSTER ON */
5152 address = ATExecClusterOn(rel, cmd->name, lockmode);
5153 break;
5154 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5155 ATExecDropCluster(rel, lockmode);
5156 break;
5157 case AT_SetLogged: /* SET LOGGED */
5158 case AT_SetUnLogged: /* SET UNLOGGED */
5159 break;
5160 case AT_DropOids: /* SET WITHOUT OIDS */
5161 /* nothing to do here, oid columns don't exist anymore */
5162 break;
5163 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5164 /* handled specially in Phase 3 */
5165 break;
5166 case AT_SetTableSpace: /* SET TABLESPACE */
5169 * Only do this for partitioned tables and indexes, for which this
5170 * is just a catalog change. Other relation types which have
5171 * storage are handled by Phase 3.
5173 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5174 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5175 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5177 break;
5178 case AT_SetRelOptions: /* SET (...) */
5179 case AT_ResetRelOptions: /* RESET (...) */
5180 case AT_ReplaceRelOptions: /* replace entire option list */
5181 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5182 break;
5183 case AT_EnableTrig: /* ENABLE TRIGGER name */
5184 ATExecEnableDisableTrigger(rel, cmd->name,
5185 TRIGGER_FIRES_ON_ORIGIN, false,
5186 cmd->recurse,
5187 lockmode);
5188 break;
5189 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5190 ATExecEnableDisableTrigger(rel, cmd->name,
5191 TRIGGER_FIRES_ALWAYS, false,
5192 cmd->recurse,
5193 lockmode);
5194 break;
5195 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5196 ATExecEnableDisableTrigger(rel, cmd->name,
5197 TRIGGER_FIRES_ON_REPLICA, false,
5198 cmd->recurse,
5199 lockmode);
5200 break;
5201 case AT_DisableTrig: /* DISABLE TRIGGER name */
5202 ATExecEnableDisableTrigger(rel, cmd->name,
5203 TRIGGER_DISABLED, false,
5204 cmd->recurse,
5205 lockmode);
5206 break;
5207 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5208 ATExecEnableDisableTrigger(rel, NULL,
5209 TRIGGER_FIRES_ON_ORIGIN, false,
5210 cmd->recurse,
5211 lockmode);
5212 break;
5213 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5214 ATExecEnableDisableTrigger(rel, NULL,
5215 TRIGGER_DISABLED, false,
5216 cmd->recurse,
5217 lockmode);
5218 break;
5219 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5220 ATExecEnableDisableTrigger(rel, NULL,
5221 TRIGGER_FIRES_ON_ORIGIN, true,
5222 cmd->recurse,
5223 lockmode);
5224 break;
5225 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5226 ATExecEnableDisableTrigger(rel, NULL,
5227 TRIGGER_DISABLED, true,
5228 cmd->recurse,
5229 lockmode);
5230 break;
5232 case AT_EnableRule: /* ENABLE RULE name */
5233 ATExecEnableDisableRule(rel, cmd->name,
5234 RULE_FIRES_ON_ORIGIN, lockmode);
5235 break;
5236 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5237 ATExecEnableDisableRule(rel, cmd->name,
5238 RULE_FIRES_ALWAYS, lockmode);
5239 break;
5240 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5241 ATExecEnableDisableRule(rel, cmd->name,
5242 RULE_FIRES_ON_REPLICA, lockmode);
5243 break;
5244 case AT_DisableRule: /* DISABLE RULE name */
5245 ATExecEnableDisableRule(rel, cmd->name,
5246 RULE_DISABLED, lockmode);
5247 break;
5249 case AT_AddInherit:
5250 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5251 break;
5252 case AT_DropInherit:
5253 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5254 break;
5255 case AT_AddOf:
5256 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5257 break;
5258 case AT_DropOf:
5259 ATExecDropOf(rel, lockmode);
5260 break;
5261 case AT_ReplicaIdentity:
5262 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5263 break;
5264 case AT_EnableRowSecurity:
5265 ATExecSetRowSecurity(rel, true);
5266 break;
5267 case AT_DisableRowSecurity:
5268 ATExecSetRowSecurity(rel, false);
5269 break;
5270 case AT_ForceRowSecurity:
5271 ATExecForceNoForceRowSecurity(rel, true);
5272 break;
5273 case AT_NoForceRowSecurity:
5274 ATExecForceNoForceRowSecurity(rel, false);
5275 break;
5276 case AT_GenericOptions:
5277 ATExecGenericOptions(rel, (List *) cmd->def);
5278 break;
5279 case AT_AttachPartition:
5280 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5281 cur_pass, context);
5282 Assert(cmd != NULL);
5283 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5284 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5285 context);
5286 else
5287 address = ATExecAttachPartitionIdx(wqueue, rel,
5288 ((PartitionCmd *) cmd->def)->name);
5289 break;
5290 case AT_DetachPartition:
5291 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5292 cur_pass, context);
5293 Assert(cmd != NULL);
5294 /* ATPrepCmd ensures it must be a table */
5295 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5296 address = ATExecDetachPartition(wqueue, tab, rel,
5297 ((PartitionCmd *) cmd->def)->name,
5298 ((PartitionCmd *) cmd->def)->concurrent);
5299 break;
5300 case AT_DetachPartitionFinalize:
5301 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5302 break;
5303 default: /* oops */
5304 elog(ERROR, "unrecognized alter table type: %d",
5305 (int) cmd->subtype);
5306 break;
5310 * Report the subcommand to interested event triggers.
5312 if (cmd)
5313 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5316 * Bump the command counter to ensure the next subcommand in the sequence
5317 * can see the changes so far
5319 CommandCounterIncrement();
5323 * ATParseTransformCmd: perform parse transformation for one subcommand
5325 * Returns the transformed subcommand tree, if there is one, else NULL.
5327 * The parser may hand back additional AlterTableCmd(s) and/or other
5328 * utility statements, either before or after the original subcommand.
5329 * Other AlterTableCmds are scheduled into the appropriate slot of the
5330 * AlteredTableInfo (they had better be for later passes than the current one).
5331 * Utility statements that are supposed to happen before the AlterTableCmd
5332 * are executed immediately. Those that are supposed to happen afterwards
5333 * are added to the tab->afterStmts list to be done at the very end.
5335 static AlterTableCmd *
5336 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5337 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5338 int cur_pass, AlterTableUtilityContext *context)
5340 AlterTableCmd *newcmd = NULL;
5341 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5342 List *beforeStmts;
5343 List *afterStmts;
5344 ListCell *lc;
5346 /* Gin up an AlterTableStmt with just this subcommand and this table */
5347 atstmt->relation =
5348 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5349 pstrdup(RelationGetRelationName(rel)),
5350 -1);
5351 atstmt->relation->inh = recurse;
5352 atstmt->cmds = list_make1(cmd);
5353 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5354 atstmt->missing_ok = false;
5356 /* Transform the AlterTableStmt */
5357 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5358 atstmt,
5359 context->queryString,
5360 &beforeStmts,
5361 &afterStmts);
5363 /* Execute any statements that should happen before these subcommand(s) */
5364 foreach(lc, beforeStmts)
5366 Node *stmt = (Node *) lfirst(lc);
5368 ProcessUtilityForAlterTable(stmt, context);
5369 CommandCounterIncrement();
5372 /* Examine the transformed subcommands and schedule them appropriately */
5373 foreach(lc, atstmt->cmds)
5375 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5376 int pass;
5379 * This switch need only cover the subcommand types that can be added
5380 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5381 * executing the subcommand immediately, as a substitute for the
5382 * original subcommand. (Note, however, that this does cause
5383 * AT_AddConstraint subcommands to be rescheduled into later passes,
5384 * which is important for index and foreign key constraints.)
5386 * We assume we needn't do any phase-1 checks for added subcommands.
5388 switch (cmd2->subtype)
5390 case AT_SetNotNull:
5391 /* Need command-specific recursion decision */
5392 ATPrepSetNotNull(wqueue, rel, cmd2,
5393 recurse, false,
5394 lockmode, context);
5395 pass = AT_PASS_COL_ATTRS;
5396 break;
5397 case AT_AddIndex:
5398 /* This command never recurses */
5399 /* No command-specific prep needed */
5400 pass = AT_PASS_ADD_INDEX;
5401 break;
5402 case AT_AddIndexConstraint:
5403 /* This command never recurses */
5404 /* No command-specific prep needed */
5405 pass = AT_PASS_ADD_INDEXCONSTR;
5406 break;
5407 case AT_AddConstraint:
5408 /* Recursion occurs during execution phase */
5409 if (recurse)
5410 cmd2->recurse = true;
5411 switch (castNode(Constraint, cmd2->def)->contype)
5413 case CONSTR_PRIMARY:
5414 case CONSTR_UNIQUE:
5415 case CONSTR_EXCLUSION:
5416 pass = AT_PASS_ADD_INDEXCONSTR;
5417 break;
5418 default:
5419 pass = AT_PASS_ADD_OTHERCONSTR;
5420 break;
5422 break;
5423 case AT_AlterColumnGenericOptions:
5424 /* This command never recurses */
5425 /* No command-specific prep needed */
5426 pass = AT_PASS_MISC;
5427 break;
5428 default:
5429 pass = cur_pass;
5430 break;
5433 if (pass < cur_pass)
5435 /* Cannot schedule into a pass we already finished */
5436 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5437 pass);
5439 else if (pass > cur_pass)
5441 /* OK, queue it up for later */
5442 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5444 else
5447 * We should see at most one subcommand for the current pass,
5448 * which is the transformed version of the original subcommand.
5450 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5452 /* Found the transformed version of our subcommand */
5453 newcmd = cmd2;
5455 else
5456 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5457 pass);
5461 /* Queue up any after-statements to happen at the end */
5462 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5464 return newcmd;
5468 * ATRewriteTables: ALTER TABLE phase 3
5470 static void
5471 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5472 AlterTableUtilityContext *context)
5474 ListCell *ltab;
5476 /* Go through each table that needs to be checked or rewritten */
5477 foreach(ltab, *wqueue)
5479 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5481 /* Relations without storage may be ignored here */
5482 if (!RELKIND_HAS_STORAGE(tab->relkind))
5483 continue;
5486 * If we change column data types, the operation has to be propagated
5487 * to tables that use this table's rowtype as a column type.
5488 * tab->newvals will also be non-NULL in the case where we're adding a
5489 * column with a default. We choose to forbid that case as well,
5490 * since composite types might eventually support defaults.
5492 * (Eventually we'll probably need to check for composite type
5493 * dependencies even when we're just scanning the table without a
5494 * rewrite, but at the moment a composite type does not enforce any
5495 * constraints, so it's not necessary/appropriate to enforce them just
5496 * during ALTER.)
5498 if (tab->newvals != NIL || tab->rewrite > 0)
5500 Relation rel;
5502 rel = table_open(tab->relid, NoLock);
5503 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5504 table_close(rel, NoLock);
5508 * We only need to rewrite the table if at least one column needs to
5509 * be recomputed, or we are changing its persistence or access method.
5511 * There are two reasons for requiring a rewrite when changing
5512 * persistence: on one hand, we need to ensure that the buffers
5513 * belonging to each of the two relations are marked with or without
5514 * BM_PERMANENT properly. On the other hand, since rewriting creates
5515 * and assigns a new relfilenumber, we automatically create or drop an
5516 * init fork for the relation as appropriate.
5518 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5520 /* Build a temporary relation and copy data */
5521 Relation OldHeap;
5522 Oid OIDNewHeap;
5523 Oid NewAccessMethod;
5524 Oid NewTableSpace;
5525 char persistence;
5527 OldHeap = table_open(tab->relid, NoLock);
5530 * We don't support rewriting of system catalogs; there are too
5531 * many corner cases and too little benefit. In particular this
5532 * is certainly not going to work for mapped catalogs.
5534 if (IsSystemRelation(OldHeap))
5535 ereport(ERROR,
5536 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5537 errmsg("cannot rewrite system relation \"%s\"",
5538 RelationGetRelationName(OldHeap))));
5540 if (RelationIsUsedAsCatalogTable(OldHeap))
5541 ereport(ERROR,
5542 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5543 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5544 RelationGetRelationName(OldHeap))));
5547 * Don't allow rewrite on temp tables of other backends ... their
5548 * local buffer manager is not going to cope.
5550 if (RELATION_IS_OTHER_TEMP(OldHeap))
5551 ereport(ERROR,
5552 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5553 errmsg("cannot rewrite temporary tables of other sessions")));
5556 * Select destination tablespace (same as original unless user
5557 * requested a change)
5559 if (tab->newTableSpace)
5560 NewTableSpace = tab->newTableSpace;
5561 else
5562 NewTableSpace = OldHeap->rd_rel->reltablespace;
5565 * Select destination access method (same as original unless user
5566 * requested a change)
5568 if (OidIsValid(tab->newAccessMethod))
5569 NewAccessMethod = tab->newAccessMethod;
5570 else
5571 NewAccessMethod = OldHeap->rd_rel->relam;
5574 * Select persistence of transient table (same as original unless
5575 * user requested a change)
5577 persistence = tab->chgPersistence ?
5578 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5580 table_close(OldHeap, NoLock);
5583 * Fire off an Event Trigger now, before actually rewriting the
5584 * table.
5586 * We don't support Event Trigger for nested commands anywhere,
5587 * here included, and parsetree is given NULL when coming from
5588 * AlterTableInternal.
5590 * And fire it only once.
5592 if (parsetree)
5593 EventTriggerTableRewrite((Node *) parsetree,
5594 tab->relid,
5595 tab->rewrite);
5598 * Create transient table that will receive the modified data.
5600 * Ensure it is marked correctly as logged or unlogged. We have
5601 * to do this here so that buffers for the new relfilenumber will
5602 * have the right persistence set, and at the same time ensure
5603 * that the original filenumbers's buffers will get read in with
5604 * the correct setting (i.e. the original one). Otherwise a
5605 * rollback after the rewrite would possibly result with buffers
5606 * for the original filenumbers having the wrong persistence
5607 * setting.
5609 * NB: This relies on swap_relation_files() also swapping the
5610 * persistence. That wouldn't work for pg_class, but that can't be
5611 * unlogged anyway.
5613 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5614 persistence, lockmode);
5617 * Copy the heap data into the new table with the desired
5618 * modifications, and test the current data within the table
5619 * against new constraints generated by ALTER TABLE commands.
5621 ATRewriteTable(tab, OIDNewHeap, lockmode);
5624 * Swap the physical files of the old and new heaps, then rebuild
5625 * indexes and discard the old heap. We can use RecentXmin for
5626 * the table's new relfrozenxid because we rewrote all the tuples
5627 * in ATRewriteTable, so no older Xid remains in the table. Also,
5628 * we never try to swap toast tables by content, since we have no
5629 * interest in letting this code work on system catalogs.
5631 finish_heap_swap(tab->relid, OIDNewHeap,
5632 false, false, true,
5633 !OidIsValid(tab->newTableSpace),
5634 RecentXmin,
5635 ReadNextMultiXactId(),
5636 persistence);
5638 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5640 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5642 if (tab->chgPersistence)
5643 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5645 else
5648 * If required, test the current data within the table against new
5649 * constraints generated by ALTER TABLE commands, but don't
5650 * rebuild data.
5652 if (tab->constraints != NIL || tab->verify_new_notnull ||
5653 tab->partition_constraint != NULL)
5654 ATRewriteTable(tab, InvalidOid, lockmode);
5657 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5658 * just do a block-by-block copy.
5660 if (tab->newTableSpace)
5661 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5665 * Also change persistence of owned sequences, so that it matches the
5666 * table persistence.
5668 if (tab->chgPersistence)
5670 List *seqlist = getOwnedSequences(tab->relid);
5671 ListCell *lc;
5673 foreach(lc, seqlist)
5675 Oid seq_relid = lfirst_oid(lc);
5677 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5683 * Foreign key constraints are checked in a final pass, since (a) it's
5684 * generally best to examine each one separately, and (b) it's at least
5685 * theoretically possible that we have changed both relations of the
5686 * foreign key, and we'd better have finished both rewrites before we try
5687 * to read the tables.
5689 foreach(ltab, *wqueue)
5691 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5692 Relation rel = NULL;
5693 ListCell *lcon;
5695 /* Relations without storage may be ignored here too */
5696 if (!RELKIND_HAS_STORAGE(tab->relkind))
5697 continue;
5699 foreach(lcon, tab->constraints)
5701 NewConstraint *con = lfirst(lcon);
5703 if (con->contype == CONSTR_FOREIGN)
5705 Constraint *fkconstraint = (Constraint *) con->qual;
5706 Relation refrel;
5708 if (rel == NULL)
5710 /* Long since locked, no need for another */
5711 rel = table_open(tab->relid, NoLock);
5714 refrel = table_open(con->refrelid, RowShareLock);
5716 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5717 con->refindid,
5718 con->conid);
5721 * No need to mark the constraint row as validated, we did
5722 * that when we inserted the row earlier.
5725 table_close(refrel, NoLock);
5729 if (rel)
5730 table_close(rel, NoLock);
5733 /* Finally, run any afterStmts that were queued up */
5734 foreach(ltab, *wqueue)
5736 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5737 ListCell *lc;
5739 foreach(lc, tab->afterStmts)
5741 Node *stmt = (Node *) lfirst(lc);
5743 ProcessUtilityForAlterTable(stmt, context);
5744 CommandCounterIncrement();
5750 * ATRewriteTable: scan or rewrite one table
5752 * OIDNewHeap is InvalidOid if we don't need to rewrite
5754 static void
5755 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5757 Relation oldrel;
5758 Relation newrel;
5759 TupleDesc oldTupDesc;
5760 TupleDesc newTupDesc;
5761 bool needscan = false;
5762 List *notnull_attrs;
5763 int i;
5764 ListCell *l;
5765 EState *estate;
5766 CommandId mycid;
5767 BulkInsertState bistate;
5768 int ti_options;
5769 ExprState *partqualstate = NULL;
5772 * Open the relation(s). We have surely already locked the existing
5773 * table.
5775 oldrel = table_open(tab->relid, NoLock);
5776 oldTupDesc = tab->oldDesc;
5777 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
5779 if (OidIsValid(OIDNewHeap))
5780 newrel = table_open(OIDNewHeap, lockmode);
5781 else
5782 newrel = NULL;
5785 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
5786 * is empty, so don't bother using it.
5788 if (newrel)
5790 mycid = GetCurrentCommandId(true);
5791 bistate = GetBulkInsertState();
5792 ti_options = TABLE_INSERT_SKIP_FSM;
5794 else
5796 /* keep compiler quiet about using these uninitialized */
5797 mycid = 0;
5798 bistate = NULL;
5799 ti_options = 0;
5803 * Generate the constraint and default execution states
5806 estate = CreateExecutorState();
5808 /* Build the needed expression execution states */
5809 foreach(l, tab->constraints)
5811 NewConstraint *con = lfirst(l);
5813 switch (con->contype)
5815 case CONSTR_CHECK:
5816 needscan = true;
5817 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
5818 break;
5819 case CONSTR_FOREIGN:
5820 /* Nothing to do here */
5821 break;
5822 default:
5823 elog(ERROR, "unrecognized constraint type: %d",
5824 (int) con->contype);
5828 /* Build expression execution states for partition check quals */
5829 if (tab->partition_constraint)
5831 needscan = true;
5832 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
5835 foreach(l, tab->newvals)
5837 NewColumnValue *ex = lfirst(l);
5839 /* expr already planned */
5840 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
5843 notnull_attrs = NIL;
5844 if (newrel || tab->verify_new_notnull)
5847 * If we are rebuilding the tuples OR if we added any new but not
5848 * verified NOT NULL constraints, check all not-null constraints. This
5849 * is a bit of overkill but it minimizes risk of bugs, and
5850 * heap_attisnull is a pretty cheap test anyway.
5852 for (i = 0; i < newTupDesc->natts; i++)
5854 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
5856 if (attr->attnotnull && !attr->attisdropped)
5857 notnull_attrs = lappend_int(notnull_attrs, i);
5859 if (notnull_attrs)
5860 needscan = true;
5863 if (newrel || needscan)
5865 ExprContext *econtext;
5866 TupleTableSlot *oldslot;
5867 TupleTableSlot *newslot;
5868 TableScanDesc scan;
5869 MemoryContext oldCxt;
5870 List *dropped_attrs = NIL;
5871 ListCell *lc;
5872 Snapshot snapshot;
5874 if (newrel)
5875 ereport(DEBUG1,
5876 (errmsg_internal("rewriting table \"%s\"",
5877 RelationGetRelationName(oldrel))));
5878 else
5879 ereport(DEBUG1,
5880 (errmsg_internal("verifying table \"%s\"",
5881 RelationGetRelationName(oldrel))));
5883 if (newrel)
5886 * All predicate locks on the tuples or pages are about to be made
5887 * invalid, because we move tuples around. Promote them to
5888 * relation locks.
5890 TransferPredicateLocksToHeapRelation(oldrel);
5893 econtext = GetPerTupleExprContext(estate);
5896 * Create necessary tuple slots. When rewriting, two slots are needed,
5897 * otherwise one suffices. In the case where one slot suffices, we
5898 * need to use the new tuple descriptor, otherwise some constraints
5899 * can't be evaluated. Note that even when the tuple layout is the
5900 * same and no rewrite is required, the tupDescs might not be
5901 * (consider ADD COLUMN without a default).
5903 if (tab->rewrite)
5905 Assert(newrel != NULL);
5906 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
5907 table_slot_callbacks(oldrel));
5908 newslot = MakeSingleTupleTableSlot(newTupDesc,
5909 table_slot_callbacks(newrel));
5912 * Set all columns in the new slot to NULL initially, to ensure
5913 * columns added as part of the rewrite are initialized to NULL.
5914 * That is necessary as tab->newvals will not contain an
5915 * expression for columns with a NULL default, e.g. when adding a
5916 * column without a default together with a column with a default
5917 * requiring an actual rewrite.
5919 ExecStoreAllNullTuple(newslot);
5921 else
5923 oldslot = MakeSingleTupleTableSlot(newTupDesc,
5924 table_slot_callbacks(oldrel));
5925 newslot = NULL;
5929 * Any attributes that are dropped according to the new tuple
5930 * descriptor can be set to NULL. We precompute the list of dropped
5931 * attributes to avoid needing to do so in the per-tuple loop.
5933 for (i = 0; i < newTupDesc->natts; i++)
5935 if (TupleDescAttr(newTupDesc, i)->attisdropped)
5936 dropped_attrs = lappend_int(dropped_attrs, i);
5940 * Scan through the rows, generating a new row if needed and then
5941 * checking all the constraints.
5943 snapshot = RegisterSnapshot(GetLatestSnapshot());
5944 scan = table_beginscan(oldrel, snapshot, 0, NULL);
5947 * Switch to per-tuple memory context and reset it for each tuple
5948 * produced, so we don't leak memory.
5950 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
5952 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
5954 TupleTableSlot *insertslot;
5956 if (tab->rewrite > 0)
5958 /* Extract data from old tuple */
5959 slot_getallattrs(oldslot);
5960 ExecClearTuple(newslot);
5962 /* copy attributes */
5963 memcpy(newslot->tts_values, oldslot->tts_values,
5964 sizeof(Datum) * oldslot->tts_nvalid);
5965 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
5966 sizeof(bool) * oldslot->tts_nvalid);
5968 /* Set dropped attributes to null in new tuple */
5969 foreach(lc, dropped_attrs)
5970 newslot->tts_isnull[lfirst_int(lc)] = true;
5973 * Constraints and GENERATED expressions might reference the
5974 * tableoid column, so fill tts_tableOid with the desired
5975 * value. (We must do this each time, because it gets
5976 * overwritten with newrel's OID during storing.)
5978 newslot->tts_tableOid = RelationGetRelid(oldrel);
5981 * Process supplied expressions to replace selected columns.
5983 * First, evaluate expressions whose inputs come from the old
5984 * tuple.
5986 econtext->ecxt_scantuple = oldslot;
5988 foreach(l, tab->newvals)
5990 NewColumnValue *ex = lfirst(l);
5992 if (ex->is_generated)
5993 continue;
5995 newslot->tts_values[ex->attnum - 1]
5996 = ExecEvalExpr(ex->exprstate,
5997 econtext,
5998 &newslot->tts_isnull[ex->attnum - 1]);
6001 ExecStoreVirtualTuple(newslot);
6004 * Now, evaluate any expressions whose inputs come from the
6005 * new tuple. We assume these columns won't reference each
6006 * other, so that there's no ordering dependency.
6008 econtext->ecxt_scantuple = newslot;
6010 foreach(l, tab->newvals)
6012 NewColumnValue *ex = lfirst(l);
6014 if (!ex->is_generated)
6015 continue;
6017 newslot->tts_values[ex->attnum - 1]
6018 = ExecEvalExpr(ex->exprstate,
6019 econtext,
6020 &newslot->tts_isnull[ex->attnum - 1]);
6023 insertslot = newslot;
6025 else
6028 * If there's no rewrite, old and new table are guaranteed to
6029 * have the same AM, so we can just use the old slot to verify
6030 * new constraints etc.
6032 insertslot = oldslot;
6035 /* Now check any constraints on the possibly-changed tuple */
6036 econtext->ecxt_scantuple = insertslot;
6038 foreach(l, notnull_attrs)
6040 int attn = lfirst_int(l);
6042 if (slot_attisnull(insertslot, attn + 1))
6044 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6046 ereport(ERROR,
6047 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6048 errmsg("column \"%s\" of relation \"%s\" contains null values",
6049 NameStr(attr->attname),
6050 RelationGetRelationName(oldrel)),
6051 errtablecol(oldrel, attn + 1)));
6055 foreach(l, tab->constraints)
6057 NewConstraint *con = lfirst(l);
6059 switch (con->contype)
6061 case CONSTR_CHECK:
6062 if (!ExecCheck(con->qualstate, econtext))
6063 ereport(ERROR,
6064 (errcode(ERRCODE_CHECK_VIOLATION),
6065 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6066 con->name,
6067 RelationGetRelationName(oldrel)),
6068 errtableconstraint(oldrel, con->name)));
6069 break;
6070 case CONSTR_FOREIGN:
6071 /* Nothing to do here */
6072 break;
6073 default:
6074 elog(ERROR, "unrecognized constraint type: %d",
6075 (int) con->contype);
6079 if (partqualstate && !ExecCheck(partqualstate, econtext))
6081 if (tab->validate_default)
6082 ereport(ERROR,
6083 (errcode(ERRCODE_CHECK_VIOLATION),
6084 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6085 RelationGetRelationName(oldrel)),
6086 errtable(oldrel)));
6087 else
6088 ereport(ERROR,
6089 (errcode(ERRCODE_CHECK_VIOLATION),
6090 errmsg("partition constraint of relation \"%s\" is violated by some row",
6091 RelationGetRelationName(oldrel)),
6092 errtable(oldrel)));
6095 /* Write the tuple out to the new relation */
6096 if (newrel)
6097 table_tuple_insert(newrel, insertslot, mycid,
6098 ti_options, bistate);
6100 ResetExprContext(econtext);
6102 CHECK_FOR_INTERRUPTS();
6105 MemoryContextSwitchTo(oldCxt);
6106 table_endscan(scan);
6107 UnregisterSnapshot(snapshot);
6109 ExecDropSingleTupleTableSlot(oldslot);
6110 if (newslot)
6111 ExecDropSingleTupleTableSlot(newslot);
6114 FreeExecutorState(estate);
6116 table_close(oldrel, NoLock);
6117 if (newrel)
6119 FreeBulkInsertState(bistate);
6121 table_finish_bulk_insert(newrel, ti_options);
6123 table_close(newrel, NoLock);
6128 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6130 static AlteredTableInfo *
6131 ATGetQueueEntry(List **wqueue, Relation rel)
6133 Oid relid = RelationGetRelid(rel);
6134 AlteredTableInfo *tab;
6135 ListCell *ltab;
6137 foreach(ltab, *wqueue)
6139 tab = (AlteredTableInfo *) lfirst(ltab);
6140 if (tab->relid == relid)
6141 return tab;
6145 * Not there, so add it. Note that we make a copy of the relation's
6146 * existing descriptor before anything interesting can happen to it.
6148 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6149 tab->relid = relid;
6150 tab->rel = NULL; /* set later */
6151 tab->relkind = rel->rd_rel->relkind;
6152 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6153 tab->newAccessMethod = InvalidOid;
6154 tab->newTableSpace = InvalidOid;
6155 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6156 tab->chgPersistence = false;
6158 *wqueue = lappend(*wqueue, tab);
6160 return tab;
6163 static const char *
6164 alter_table_type_to_string(AlterTableType cmdtype)
6166 switch (cmdtype)
6168 case AT_AddColumn:
6169 case AT_AddColumnToView:
6170 return "ADD COLUMN";
6171 case AT_ColumnDefault:
6172 case AT_CookedColumnDefault:
6173 return "ALTER COLUMN ... SET DEFAULT";
6174 case AT_DropNotNull:
6175 return "ALTER COLUMN ... DROP NOT NULL";
6176 case AT_SetNotNull:
6177 return "ALTER COLUMN ... SET NOT NULL";
6178 case AT_DropExpression:
6179 return "ALTER COLUMN ... DROP EXPRESSION";
6180 case AT_CheckNotNull:
6181 return NULL; /* not real grammar */
6182 case AT_SetStatistics:
6183 return "ALTER COLUMN ... SET STATISTICS";
6184 case AT_SetOptions:
6185 return "ALTER COLUMN ... SET";
6186 case AT_ResetOptions:
6187 return "ALTER COLUMN ... RESET";
6188 case AT_SetStorage:
6189 return "ALTER COLUMN ... SET STORAGE";
6190 case AT_SetCompression:
6191 return "ALTER COLUMN ... SET COMPRESSION";
6192 case AT_DropColumn:
6193 return "DROP COLUMN";
6194 case AT_AddIndex:
6195 case AT_ReAddIndex:
6196 return NULL; /* not real grammar */
6197 case AT_AddConstraint:
6198 case AT_ReAddConstraint:
6199 case AT_ReAddDomainConstraint:
6200 case AT_AddIndexConstraint:
6201 return "ADD CONSTRAINT";
6202 case AT_AlterConstraint:
6203 return "ALTER CONSTRAINT";
6204 case AT_ValidateConstraint:
6205 return "VALIDATE CONSTRAINT";
6206 case AT_DropConstraint:
6207 return "DROP CONSTRAINT";
6208 case AT_ReAddComment:
6209 return NULL; /* not real grammar */
6210 case AT_AlterColumnType:
6211 return "ALTER COLUMN ... SET DATA TYPE";
6212 case AT_AlterColumnGenericOptions:
6213 return "ALTER COLUMN ... OPTIONS";
6214 case AT_ChangeOwner:
6215 return "OWNER TO";
6216 case AT_ClusterOn:
6217 return "CLUSTER ON";
6218 case AT_DropCluster:
6219 return "SET WITHOUT CLUSTER";
6220 case AT_SetAccessMethod:
6221 return "SET ACCESS METHOD";
6222 case AT_SetLogged:
6223 return "SET LOGGED";
6224 case AT_SetUnLogged:
6225 return "SET UNLOGGED";
6226 case AT_DropOids:
6227 return "SET WITHOUT OIDS";
6228 case AT_SetTableSpace:
6229 return "SET TABLESPACE";
6230 case AT_SetRelOptions:
6231 return "SET";
6232 case AT_ResetRelOptions:
6233 return "RESET";
6234 case AT_ReplaceRelOptions:
6235 return NULL; /* not real grammar */
6236 case AT_EnableTrig:
6237 return "ENABLE TRIGGER";
6238 case AT_EnableAlwaysTrig:
6239 return "ENABLE ALWAYS TRIGGER";
6240 case AT_EnableReplicaTrig:
6241 return "ENABLE REPLICA TRIGGER";
6242 case AT_DisableTrig:
6243 return "DISABLE TRIGGER";
6244 case AT_EnableTrigAll:
6245 return "ENABLE TRIGGER ALL";
6246 case AT_DisableTrigAll:
6247 return "DISABLE TRIGGER ALL";
6248 case AT_EnableTrigUser:
6249 return "ENABLE TRIGGER USER";
6250 case AT_DisableTrigUser:
6251 return "DISABLE TRIGGER USER";
6252 case AT_EnableRule:
6253 return "ENABLE RULE";
6254 case AT_EnableAlwaysRule:
6255 return "ENABLE ALWAYS RULE";
6256 case AT_EnableReplicaRule:
6257 return "ENABLE REPLICA RULE";
6258 case AT_DisableRule:
6259 return "DISABLE RULE";
6260 case AT_AddInherit:
6261 return "INHERIT";
6262 case AT_DropInherit:
6263 return "NO INHERIT";
6264 case AT_AddOf:
6265 return "OF";
6266 case AT_DropOf:
6267 return "NOT OF";
6268 case AT_ReplicaIdentity:
6269 return "REPLICA IDENTITY";
6270 case AT_EnableRowSecurity:
6271 return "ENABLE ROW SECURITY";
6272 case AT_DisableRowSecurity:
6273 return "DISABLE ROW SECURITY";
6274 case AT_ForceRowSecurity:
6275 return "FORCE ROW SECURITY";
6276 case AT_NoForceRowSecurity:
6277 return "NO FORCE ROW SECURITY";
6278 case AT_GenericOptions:
6279 return "OPTIONS";
6280 case AT_AttachPartition:
6281 return "ATTACH PARTITION";
6282 case AT_DetachPartition:
6283 return "DETACH PARTITION";
6284 case AT_DetachPartitionFinalize:
6285 return "DETACH PARTITION ... FINALIZE";
6286 case AT_AddIdentity:
6287 return "ALTER COLUMN ... ADD IDENTITY";
6288 case AT_SetIdentity:
6289 return "ALTER COLUMN ... SET";
6290 case AT_DropIdentity:
6291 return "ALTER COLUMN ... DROP IDENTITY";
6292 case AT_ReAddStatistics:
6293 return NULL; /* not real grammar */
6296 return NULL;
6300 * ATSimplePermissions
6302 * - Ensure that it is a relation (or possibly a view)
6303 * - Ensure this user is the owner
6304 * - Ensure that it is not a system table
6306 static void
6307 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6309 int actual_target;
6311 switch (rel->rd_rel->relkind)
6313 case RELKIND_RELATION:
6314 case RELKIND_PARTITIONED_TABLE:
6315 actual_target = ATT_TABLE;
6316 break;
6317 case RELKIND_VIEW:
6318 actual_target = ATT_VIEW;
6319 break;
6320 case RELKIND_MATVIEW:
6321 actual_target = ATT_MATVIEW;
6322 break;
6323 case RELKIND_INDEX:
6324 actual_target = ATT_INDEX;
6325 break;
6326 case RELKIND_PARTITIONED_INDEX:
6327 actual_target = ATT_PARTITIONED_INDEX;
6328 break;
6329 case RELKIND_COMPOSITE_TYPE:
6330 actual_target = ATT_COMPOSITE_TYPE;
6331 break;
6332 case RELKIND_FOREIGN_TABLE:
6333 actual_target = ATT_FOREIGN_TABLE;
6334 break;
6335 case RELKIND_SEQUENCE:
6336 actual_target = ATT_SEQUENCE;
6337 break;
6338 default:
6339 actual_target = 0;
6340 break;
6343 /* Wrong target type? */
6344 if ((actual_target & allowed_targets) == 0)
6346 const char *action_str = alter_table_type_to_string(cmdtype);
6348 if (action_str)
6349 ereport(ERROR,
6350 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6351 /* translator: %s is a group of some SQL keywords */
6352 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6353 action_str, RelationGetRelationName(rel)),
6354 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6355 else
6356 /* internal error? */
6357 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6358 RelationGetRelationName(rel));
6361 /* Permissions checks */
6362 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6363 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6364 RelationGetRelationName(rel));
6366 if (!allowSystemTableMods && IsSystemRelation(rel))
6367 ereport(ERROR,
6368 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6369 errmsg("permission denied: \"%s\" is a system catalog",
6370 RelationGetRelationName(rel))));
6374 * ATSimpleRecursion
6376 * Simple table recursion sufficient for most ALTER TABLE operations.
6377 * All direct and indirect children are processed in an unspecified order.
6378 * Note that if a child inherits from the original table via multiple
6379 * inheritance paths, it will be visited just once.
6381 static void
6382 ATSimpleRecursion(List **wqueue, Relation rel,
6383 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6384 AlterTableUtilityContext *context)
6387 * Propagate to children, if desired and if there are (or might be) any
6388 * children.
6390 if (recurse && rel->rd_rel->relhassubclass)
6392 Oid relid = RelationGetRelid(rel);
6393 ListCell *child;
6394 List *children;
6396 children = find_all_inheritors(relid, lockmode, NULL);
6399 * find_all_inheritors does the recursive search of the inheritance
6400 * hierarchy, so all we have to do is process all of the relids in the
6401 * list that it returns.
6403 foreach(child, children)
6405 Oid childrelid = lfirst_oid(child);
6406 Relation childrel;
6408 if (childrelid == relid)
6409 continue;
6410 /* find_all_inheritors already got lock */
6411 childrel = relation_open(childrelid, NoLock);
6412 CheckTableNotInUse(childrel, "ALTER TABLE");
6413 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6414 relation_close(childrel, NoLock);
6420 * Obtain list of partitions of the given table, locking them all at the given
6421 * lockmode and ensuring that they all pass CheckTableNotInUse.
6423 * This function is a no-op if the given relation is not a partitioned table;
6424 * in particular, nothing is done if it's a legacy inheritance parent.
6426 static void
6427 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6429 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6431 List *inh;
6432 ListCell *cell;
6434 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6435 /* first element is the parent rel; must ignore it */
6436 for_each_from(cell, inh, 1)
6438 Relation childrel;
6440 /* find_all_inheritors already got lock */
6441 childrel = table_open(lfirst_oid(cell), NoLock);
6442 CheckTableNotInUse(childrel, "ALTER TABLE");
6443 table_close(childrel, NoLock);
6445 list_free(inh);
6450 * ATTypedTableRecursion
6452 * Propagate ALTER TYPE operations to the typed tables of that type.
6453 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6454 * recursion to inheritance children of the typed tables.
6456 static void
6457 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6458 LOCKMODE lockmode, AlterTableUtilityContext *context)
6460 ListCell *child;
6461 List *children;
6463 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6465 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6466 RelationGetRelationName(rel),
6467 cmd->behavior);
6469 foreach(child, children)
6471 Oid childrelid = lfirst_oid(child);
6472 Relation childrel;
6474 childrel = relation_open(childrelid, lockmode);
6475 CheckTableNotInUse(childrel, "ALTER TABLE");
6476 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6477 relation_close(childrel, NoLock);
6483 * find_composite_type_dependencies
6485 * Check to see if the type "typeOid" is being used as a column in some table
6486 * (possibly nested several levels deep in composite types, arrays, etc!).
6487 * Eventually, we'd like to propagate the check or rewrite operation
6488 * into such tables, but for now, just error out if we find any.
6490 * Caller should provide either the associated relation of a rowtype,
6491 * or a type name (not both) for use in the error message, if any.
6493 * Note that "typeOid" is not necessarily a composite type; it could also be
6494 * another container type such as an array or range, or a domain over one of
6495 * these things. The name of this function is therefore somewhat historical,
6496 * but it's not worth changing.
6498 * We assume that functions and views depending on the type are not reasons
6499 * to reject the ALTER. (How safe is this really?)
6501 void
6502 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6503 const char *origTypeName)
6505 Relation depRel;
6506 ScanKeyData key[2];
6507 SysScanDesc depScan;
6508 HeapTuple depTup;
6510 /* since this function recurses, it could be driven to stack overflow */
6511 check_stack_depth();
6514 * We scan pg_depend to find those things that depend on the given type.
6515 * (We assume we can ignore refobjsubid for a type.)
6517 depRel = table_open(DependRelationId, AccessShareLock);
6519 ScanKeyInit(&key[0],
6520 Anum_pg_depend_refclassid,
6521 BTEqualStrategyNumber, F_OIDEQ,
6522 ObjectIdGetDatum(TypeRelationId));
6523 ScanKeyInit(&key[1],
6524 Anum_pg_depend_refobjid,
6525 BTEqualStrategyNumber, F_OIDEQ,
6526 ObjectIdGetDatum(typeOid));
6528 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6529 NULL, 2, key);
6531 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6533 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6534 Relation rel;
6535 TupleDesc tupleDesc;
6536 Form_pg_attribute att;
6538 /* Check for directly dependent types */
6539 if (pg_depend->classid == TypeRelationId)
6542 * This must be an array, domain, or range containing the given
6543 * type, so recursively check for uses of this type. Note that
6544 * any error message will mention the original type not the
6545 * container; this is intentional.
6547 find_composite_type_dependencies(pg_depend->objid,
6548 origRelation, origTypeName);
6549 continue;
6552 /* Else, ignore dependees that aren't relations */
6553 if (pg_depend->classid != RelationRelationId)
6554 continue;
6556 rel = relation_open(pg_depend->objid, AccessShareLock);
6557 tupleDesc = RelationGetDescr(rel);
6560 * If objsubid identifies a specific column, refer to that in error
6561 * messages. Otherwise, search to see if there's a user column of the
6562 * type. (We assume system columns are never of interesting types.)
6563 * The search is needed because an index containing an expression
6564 * column of the target type will just be recorded as a whole-relation
6565 * dependency. If we do not find a column of the type, the dependency
6566 * must indicate that the type is transiently referenced in an index
6567 * expression but not stored on disk, which we assume is OK, just as
6568 * we do for references in views. (It could also be that the target
6569 * type is embedded in some container type that is stored in an index
6570 * column, but the previous recursion should catch such cases.)
6572 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6573 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6574 else
6576 att = NULL;
6577 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6579 att = TupleDescAttr(tupleDesc, attno - 1);
6580 if (att->atttypid == typeOid && !att->attisdropped)
6581 break;
6582 att = NULL;
6584 if (att == NULL)
6586 /* No such column, so assume OK */
6587 relation_close(rel, AccessShareLock);
6588 continue;
6593 * We definitely should reject if the relation has storage. If it's
6594 * partitioned, then perhaps we don't have to reject: if there are
6595 * partitions then we'll fail when we find one, else there is no
6596 * stored data to worry about. However, it's possible that the type
6597 * change would affect conclusions about whether the type is sortable
6598 * or hashable and thus (if it's a partitioning column) break the
6599 * partitioning rule. For now, reject for partitioned rels too.
6601 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6602 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6604 if (origTypeName)
6605 ereport(ERROR,
6606 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6607 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6608 origTypeName,
6609 RelationGetRelationName(rel),
6610 NameStr(att->attname))));
6611 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6612 ereport(ERROR,
6613 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6614 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6615 RelationGetRelationName(origRelation),
6616 RelationGetRelationName(rel),
6617 NameStr(att->attname))));
6618 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6619 ereport(ERROR,
6620 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6621 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6622 RelationGetRelationName(origRelation),
6623 RelationGetRelationName(rel),
6624 NameStr(att->attname))));
6625 else
6626 ereport(ERROR,
6627 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6628 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6629 RelationGetRelationName(origRelation),
6630 RelationGetRelationName(rel),
6631 NameStr(att->attname))));
6633 else if (OidIsValid(rel->rd_rel->reltype))
6636 * A view or composite type itself isn't a problem, but we must
6637 * recursively check for indirect dependencies via its rowtype.
6639 find_composite_type_dependencies(rel->rd_rel->reltype,
6640 origRelation, origTypeName);
6643 relation_close(rel, AccessShareLock);
6646 systable_endscan(depScan);
6648 relation_close(depRel, AccessShareLock);
6653 * find_typed_table_dependencies
6655 * Check to see if a composite type is being used as the type of a
6656 * typed table. Abort if any are found and behavior is RESTRICT.
6657 * Else return the list of tables.
6659 static List *
6660 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6662 Relation classRel;
6663 ScanKeyData key[1];
6664 TableScanDesc scan;
6665 HeapTuple tuple;
6666 List *result = NIL;
6668 classRel = table_open(RelationRelationId, AccessShareLock);
6670 ScanKeyInit(&key[0],
6671 Anum_pg_class_reloftype,
6672 BTEqualStrategyNumber, F_OIDEQ,
6673 ObjectIdGetDatum(typeOid));
6675 scan = table_beginscan_catalog(classRel, 1, key);
6677 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6679 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6681 if (behavior == DROP_RESTRICT)
6682 ereport(ERROR,
6683 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6684 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6685 typeName),
6686 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6687 else
6688 result = lappend_oid(result, classform->oid);
6691 table_endscan(scan);
6692 table_close(classRel, AccessShareLock);
6694 return result;
6699 * check_of_type
6701 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6702 * isn't suitable, throw an error. Currently, we require that the type
6703 * originated with CREATE TYPE AS. We could support any row type, but doing so
6704 * would require handling a number of extra corner cases in the DDL commands.
6705 * (Also, allowing domain-over-composite would open up a can of worms about
6706 * whether and how the domain's constraints should apply to derived tables.)
6708 void
6709 check_of_type(HeapTuple typetuple)
6711 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6712 bool typeOk = false;
6714 if (typ->typtype == TYPTYPE_COMPOSITE)
6716 Relation typeRelation;
6718 Assert(OidIsValid(typ->typrelid));
6719 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6720 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6723 * Close the parent rel, but keep our AccessShareLock on it until xact
6724 * commit. That will prevent someone else from deleting or ALTERing
6725 * the type before the typed table creation/conversion commits.
6727 relation_close(typeRelation, NoLock);
6729 if (!typeOk)
6730 ereport(ERROR,
6731 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6732 errmsg("type %s is not a composite type",
6733 format_type_be(typ->oid))));
6738 * ALTER TABLE ADD COLUMN
6740 * Adds an additional attribute to a relation making the assumption that
6741 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6742 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6743 * AlterTableCmd's.
6745 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6746 * have to decide at runtime whether to recurse or not depending on whether we
6747 * actually add a column or merely merge with an existing column. (We can't
6748 * check this in a static pre-pass because it won't handle multiple inheritance
6749 * situations correctly.)
6751 static void
6752 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6753 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6754 AlterTableUtilityContext *context)
6756 if (rel->rd_rel->reloftype && !recursing)
6757 ereport(ERROR,
6758 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6759 errmsg("cannot add column to typed table")));
6761 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6762 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
6764 if (recurse && !is_view)
6765 cmd->recurse = true;
6769 * Add a column to a table. The return value is the address of the
6770 * new column in the parent relation.
6772 * cmd is pass-by-ref so that we can replace it with the parse-transformed
6773 * copy (but that happens only after we check for IF NOT EXISTS).
6775 static ObjectAddress
6776 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
6777 AlterTableCmd **cmd,
6778 bool recurse, bool recursing,
6779 LOCKMODE lockmode, int cur_pass,
6780 AlterTableUtilityContext *context)
6782 Oid myrelid = RelationGetRelid(rel);
6783 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
6784 bool if_not_exists = (*cmd)->missing_ok;
6785 Relation pgclass,
6786 attrdesc;
6787 HeapTuple reltup;
6788 FormData_pg_attribute attribute;
6789 int newattnum;
6790 char relkind;
6791 HeapTuple typeTuple;
6792 Oid typeOid;
6793 int32 typmod;
6794 Oid collOid;
6795 Form_pg_type tform;
6796 Expr *defval;
6797 List *children;
6798 ListCell *child;
6799 AlterTableCmd *childcmd;
6800 AclResult aclresult;
6801 ObjectAddress address;
6802 TupleDesc tupdesc;
6803 FormData_pg_attribute *aattr[] = {&attribute};
6805 /* At top level, permission check was done in ATPrepCmd, else do it */
6806 if (recursing)
6807 ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
6809 if (rel->rd_rel->relispartition && !recursing)
6810 ereport(ERROR,
6811 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6812 errmsg("cannot add column to a partition")));
6814 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
6817 * Are we adding the column to a recursion child? If so, check whether to
6818 * merge with an existing definition for the column. If we do merge, we
6819 * must not recurse. Children will already have the column, and recursing
6820 * into them would mess up attinhcount.
6822 if (colDef->inhcount > 0)
6824 HeapTuple tuple;
6826 /* Does child already have a column by this name? */
6827 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
6828 if (HeapTupleIsValid(tuple))
6830 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
6831 Oid ctypeId;
6832 int32 ctypmod;
6833 Oid ccollid;
6835 /* Child column must match on type, typmod, and collation */
6836 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
6837 if (ctypeId != childatt->atttypid ||
6838 ctypmod != childatt->atttypmod)
6839 ereport(ERROR,
6840 (errcode(ERRCODE_DATATYPE_MISMATCH),
6841 errmsg("child table \"%s\" has different type for column \"%s\"",
6842 RelationGetRelationName(rel), colDef->colname)));
6843 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
6844 if (ccollid != childatt->attcollation)
6845 ereport(ERROR,
6846 (errcode(ERRCODE_COLLATION_MISMATCH),
6847 errmsg("child table \"%s\" has different collation for column \"%s\"",
6848 RelationGetRelationName(rel), colDef->colname),
6849 errdetail("\"%s\" versus \"%s\"",
6850 get_collation_name(ccollid),
6851 get_collation_name(childatt->attcollation))));
6853 /* Bump the existing child att's inhcount */
6854 childatt->attinhcount++;
6855 if (childatt->attinhcount < 0)
6856 ereport(ERROR,
6857 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6858 errmsg("too many inheritance parents"));
6859 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
6861 heap_freetuple(tuple);
6863 /* Inform the user about the merge */
6864 ereport(NOTICE,
6865 (errmsg("merging definition of column \"%s\" for child \"%s\"",
6866 colDef->colname, RelationGetRelationName(rel))));
6868 table_close(attrdesc, RowExclusiveLock);
6869 return InvalidObjectAddress;
6873 /* skip if the name already exists and if_not_exists is true */
6874 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
6876 table_close(attrdesc, RowExclusiveLock);
6877 return InvalidObjectAddress;
6881 * Okay, we need to add the column, so go ahead and do parse
6882 * transformation. This can result in queueing up, or even immediately
6883 * executing, subsidiary operations (such as creation of unique indexes);
6884 * so we mustn't do it until we have made the if_not_exists check.
6886 * When recursing, the command was already transformed and we needn't do
6887 * so again. Also, if context isn't given we can't transform. (That
6888 * currently happens only for AT_AddColumnToView; we expect that view.c
6889 * passed us a ColumnDef that doesn't need work.)
6891 if (context != NULL && !recursing)
6893 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
6894 cur_pass, context);
6895 Assert(*cmd != NULL);
6896 colDef = castNode(ColumnDef, (*cmd)->def);
6900 * Cannot add identity column if table has children, because identity does
6901 * not inherit. (Adding column and identity separately will work.)
6903 if (colDef->identity &&
6904 recurse &&
6905 find_inheritance_children(myrelid, NoLock) != NIL)
6906 ereport(ERROR,
6907 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
6908 errmsg("cannot recursively add identity column to table that has child tables")));
6910 pgclass = table_open(RelationRelationId, RowExclusiveLock);
6912 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
6913 if (!HeapTupleIsValid(reltup))
6914 elog(ERROR, "cache lookup failed for relation %u", myrelid);
6915 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
6917 /* Determine the new attribute's number */
6918 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
6919 if (newattnum > MaxHeapAttributeNumber)
6920 ereport(ERROR,
6921 (errcode(ERRCODE_TOO_MANY_COLUMNS),
6922 errmsg("tables can have at most %d columns",
6923 MaxHeapAttributeNumber)));
6925 typeTuple = typenameType(NULL, colDef->typeName, &typmod);
6926 tform = (Form_pg_type) GETSTRUCT(typeTuple);
6927 typeOid = tform->oid;
6929 aclresult = object_aclcheck(TypeRelationId, typeOid, GetUserId(), ACL_USAGE);
6930 if (aclresult != ACLCHECK_OK)
6931 aclcheck_error_type(aclresult, typeOid);
6933 collOid = GetColumnDefCollation(NULL, colDef, typeOid);
6935 /* make sure datatype is legal for a column */
6936 CheckAttributeType(colDef->colname, typeOid, collOid,
6937 list_make1_oid(rel->rd_rel->reltype),
6941 * Construct new attribute's pg_attribute entry. (Variable-length fields
6942 * are handled by InsertPgAttributeTuples().)
6944 attribute.attrelid = myrelid;
6945 namestrcpy(&(attribute.attname), colDef->colname);
6946 attribute.atttypid = typeOid;
6947 attribute.attstattarget = -1;
6948 attribute.attlen = tform->typlen;
6949 attribute.attnum = newattnum;
6950 if (list_length(colDef->typeName->arrayBounds) > PG_INT16_MAX)
6951 ereport(ERROR,
6952 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
6953 errmsg("too many array dimensions"));
6954 attribute.attndims = list_length(colDef->typeName->arrayBounds);
6955 attribute.atttypmod = typmod;
6956 attribute.attbyval = tform->typbyval;
6957 attribute.attalign = tform->typalign;
6958 if (colDef->storage_name)
6959 attribute.attstorage = GetAttributeStorage(typeOid, colDef->storage_name);
6960 else
6961 attribute.attstorage = tform->typstorage;
6962 attribute.attcompression = GetAttributeCompression(typeOid,
6963 colDef->compression);
6964 attribute.attnotnull = colDef->is_not_null;
6965 attribute.atthasdef = false;
6966 attribute.atthasmissing = false;
6967 attribute.attidentity = colDef->identity;
6968 attribute.attgenerated = colDef->generated;
6969 attribute.attisdropped = false;
6970 attribute.attislocal = colDef->is_local;
6971 attribute.attinhcount = colDef->inhcount;
6972 attribute.attcollation = collOid;
6974 ReleaseSysCache(typeTuple);
6976 tupdesc = CreateTupleDesc(lengthof(aattr), (FormData_pg_attribute **) &aattr);
6978 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
6980 table_close(attrdesc, RowExclusiveLock);
6983 * Update pg_class tuple as appropriate
6985 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
6987 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
6989 heap_freetuple(reltup);
6991 /* Post creation hook for new attribute */
6992 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
6994 table_close(pgclass, RowExclusiveLock);
6996 /* Make the attribute's catalog entry visible */
6997 CommandCounterIncrement();
7000 * Store the DEFAULT, if any, in the catalogs
7002 if (colDef->raw_default)
7004 RawColumnDefault *rawEnt;
7006 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7007 rawEnt->attnum = attribute.attnum;
7008 rawEnt->raw_default = copyObject(colDef->raw_default);
7011 * Attempt to skip a complete table rewrite by storing the specified
7012 * DEFAULT value outside of the heap. This may be disabled inside
7013 * AddRelationNewConstraints if the optimization cannot be applied.
7015 rawEnt->missingMode = (!colDef->generated);
7017 rawEnt->generated = colDef->generated;
7020 * This function is intended for CREATE TABLE, so it processes a
7021 * _list_ of defaults, but we just do one.
7023 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7024 false, true, false, NULL);
7026 /* Make the additional catalog changes visible */
7027 CommandCounterIncrement();
7030 * Did the request for a missing value work? If not we'll have to do a
7031 * rewrite
7033 if (!rawEnt->missingMode)
7034 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7038 * Tell Phase 3 to fill in the default expression, if there is one.
7040 * If there is no default, Phase 3 doesn't have to do anything, because
7041 * that effectively means that the default is NULL. The heap tuple access
7042 * routines always check for attnum > # of attributes in tuple, and return
7043 * NULL if so, so without any modification of the tuple data we will get
7044 * the effect of NULL values in the new column.
7046 * An exception occurs when the new column is of a domain type: the domain
7047 * might have a NOT NULL constraint, or a check constraint that indirectly
7048 * rejects nulls. If there are any domain constraints then we construct
7049 * an explicit NULL default value that will be passed through
7050 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7051 * rewriting the table which we really don't have to do, but the present
7052 * design of domain processing doesn't offer any simple way of checking
7053 * the constraints more directly.)
7055 * Note: we use build_column_default, and not just the cooked default
7056 * returned by AddRelationNewConstraints, so that the right thing happens
7057 * when a datatype's default applies.
7059 * Note: it might seem that this should happen at the end of Phase 2, so
7060 * that the effects of subsequent subcommands can be taken into account.
7061 * It's intentional that we do it now, though. The new column should be
7062 * filled according to what is said in the ADD COLUMN subcommand, so that
7063 * the effects are the same as if this subcommand had been run by itself
7064 * and the later subcommands had been issued in new ALTER TABLE commands.
7066 * We can skip this entirely for relations without storage, since Phase 3
7067 * is certainly not going to touch them. System attributes don't have
7068 * interesting defaults, either.
7070 if (RELKIND_HAS_STORAGE(relkind))
7073 * For an identity column, we can't use build_column_default(),
7074 * because the sequence ownership isn't set yet. So do it manually.
7076 if (colDef->identity)
7078 NextValueExpr *nve = makeNode(NextValueExpr);
7080 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7081 nve->typeId = typeOid;
7083 defval = (Expr *) nve;
7085 /* must do a rewrite for identity columns */
7086 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7088 else
7089 defval = (Expr *) build_column_default(rel, attribute.attnum);
7091 if (!defval && DomainHasConstraints(typeOid))
7093 Oid baseTypeId;
7094 int32 baseTypeMod;
7095 Oid baseTypeColl;
7097 baseTypeMod = typmod;
7098 baseTypeId = getBaseTypeAndTypmod(typeOid, &baseTypeMod);
7099 baseTypeColl = get_typcollation(baseTypeId);
7100 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7101 defval = (Expr *) coerce_to_target_type(NULL,
7102 (Node *) defval,
7103 baseTypeId,
7104 typeOid,
7105 typmod,
7106 COERCION_ASSIGNMENT,
7107 COERCE_IMPLICIT_CAST,
7108 -1);
7109 if (defval == NULL) /* should not happen */
7110 elog(ERROR, "failed to coerce base type to domain");
7113 if (defval)
7115 NewColumnValue *newval;
7117 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7118 newval->attnum = attribute.attnum;
7119 newval->expr = expression_planner(defval);
7120 newval->is_generated = (colDef->generated != '\0');
7122 tab->newvals = lappend(tab->newvals, newval);
7125 if (DomainHasConstraints(typeOid))
7126 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7128 if (!TupleDescAttr(rel->rd_att, attribute.attnum - 1)->atthasmissing)
7131 * If the new column is NOT NULL, and there is no missing value,
7132 * tell Phase 3 it needs to check for NULLs.
7134 tab->verify_new_notnull |= colDef->is_not_null;
7139 * Add needed dependency entries for the new column.
7141 add_column_datatype_dependency(myrelid, newattnum, attribute.atttypid);
7142 add_column_collation_dependency(myrelid, newattnum, attribute.attcollation);
7145 * Propagate to children as appropriate. Unlike most other ALTER
7146 * routines, we have to do this one level of recursion at a time; we can't
7147 * use find_all_inheritors to do it in one pass.
7149 children =
7150 find_inheritance_children(RelationGetRelid(rel), lockmode);
7153 * If we are told not to recurse, there had better not be any child
7154 * tables; else the addition would put them out of step.
7156 if (children && !recurse)
7157 ereport(ERROR,
7158 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7159 errmsg("column must be added to child tables too")));
7161 /* Children should see column as singly inherited */
7162 if (!recursing)
7164 childcmd = copyObject(*cmd);
7165 colDef = castNode(ColumnDef, childcmd->def);
7166 colDef->inhcount = 1;
7167 colDef->is_local = false;
7169 else
7170 childcmd = *cmd; /* no need to copy again */
7172 foreach(child, children)
7174 Oid childrelid = lfirst_oid(child);
7175 Relation childrel;
7176 AlteredTableInfo *childtab;
7178 /* find_inheritance_children already got lock */
7179 childrel = table_open(childrelid, NoLock);
7180 CheckTableNotInUse(childrel, "ALTER TABLE");
7182 /* Find or create work queue entry for this table */
7183 childtab = ATGetQueueEntry(wqueue, childrel);
7185 /* Recurse to child; return value is ignored */
7186 ATExecAddColumn(wqueue, childtab, childrel,
7187 &childcmd, recurse, true,
7188 lockmode, cur_pass, context);
7190 table_close(childrel, NoLock);
7193 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7194 return address;
7198 * If a new or renamed column will collide with the name of an existing
7199 * column and if_not_exists is false then error out, else do nothing.
7201 static bool
7202 check_for_column_name_collision(Relation rel, const char *colname,
7203 bool if_not_exists)
7205 HeapTuple attTuple;
7206 int attnum;
7209 * this test is deliberately not attisdropped-aware, since if one tries to
7210 * add a column matching a dropped column name, it's gonna fail anyway.
7212 attTuple = SearchSysCache2(ATTNAME,
7213 ObjectIdGetDatum(RelationGetRelid(rel)),
7214 PointerGetDatum(colname));
7215 if (!HeapTupleIsValid(attTuple))
7216 return true;
7218 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7219 ReleaseSysCache(attTuple);
7222 * We throw a different error message for conflicts with system column
7223 * names, since they are normally not shown and the user might otherwise
7224 * be confused about the reason for the conflict.
7226 if (attnum <= 0)
7227 ereport(ERROR,
7228 (errcode(ERRCODE_DUPLICATE_COLUMN),
7229 errmsg("column name \"%s\" conflicts with a system column name",
7230 colname)));
7231 else
7233 if (if_not_exists)
7235 ereport(NOTICE,
7236 (errcode(ERRCODE_DUPLICATE_COLUMN),
7237 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7238 colname, RelationGetRelationName(rel))));
7239 return false;
7242 ereport(ERROR,
7243 (errcode(ERRCODE_DUPLICATE_COLUMN),
7244 errmsg("column \"%s\" of relation \"%s\" already exists",
7245 colname, RelationGetRelationName(rel))));
7248 return true;
7252 * Install a column's dependency on its datatype.
7254 static void
7255 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7257 ObjectAddress myself,
7258 referenced;
7260 myself.classId = RelationRelationId;
7261 myself.objectId = relid;
7262 myself.objectSubId = attnum;
7263 referenced.classId = TypeRelationId;
7264 referenced.objectId = typid;
7265 referenced.objectSubId = 0;
7266 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7270 * Install a column's dependency on its collation.
7272 static void
7273 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7275 ObjectAddress myself,
7276 referenced;
7278 /* We know the default collation is pinned, so don't bother recording it */
7279 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7281 myself.classId = RelationRelationId;
7282 myself.objectId = relid;
7283 myself.objectSubId = attnum;
7284 referenced.classId = CollationRelationId;
7285 referenced.objectId = collid;
7286 referenced.objectSubId = 0;
7287 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7292 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7295 static void
7296 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
7299 * If the parent is a partitioned table, like check constraints, we do not
7300 * support removing the NOT NULL while partitions exist.
7302 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7304 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
7306 Assert(partdesc != NULL);
7307 if (partdesc->nparts > 0 && !recurse && !recursing)
7308 ereport(ERROR,
7309 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7310 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7311 errhint("Do not specify the ONLY keyword.")));
7316 * Return the address of the modified column. If the column was already
7317 * nullable, InvalidObjectAddress is returned.
7319 static ObjectAddress
7320 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
7322 HeapTuple tuple;
7323 Form_pg_attribute attTup;
7324 AttrNumber attnum;
7325 Relation attr_rel;
7326 List *indexoidlist;
7327 ListCell *indexoidscan;
7328 ObjectAddress address;
7331 * lookup the attribute
7333 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7335 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7336 if (!HeapTupleIsValid(tuple))
7337 ereport(ERROR,
7338 (errcode(ERRCODE_UNDEFINED_COLUMN),
7339 errmsg("column \"%s\" of relation \"%s\" does not exist",
7340 colName, RelationGetRelationName(rel))));
7341 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7342 attnum = attTup->attnum;
7344 /* Prevent them from altering a system attribute */
7345 if (attnum <= 0)
7346 ereport(ERROR,
7347 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7348 errmsg("cannot alter system column \"%s\"",
7349 colName)));
7351 if (attTup->attidentity)
7352 ereport(ERROR,
7353 (errcode(ERRCODE_SYNTAX_ERROR),
7354 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7355 colName, RelationGetRelationName(rel))));
7358 * Check that the attribute is not in a primary key or in an index used as
7359 * a replica identity.
7361 * Note: we'll throw error even if the pkey index is not valid.
7364 /* Loop over all indexes on the relation */
7365 indexoidlist = RelationGetIndexList(rel);
7367 foreach(indexoidscan, indexoidlist)
7369 Oid indexoid = lfirst_oid(indexoidscan);
7370 HeapTuple indexTuple;
7371 Form_pg_index indexStruct;
7372 int i;
7374 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7375 if (!HeapTupleIsValid(indexTuple))
7376 elog(ERROR, "cache lookup failed for index %u", indexoid);
7377 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7380 * If the index is not a primary key or an index used as replica
7381 * identity, skip the check.
7383 if (indexStruct->indisprimary || indexStruct->indisreplident)
7386 * Loop over each attribute in the primary key or the index used
7387 * as replica identity and see if it matches the to-be-altered
7388 * attribute.
7390 for (i = 0; i < indexStruct->indnkeyatts; i++)
7392 if (indexStruct->indkey.values[i] == attnum)
7394 if (indexStruct->indisprimary)
7395 ereport(ERROR,
7396 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7397 errmsg("column \"%s\" is in a primary key",
7398 colName)));
7399 else
7400 ereport(ERROR,
7401 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7402 errmsg("column \"%s\" is in index used as replica identity",
7403 colName)));
7408 ReleaseSysCache(indexTuple);
7411 list_free(indexoidlist);
7413 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7414 if (rel->rd_rel->relispartition)
7416 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7417 Relation parent = table_open(parentId, AccessShareLock);
7418 TupleDesc tupDesc = RelationGetDescr(parent);
7419 AttrNumber parent_attnum;
7421 parent_attnum = get_attnum(parentId, colName);
7422 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7423 ereport(ERROR,
7424 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7425 errmsg("column \"%s\" is marked NOT NULL in parent table",
7426 colName)));
7427 table_close(parent, AccessShareLock);
7431 * Okay, actually perform the catalog change ... if needed
7433 if (attTup->attnotnull)
7435 attTup->attnotnull = false;
7437 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7439 ObjectAddressSubSet(address, RelationRelationId,
7440 RelationGetRelid(rel), attnum);
7442 else
7443 address = InvalidObjectAddress;
7445 InvokeObjectPostAlterHook(RelationRelationId,
7446 RelationGetRelid(rel), attnum);
7448 table_close(attr_rel, RowExclusiveLock);
7450 return address;
7454 * ALTER TABLE ALTER COLUMN SET NOT NULL
7457 static void
7458 ATPrepSetNotNull(List **wqueue, Relation rel,
7459 AlterTableCmd *cmd, bool recurse, bool recursing,
7460 LOCKMODE lockmode, AlterTableUtilityContext *context)
7463 * If we're already recursing, there's nothing to do; the topmost
7464 * invocation of ATSimpleRecursion already visited all children.
7466 if (recursing)
7467 return;
7470 * If the target column is already marked NOT NULL, we can skip recursing
7471 * to children, because their columns should already be marked NOT NULL as
7472 * well. But there's no point in checking here unless the relation has
7473 * some children; else we can just wait till execution to check. (If it
7474 * does have children, however, this can save taking per-child locks
7475 * unnecessarily. This greatly improves concurrency in some parallel
7476 * restore scenarios.)
7478 * Unfortunately, we can only apply this optimization to partitioned
7479 * tables, because traditional inheritance doesn't enforce that child
7480 * columns be NOT NULL when their parent is. (That's a bug that should
7481 * get fixed someday.)
7483 if (rel->rd_rel->relhassubclass &&
7484 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7486 HeapTuple tuple;
7487 bool attnotnull;
7489 tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
7491 /* Might as well throw the error now, if name is bad */
7492 if (!HeapTupleIsValid(tuple))
7493 ereport(ERROR,
7494 (errcode(ERRCODE_UNDEFINED_COLUMN),
7495 errmsg("column \"%s\" of relation \"%s\" does not exist",
7496 cmd->name, RelationGetRelationName(rel))));
7498 attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
7499 ReleaseSysCache(tuple);
7500 if (attnotnull)
7501 return;
7505 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7506 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7507 * normal recursion logic.
7509 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
7510 !recurse)
7512 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
7514 newcmd->subtype = AT_CheckNotNull;
7515 newcmd->name = pstrdup(cmd->name);
7516 ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
7518 else
7519 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
7523 * Return the address of the modified column. If the column was already NOT
7524 * NULL, InvalidObjectAddress is returned.
7526 static ObjectAddress
7527 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
7528 const char *colName, LOCKMODE lockmode)
7530 HeapTuple tuple;
7531 AttrNumber attnum;
7532 Relation attr_rel;
7533 ObjectAddress address;
7536 * lookup the attribute
7538 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7540 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7542 if (!HeapTupleIsValid(tuple))
7543 ereport(ERROR,
7544 (errcode(ERRCODE_UNDEFINED_COLUMN),
7545 errmsg("column \"%s\" of relation \"%s\" does not exist",
7546 colName, RelationGetRelationName(rel))));
7548 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
7550 /* Prevent them from altering a system attribute */
7551 if (attnum <= 0)
7552 ereport(ERROR,
7553 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7554 errmsg("cannot alter system column \"%s\"",
7555 colName)));
7558 * Okay, actually perform the catalog change ... if needed
7560 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7562 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
7564 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7567 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7568 * are set NOT NULL; however, if we can find a constraint which proves
7569 * this then we can skip that. We needn't bother looking if we've
7570 * already found that we must verify some other NOT NULL constraint.
7572 if (!tab->verify_new_notnull &&
7573 !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
7575 /* Tell Phase 3 it needs to test the constraint */
7576 tab->verify_new_notnull = true;
7579 ObjectAddressSubSet(address, RelationRelationId,
7580 RelationGetRelid(rel), attnum);
7582 else
7583 address = InvalidObjectAddress;
7585 InvokeObjectPostAlterHook(RelationRelationId,
7586 RelationGetRelid(rel), attnum);
7588 table_close(attr_rel, RowExclusiveLock);
7590 return address;
7594 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7596 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7597 * commands against the partitions of a partitioned table if the user
7598 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7599 * or tries to create a primary key on it (which internally creates
7600 * AT_SetNotNull on the partitioned table). Such a command doesn't
7601 * allow us to actually modify any partition, but we want to let it
7602 * go through if the partitions are already properly marked.
7604 * In future, this might need to adjust the child table's state, likely
7605 * by incrementing an inheritance count for the attnotnull constraint.
7606 * For now we need only check for the presence of the flag.
7608 static void
7609 ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
7610 const char *colName, LOCKMODE lockmode)
7612 HeapTuple tuple;
7614 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7616 if (!HeapTupleIsValid(tuple))
7617 ereport(ERROR,
7618 (errcode(ERRCODE_UNDEFINED_COLUMN),
7619 errmsg("column \"%s\" of relation \"%s\" does not exist",
7620 colName, RelationGetRelationName(rel))));
7622 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7623 ereport(ERROR,
7624 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7625 errmsg("constraint must be added to child tables too"),
7626 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7627 colName, RelationGetRelationName(rel)),
7628 errhint("Do not specify the ONLY keyword.")));
7630 ReleaseSysCache(tuple);
7634 * NotNullImpliedByRelConstraints
7635 * Does rel's existing constraints imply NOT NULL for the given attribute?
7637 static bool
7638 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7640 NullTest *nnulltest = makeNode(NullTest);
7642 nnulltest->arg = (Expr *) makeVar(1,
7643 attr->attnum,
7644 attr->atttypid,
7645 attr->atttypmod,
7646 attr->attcollation,
7648 nnulltest->nulltesttype = IS_NOT_NULL;
7651 * argisrow = false is correct even for a composite column, because
7652 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7653 * case, just IS DISTINCT FROM NULL.
7655 nnulltest->argisrow = false;
7656 nnulltest->location = -1;
7658 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7660 ereport(DEBUG1,
7661 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7662 RelationGetRelationName(rel), NameStr(attr->attname))));
7663 return true;
7666 return false;
7670 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7672 * Return the address of the affected column.
7674 static ObjectAddress
7675 ATExecColumnDefault(Relation rel, const char *colName,
7676 Node *newDefault, LOCKMODE lockmode)
7678 TupleDesc tupdesc = RelationGetDescr(rel);
7679 AttrNumber attnum;
7680 ObjectAddress address;
7683 * get the number of the attribute
7685 attnum = get_attnum(RelationGetRelid(rel), colName);
7686 if (attnum == InvalidAttrNumber)
7687 ereport(ERROR,
7688 (errcode(ERRCODE_UNDEFINED_COLUMN),
7689 errmsg("column \"%s\" of relation \"%s\" does not exist",
7690 colName, RelationGetRelationName(rel))));
7692 /* Prevent them from altering a system attribute */
7693 if (attnum <= 0)
7694 ereport(ERROR,
7695 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7696 errmsg("cannot alter system column \"%s\"",
7697 colName)));
7699 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7700 ereport(ERROR,
7701 (errcode(ERRCODE_SYNTAX_ERROR),
7702 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7703 colName, RelationGetRelationName(rel)),
7704 newDefault ? 0 : errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY instead.")));
7706 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7707 ereport(ERROR,
7708 (errcode(ERRCODE_SYNTAX_ERROR),
7709 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7710 colName, RelationGetRelationName(rel)),
7711 newDefault || TupleDescAttr(tupdesc, attnum - 1)->attgenerated != ATTRIBUTE_GENERATED_STORED ? 0 :
7712 errhint("Use ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION instead.")));
7715 * Remove any old default for the column. We use RESTRICT here for
7716 * safety, but at present we do not expect anything to depend on the
7717 * default.
7719 * We treat removing the existing default as an internal operation when it
7720 * is preparatory to adding a new default, but as a user-initiated
7721 * operation when the user asked for a drop.
7723 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7724 newDefault != NULL);
7726 if (newDefault)
7728 /* SET DEFAULT */
7729 RawColumnDefault *rawEnt;
7731 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7732 rawEnt->attnum = attnum;
7733 rawEnt->raw_default = newDefault;
7734 rawEnt->missingMode = false;
7735 rawEnt->generated = '\0';
7738 * This function is intended for CREATE TABLE, so it processes a
7739 * _list_ of defaults, but we just do one.
7741 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7742 false, true, false, NULL);
7745 ObjectAddressSubSet(address, RelationRelationId,
7746 RelationGetRelid(rel), attnum);
7747 return address;
7751 * Add a pre-cooked default expression.
7753 * Return the address of the affected column.
7755 static ObjectAddress
7756 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
7757 Node *newDefault)
7759 ObjectAddress address;
7761 /* We assume no checking is required */
7764 * Remove any old default for the column. We use RESTRICT here for
7765 * safety, but at present we do not expect anything to depend on the
7766 * default. (In ordinary cases, there could not be a default in place
7767 * anyway, but it's possible when combining LIKE with inheritance.)
7769 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7770 true);
7772 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7774 ObjectAddressSubSet(address, RelationRelationId,
7775 RelationGetRelid(rel), attnum);
7776 return address;
7780 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7782 * Return the address of the affected column.
7784 static ObjectAddress
7785 ATExecAddIdentity(Relation rel, const char *colName,
7786 Node *def, LOCKMODE lockmode)
7788 Relation attrelation;
7789 HeapTuple tuple;
7790 Form_pg_attribute attTup;
7791 AttrNumber attnum;
7792 ObjectAddress address;
7793 ColumnDef *cdef = castNode(ColumnDef, def);
7795 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7797 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7798 if (!HeapTupleIsValid(tuple))
7799 ereport(ERROR,
7800 (errcode(ERRCODE_UNDEFINED_COLUMN),
7801 errmsg("column \"%s\" of relation \"%s\" does not exist",
7802 colName, RelationGetRelationName(rel))));
7803 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7804 attnum = attTup->attnum;
7806 /* Can't alter a system attribute */
7807 if (attnum <= 0)
7808 ereport(ERROR,
7809 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7810 errmsg("cannot alter system column \"%s\"",
7811 colName)));
7814 * Creating a column as identity implies NOT NULL, so adding the identity
7815 * to an existing column that is not NOT NULL would create a state that
7816 * cannot be reproduced without contortions.
7818 if (!attTup->attnotnull)
7819 ereport(ERROR,
7820 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7821 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
7822 colName, RelationGetRelationName(rel))));
7824 if (attTup->attidentity)
7825 ereport(ERROR,
7826 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7827 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
7828 colName, RelationGetRelationName(rel))));
7830 if (attTup->atthasdef)
7831 ereport(ERROR,
7832 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7833 errmsg("column \"%s\" of relation \"%s\" already has a default value",
7834 colName, RelationGetRelationName(rel))));
7836 attTup->attidentity = cdef->identity;
7837 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7839 InvokeObjectPostAlterHook(RelationRelationId,
7840 RelationGetRelid(rel),
7841 attTup->attnum);
7842 ObjectAddressSubSet(address, RelationRelationId,
7843 RelationGetRelid(rel), attnum);
7844 heap_freetuple(tuple);
7846 table_close(attrelation, RowExclusiveLock);
7848 return address;
7852 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
7854 * Return the address of the affected column.
7856 static ObjectAddress
7857 ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode)
7859 ListCell *option;
7860 DefElem *generatedEl = NULL;
7861 HeapTuple tuple;
7862 Form_pg_attribute attTup;
7863 AttrNumber attnum;
7864 Relation attrelation;
7865 ObjectAddress address;
7867 foreach(option, castNode(List, def))
7869 DefElem *defel = lfirst_node(DefElem, option);
7871 if (strcmp(defel->defname, "generated") == 0)
7873 if (generatedEl)
7874 ereport(ERROR,
7875 (errcode(ERRCODE_SYNTAX_ERROR),
7876 errmsg("conflicting or redundant options")));
7877 generatedEl = defel;
7879 else
7880 elog(ERROR, "option \"%s\" not recognized",
7881 defel->defname);
7885 * Even if there is nothing to change here, we run all the checks. There
7886 * will be a subsequent ALTER SEQUENCE that relies on everything being
7887 * there.
7890 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7891 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7892 if (!HeapTupleIsValid(tuple))
7893 ereport(ERROR,
7894 (errcode(ERRCODE_UNDEFINED_COLUMN),
7895 errmsg("column \"%s\" of relation \"%s\" does not exist",
7896 colName, RelationGetRelationName(rel))));
7898 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7899 attnum = attTup->attnum;
7901 if (attnum <= 0)
7902 ereport(ERROR,
7903 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7904 errmsg("cannot alter system column \"%s\"",
7905 colName)));
7907 if (!attTup->attidentity)
7908 ereport(ERROR,
7909 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7910 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7911 colName, RelationGetRelationName(rel))));
7913 if (generatedEl)
7915 attTup->attidentity = defGetInt32(generatedEl);
7916 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7918 InvokeObjectPostAlterHook(RelationRelationId,
7919 RelationGetRelid(rel),
7920 attTup->attnum);
7921 ObjectAddressSubSet(address, RelationRelationId,
7922 RelationGetRelid(rel), attnum);
7924 else
7925 address = InvalidObjectAddress;
7927 heap_freetuple(tuple);
7928 table_close(attrelation, RowExclusiveLock);
7930 return address;
7934 * ALTER TABLE ALTER COLUMN DROP IDENTITY
7936 * Return the address of the affected column.
7938 static ObjectAddress
7939 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
7941 HeapTuple tuple;
7942 Form_pg_attribute attTup;
7943 AttrNumber attnum;
7944 Relation attrelation;
7945 ObjectAddress address;
7946 Oid seqid;
7947 ObjectAddress seqaddress;
7949 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
7950 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7951 if (!HeapTupleIsValid(tuple))
7952 ereport(ERROR,
7953 (errcode(ERRCODE_UNDEFINED_COLUMN),
7954 errmsg("column \"%s\" of relation \"%s\" does not exist",
7955 colName, RelationGetRelationName(rel))));
7957 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7958 attnum = attTup->attnum;
7960 if (attnum <= 0)
7961 ereport(ERROR,
7962 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7963 errmsg("cannot alter system column \"%s\"",
7964 colName)));
7966 if (!attTup->attidentity)
7968 if (!missing_ok)
7969 ereport(ERROR,
7970 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
7971 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
7972 colName, RelationGetRelationName(rel))));
7973 else
7975 ereport(NOTICE,
7976 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
7977 colName, RelationGetRelationName(rel))));
7978 heap_freetuple(tuple);
7979 table_close(attrelation, RowExclusiveLock);
7980 return InvalidObjectAddress;
7984 attTup->attidentity = '\0';
7985 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
7987 InvokeObjectPostAlterHook(RelationRelationId,
7988 RelationGetRelid(rel),
7989 attTup->attnum);
7990 ObjectAddressSubSet(address, RelationRelationId,
7991 RelationGetRelid(rel), attnum);
7992 heap_freetuple(tuple);
7994 table_close(attrelation, RowExclusiveLock);
7996 /* drop the internal sequence */
7997 seqid = getIdentitySequence(RelationGetRelid(rel), attnum, false);
7998 deleteDependencyRecordsForClass(RelationRelationId, seqid,
7999 RelationRelationId, DEPENDENCY_INTERNAL);
8000 CommandCounterIncrement();
8001 seqaddress.classId = RelationRelationId;
8002 seqaddress.objectId = seqid;
8003 seqaddress.objectSubId = 0;
8004 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8006 return address;
8010 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8012 static void
8013 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8016 * Reject ONLY if there are child tables. We could implement this, but it
8017 * is a bit complicated. GENERATED clauses must be attached to the column
8018 * definition and cannot be added later like DEFAULT, so if a child table
8019 * has a generation expression that the parent does not have, the child
8020 * column will necessarily be an attislocal column. So to implement ONLY
8021 * here, we'd need extra code to update attislocal of the direct child
8022 * tables, somewhat similar to how DROP COLUMN does it, so that the
8023 * resulting state can be properly dumped and restored.
8025 if (!recurse &&
8026 find_inheritance_children(RelationGetRelid(rel), lockmode))
8027 ereport(ERROR,
8028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8029 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8032 * Cannot drop generation expression from inherited columns.
8034 if (!recursing)
8036 HeapTuple tuple;
8037 Form_pg_attribute attTup;
8039 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8040 if (!HeapTupleIsValid(tuple))
8041 ereport(ERROR,
8042 (errcode(ERRCODE_UNDEFINED_COLUMN),
8043 errmsg("column \"%s\" of relation \"%s\" does not exist",
8044 cmd->name, RelationGetRelationName(rel))));
8046 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8048 if (attTup->attinhcount > 0)
8049 ereport(ERROR,
8050 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8051 errmsg("cannot drop generation expression from inherited column")));
8056 * Return the address of the affected column.
8058 static ObjectAddress
8059 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8061 HeapTuple tuple;
8062 Form_pg_attribute attTup;
8063 AttrNumber attnum;
8064 Relation attrelation;
8065 Oid attrdefoid;
8066 ObjectAddress address;
8068 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8069 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8070 if (!HeapTupleIsValid(tuple))
8071 ereport(ERROR,
8072 (errcode(ERRCODE_UNDEFINED_COLUMN),
8073 errmsg("column \"%s\" of relation \"%s\" does not exist",
8074 colName, RelationGetRelationName(rel))));
8076 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8077 attnum = attTup->attnum;
8079 if (attnum <= 0)
8080 ereport(ERROR,
8081 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8082 errmsg("cannot alter system column \"%s\"",
8083 colName)));
8085 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8087 if (!missing_ok)
8088 ereport(ERROR,
8089 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8090 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8091 colName, RelationGetRelationName(rel))));
8092 else
8094 ereport(NOTICE,
8095 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8096 colName, RelationGetRelationName(rel))));
8097 heap_freetuple(tuple);
8098 table_close(attrelation, RowExclusiveLock);
8099 return InvalidObjectAddress;
8104 * Mark the column as no longer generated. (The atthasdef flag needs to
8105 * get cleared too, but RemoveAttrDefault will handle that.)
8107 attTup->attgenerated = '\0';
8108 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8110 InvokeObjectPostAlterHook(RelationRelationId,
8111 RelationGetRelid(rel),
8112 attnum);
8113 heap_freetuple(tuple);
8115 table_close(attrelation, RowExclusiveLock);
8118 * Drop the dependency records of the GENERATED expression, in particular
8119 * its INTERNAL dependency on the column, which would otherwise cause
8120 * dependency.c to refuse to perform the deletion.
8122 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8123 if (!OidIsValid(attrdefoid))
8124 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8125 RelationGetRelid(rel), attnum);
8126 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8128 /* Make above changes visible */
8129 CommandCounterIncrement();
8132 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8133 * safety, but at present we do not expect anything to depend on the
8134 * default.
8136 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8137 false, false);
8139 ObjectAddressSubSet(address, RelationRelationId,
8140 RelationGetRelid(rel), attnum);
8141 return address;
8145 * ALTER TABLE ALTER COLUMN SET STATISTICS
8147 * Return value is the address of the modified column
8149 static ObjectAddress
8150 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8152 int newtarget;
8153 Relation attrelation;
8154 HeapTuple tuple;
8155 Form_pg_attribute attrtuple;
8156 AttrNumber attnum;
8157 ObjectAddress address;
8160 * We allow referencing columns by numbers only for indexes, since table
8161 * column numbers could contain gaps if columns are later dropped.
8163 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8164 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8165 !colName)
8166 ereport(ERROR,
8167 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8168 errmsg("cannot refer to non-index column by number")));
8170 Assert(IsA(newValue, Integer));
8171 newtarget = intVal(newValue);
8174 * Limit target to a sane range
8176 if (newtarget < -1)
8178 ereport(ERROR,
8179 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8180 errmsg("statistics target %d is too low",
8181 newtarget)));
8183 else if (newtarget > MAX_STATISTICS_TARGET)
8185 newtarget = MAX_STATISTICS_TARGET;
8186 ereport(WARNING,
8187 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8188 errmsg("lowering statistics target to %d",
8189 newtarget)));
8192 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8194 if (colName)
8196 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8198 if (!HeapTupleIsValid(tuple))
8199 ereport(ERROR,
8200 (errcode(ERRCODE_UNDEFINED_COLUMN),
8201 errmsg("column \"%s\" of relation \"%s\" does not exist",
8202 colName, RelationGetRelationName(rel))));
8204 else
8206 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(rel), colNum);
8208 if (!HeapTupleIsValid(tuple))
8209 ereport(ERROR,
8210 (errcode(ERRCODE_UNDEFINED_COLUMN),
8211 errmsg("column number %d of relation \"%s\" does not exist",
8212 colNum, RelationGetRelationName(rel))));
8215 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8217 attnum = attrtuple->attnum;
8218 if (attnum <= 0)
8219 ereport(ERROR,
8220 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8221 errmsg("cannot alter system column \"%s\"",
8222 colName)));
8224 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8225 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8227 if (attnum > rel->rd_index->indnkeyatts)
8228 ereport(ERROR,
8229 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8230 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8231 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8232 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8233 ereport(ERROR,
8234 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8235 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8236 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8237 errhint("Alter statistics on table column instead.")));
8240 attrtuple->attstattarget = newtarget;
8242 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8244 InvokeObjectPostAlterHook(RelationRelationId,
8245 RelationGetRelid(rel),
8246 attrtuple->attnum);
8247 ObjectAddressSubSet(address, RelationRelationId,
8248 RelationGetRelid(rel), attnum);
8249 heap_freetuple(tuple);
8251 table_close(attrelation, RowExclusiveLock);
8253 return address;
8257 * Return value is the address of the modified column
8259 static ObjectAddress
8260 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8261 bool isReset, LOCKMODE lockmode)
8263 Relation attrelation;
8264 HeapTuple tuple,
8265 newtuple;
8266 Form_pg_attribute attrtuple;
8267 AttrNumber attnum;
8268 Datum datum,
8269 newOptions;
8270 bool isnull;
8271 ObjectAddress address;
8272 Datum repl_val[Natts_pg_attribute];
8273 bool repl_null[Natts_pg_attribute];
8274 bool repl_repl[Natts_pg_attribute];
8276 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8278 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8280 if (!HeapTupleIsValid(tuple))
8281 ereport(ERROR,
8282 (errcode(ERRCODE_UNDEFINED_COLUMN),
8283 errmsg("column \"%s\" of relation \"%s\" does not exist",
8284 colName, RelationGetRelationName(rel))));
8285 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8287 attnum = attrtuple->attnum;
8288 if (attnum <= 0)
8289 ereport(ERROR,
8290 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8291 errmsg("cannot alter system column \"%s\"",
8292 colName)));
8294 /* Generate new proposed attoptions (text array) */
8295 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8296 &isnull);
8297 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8298 castNode(List, options), NULL, NULL,
8299 false, isReset);
8300 /* Validate new options */
8301 (void) attribute_reloptions(newOptions, true);
8303 /* Build new tuple. */
8304 memset(repl_null, false, sizeof(repl_null));
8305 memset(repl_repl, false, sizeof(repl_repl));
8306 if (newOptions != (Datum) 0)
8307 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8308 else
8309 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8310 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8311 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8312 repl_val, repl_null, repl_repl);
8314 /* Update system catalog. */
8315 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8317 InvokeObjectPostAlterHook(RelationRelationId,
8318 RelationGetRelid(rel),
8319 attrtuple->attnum);
8320 ObjectAddressSubSet(address, RelationRelationId,
8321 RelationGetRelid(rel), attnum);
8323 heap_freetuple(newtuple);
8325 ReleaseSysCache(tuple);
8327 table_close(attrelation, RowExclusiveLock);
8329 return address;
8333 * Helper function for ATExecSetStorage and ATExecSetCompression
8335 * Set the attstorage and/or attcompression fields for index columns
8336 * associated with the specified table column.
8338 static void
8339 SetIndexStorageProperties(Relation rel, Relation attrelation,
8340 AttrNumber attnum,
8341 bool setstorage, char newstorage,
8342 bool setcompression, char newcompression,
8343 LOCKMODE lockmode)
8345 ListCell *lc;
8347 foreach(lc, RelationGetIndexList(rel))
8349 Oid indexoid = lfirst_oid(lc);
8350 Relation indrel;
8351 AttrNumber indattnum = 0;
8352 HeapTuple tuple;
8354 indrel = index_open(indexoid, lockmode);
8356 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8358 if (indrel->rd_index->indkey.values[i] == attnum)
8360 indattnum = i + 1;
8361 break;
8365 if (indattnum == 0)
8367 index_close(indrel, lockmode);
8368 continue;
8371 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8373 if (HeapTupleIsValid(tuple))
8375 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8377 if (setstorage)
8378 attrtuple->attstorage = newstorage;
8380 if (setcompression)
8381 attrtuple->attcompression = newcompression;
8383 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8385 InvokeObjectPostAlterHook(RelationRelationId,
8386 RelationGetRelid(rel),
8387 attrtuple->attnum);
8389 heap_freetuple(tuple);
8392 index_close(indrel, lockmode);
8397 * ALTER TABLE ALTER COLUMN SET STORAGE
8399 * Return value is the address of the modified column
8401 static ObjectAddress
8402 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8404 Relation attrelation;
8405 HeapTuple tuple;
8406 Form_pg_attribute attrtuple;
8407 AttrNumber attnum;
8408 ObjectAddress address;
8410 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8412 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8414 if (!HeapTupleIsValid(tuple))
8415 ereport(ERROR,
8416 (errcode(ERRCODE_UNDEFINED_COLUMN),
8417 errmsg("column \"%s\" of relation \"%s\" does not exist",
8418 colName, RelationGetRelationName(rel))));
8419 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8421 attnum = attrtuple->attnum;
8422 if (attnum <= 0)
8423 ereport(ERROR,
8424 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8425 errmsg("cannot alter system column \"%s\"",
8426 colName)));
8428 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8430 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8432 InvokeObjectPostAlterHook(RelationRelationId,
8433 RelationGetRelid(rel),
8434 attrtuple->attnum);
8437 * Apply the change to indexes as well (only for simple index columns,
8438 * matching behavior of index.c ConstructTupleDescriptor()).
8440 SetIndexStorageProperties(rel, attrelation, attnum,
8441 true, attrtuple->attstorage,
8442 false, 0,
8443 lockmode);
8445 heap_freetuple(tuple);
8447 table_close(attrelation, RowExclusiveLock);
8449 ObjectAddressSubSet(address, RelationRelationId,
8450 RelationGetRelid(rel), attnum);
8451 return address;
8456 * ALTER TABLE DROP COLUMN
8458 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8459 * because we have to decide at runtime whether to recurse or not depending
8460 * on whether attinhcount goes to zero or not. (We can't check this in a
8461 * static pre-pass because it won't handle multiple inheritance situations
8462 * correctly.)
8464 static void
8465 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8466 AlterTableCmd *cmd, LOCKMODE lockmode,
8467 AlterTableUtilityContext *context)
8469 if (rel->rd_rel->reloftype && !recursing)
8470 ereport(ERROR,
8471 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8472 errmsg("cannot drop column from typed table")));
8474 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8475 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8477 if (recurse)
8478 cmd->recurse = true;
8482 * Drops column 'colName' from relation 'rel' and returns the address of the
8483 * dropped column. The column is also dropped (or marked as no longer
8484 * inherited from relation) from the relation's inheritance children, if any.
8486 * In the recursive invocations for inheritance child relations, instead of
8487 * dropping the column directly (if to be dropped at all), its object address
8488 * is added to 'addrs', which must be non-NULL in such invocations. All
8489 * columns are dropped at the same time after all the children have been
8490 * checked recursively.
8492 static ObjectAddress
8493 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8494 DropBehavior behavior,
8495 bool recurse, bool recursing,
8496 bool missing_ok, LOCKMODE lockmode,
8497 ObjectAddresses *addrs)
8499 HeapTuple tuple;
8500 Form_pg_attribute targetatt;
8501 AttrNumber attnum;
8502 List *children;
8503 ObjectAddress object;
8504 bool is_expr;
8506 /* At top level, permission check was done in ATPrepCmd, else do it */
8507 if (recursing)
8508 ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8510 /* Initialize addrs on the first invocation */
8511 Assert(!recursing || addrs != NULL);
8512 if (!recursing)
8513 addrs = new_object_addresses();
8516 * get the number of the attribute
8518 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8519 if (!HeapTupleIsValid(tuple))
8521 if (!missing_ok)
8523 ereport(ERROR,
8524 (errcode(ERRCODE_UNDEFINED_COLUMN),
8525 errmsg("column \"%s\" of relation \"%s\" does not exist",
8526 colName, RelationGetRelationName(rel))));
8528 else
8530 ereport(NOTICE,
8531 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8532 colName, RelationGetRelationName(rel))));
8533 return InvalidObjectAddress;
8536 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
8538 attnum = targetatt->attnum;
8540 /* Can't drop a system attribute */
8541 if (attnum <= 0)
8542 ereport(ERROR,
8543 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8544 errmsg("cannot drop system column \"%s\"",
8545 colName)));
8548 * Don't drop inherited columns, unless recursing (presumably from a drop
8549 * of the parent column)
8551 if (targetatt->attinhcount > 0 && !recursing)
8552 ereport(ERROR,
8553 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8554 errmsg("cannot drop inherited column \"%s\"",
8555 colName)));
8558 * Don't drop columns used in the partition key, either. (If we let this
8559 * go through, the key column's dependencies would cause a cascaded drop
8560 * of the whole table, which is surely not what the user expected.)
8562 if (has_partition_attrs(rel,
8563 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
8564 &is_expr))
8565 ereport(ERROR,
8566 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8567 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
8568 colName, RelationGetRelationName(rel))));
8570 ReleaseSysCache(tuple);
8573 * Propagate to children as appropriate. Unlike most other ALTER
8574 * routines, we have to do this one level of recursion at a time; we can't
8575 * use find_all_inheritors to do it in one pass.
8577 children =
8578 find_inheritance_children(RelationGetRelid(rel), lockmode);
8580 if (children)
8582 Relation attr_rel;
8583 ListCell *child;
8586 * In case of a partitioned table, the column must be dropped from the
8587 * partitions as well.
8589 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
8590 ereport(ERROR,
8591 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8592 errmsg("cannot drop column from only the partitioned table when partitions exist"),
8593 errhint("Do not specify the ONLY keyword.")));
8595 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
8596 foreach(child, children)
8598 Oid childrelid = lfirst_oid(child);
8599 Relation childrel;
8600 Form_pg_attribute childatt;
8602 /* find_inheritance_children already got lock */
8603 childrel = table_open(childrelid, NoLock);
8604 CheckTableNotInUse(childrel, "ALTER TABLE");
8606 tuple = SearchSysCacheCopyAttName(childrelid, colName);
8607 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
8608 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
8609 colName, childrelid);
8610 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
8612 if (childatt->attinhcount <= 0) /* shouldn't happen */
8613 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
8614 childrelid, colName);
8616 if (recurse)
8619 * If the child column has other definition sources, just
8620 * decrement its inheritance count; if not, recurse to delete
8621 * it.
8623 if (childatt->attinhcount == 1 && !childatt->attislocal)
8625 /* Time to delete this child column, too */
8626 ATExecDropColumn(wqueue, childrel, colName,
8627 behavior, true, true,
8628 false, lockmode, addrs);
8630 else
8632 /* Child column must survive my deletion */
8633 childatt->attinhcount--;
8635 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8637 /* Make update visible */
8638 CommandCounterIncrement();
8641 else
8644 * If we were told to drop ONLY in this table (no recursion),
8645 * we need to mark the inheritors' attributes as locally
8646 * defined rather than inherited.
8648 childatt->attinhcount--;
8649 childatt->attislocal = true;
8651 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8653 /* Make update visible */
8654 CommandCounterIncrement();
8657 heap_freetuple(tuple);
8659 table_close(childrel, NoLock);
8661 table_close(attr_rel, RowExclusiveLock);
8664 /* Add object to delete */
8665 object.classId = RelationRelationId;
8666 object.objectId = RelationGetRelid(rel);
8667 object.objectSubId = attnum;
8668 add_exact_object_address(&object, addrs);
8670 if (!recursing)
8672 /* Recursion has ended, drop everything that was collected */
8673 performMultipleDeletions(addrs, behavior, 0);
8674 free_object_addresses(addrs);
8677 return object;
8681 * ALTER TABLE ADD INDEX
8683 * There is no such command in the grammar, but parse_utilcmd.c converts
8684 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
8685 * us schedule creation of the index at the appropriate time during ALTER.
8687 * Return value is the address of the new index.
8689 static ObjectAddress
8690 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
8691 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
8693 bool check_rights;
8694 bool skip_build;
8695 bool quiet;
8696 ObjectAddress address;
8698 Assert(IsA(stmt, IndexStmt));
8699 Assert(!stmt->concurrent);
8701 /* The IndexStmt has already been through transformIndexStmt */
8702 Assert(stmt->transformed);
8704 /* suppress schema rights check when rebuilding existing index */
8705 check_rights = !is_rebuild;
8706 /* skip index build if phase 3 will do it or we're reusing an old one */
8707 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
8708 /* suppress notices when rebuilding existing index */
8709 quiet = is_rebuild;
8711 address = DefineIndex(RelationGetRelid(rel),
8712 stmt,
8713 InvalidOid, /* no predefined OID */
8714 InvalidOid, /* no parent index */
8715 InvalidOid, /* no parent constraint */
8716 -1, /* total_parts unknown */
8717 true, /* is_alter_table */
8718 check_rights,
8719 false, /* check_not_in_use - we did it already */
8720 skip_build,
8721 quiet);
8724 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
8725 * new index instead of building from scratch. Restore associated fields.
8726 * This may store InvalidSubTransactionId in both fields, in which case
8727 * relcache.c will assume it can rebuild the relcache entry. Hence, do
8728 * this after the CCI that made catalog rows visible to any rebuild. The
8729 * DROP of the old edition of this index will have scheduled the storage
8730 * for deletion at commit, so cancel that pending deletion.
8732 if (RelFileNumberIsValid(stmt->oldNumber))
8734 Relation irel = index_open(address.objectId, NoLock);
8736 irel->rd_createSubid = stmt->oldCreateSubid;
8737 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
8738 RelationPreserveStorage(irel->rd_locator, true);
8739 index_close(irel, NoLock);
8742 return address;
8746 * ALTER TABLE ADD STATISTICS
8748 * This is no such command in the grammar, but we use this internally to add
8749 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
8750 * column type change.
8752 static ObjectAddress
8753 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
8754 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
8756 ObjectAddress address;
8758 Assert(IsA(stmt, CreateStatsStmt));
8760 /* The CreateStatsStmt has already been through transformStatsStmt */
8761 Assert(stmt->transformed);
8763 address = CreateStatistics(stmt);
8765 return address;
8769 * ALTER TABLE ADD CONSTRAINT USING INDEX
8771 * Returns the address of the new constraint.
8773 static ObjectAddress
8774 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
8775 IndexStmt *stmt, LOCKMODE lockmode)
8777 Oid index_oid = stmt->indexOid;
8778 Relation indexRel;
8779 char *indexName;
8780 IndexInfo *indexInfo;
8781 char *constraintName;
8782 char constraintType;
8783 ObjectAddress address;
8784 bits16 flags;
8786 Assert(IsA(stmt, IndexStmt));
8787 Assert(OidIsValid(index_oid));
8788 Assert(stmt->isconstraint);
8791 * Doing this on partitioned tables is not a simple feature to implement,
8792 * so let's punt for now.
8794 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8795 ereport(ERROR,
8796 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8797 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
8799 indexRel = index_open(index_oid, AccessShareLock);
8801 indexName = pstrdup(RelationGetRelationName(indexRel));
8803 indexInfo = BuildIndexInfo(indexRel);
8805 /* this should have been checked at parse time */
8806 if (!indexInfo->ii_Unique)
8807 elog(ERROR, "index \"%s\" is not unique", indexName);
8810 * Determine name to assign to constraint. We require a constraint to
8811 * have the same name as the underlying index; therefore, use the index's
8812 * existing name as the default constraint name, and if the user
8813 * explicitly gives some other name for the constraint, rename the index
8814 * to match.
8816 constraintName = stmt->idxname;
8817 if (constraintName == NULL)
8818 constraintName = indexName;
8819 else if (strcmp(constraintName, indexName) != 0)
8821 ereport(NOTICE,
8822 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
8823 indexName, constraintName)));
8824 RenameRelationInternal(index_oid, constraintName, false, true);
8827 /* Extra checks needed if making primary key */
8828 if (stmt->primary)
8829 index_check_primary_key(rel, indexInfo, true, stmt);
8831 /* Note we currently don't support EXCLUSION constraints here */
8832 if (stmt->primary)
8833 constraintType = CONSTRAINT_PRIMARY;
8834 else
8835 constraintType = CONSTRAINT_UNIQUE;
8837 /* Create the catalog entries for the constraint */
8838 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
8839 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
8840 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
8841 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
8842 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
8844 address = index_constraint_create(rel,
8845 index_oid,
8846 InvalidOid,
8847 indexInfo,
8848 constraintName,
8849 constraintType,
8850 flags,
8851 allowSystemTableMods,
8852 false); /* is_internal */
8854 index_close(indexRel, NoLock);
8856 return address;
8860 * ALTER TABLE ADD CONSTRAINT
8862 * Return value is the address of the new constraint; if no constraint was
8863 * added, InvalidObjectAddress is returned.
8865 static ObjectAddress
8866 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8867 Constraint *newConstraint, bool recurse, bool is_readd,
8868 LOCKMODE lockmode)
8870 ObjectAddress address = InvalidObjectAddress;
8872 Assert(IsA(newConstraint, Constraint));
8875 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
8876 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
8877 * switch anyway to make it easier to add more code later.
8879 switch (newConstraint->contype)
8881 case CONSTR_CHECK:
8882 address =
8883 ATAddCheckConstraint(wqueue, tab, rel,
8884 newConstraint, recurse, false, is_readd,
8885 lockmode);
8886 break;
8888 case CONSTR_FOREIGN:
8891 * Assign or validate constraint name
8893 if (newConstraint->conname)
8895 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
8896 RelationGetRelid(rel),
8897 newConstraint->conname))
8898 ereport(ERROR,
8899 (errcode(ERRCODE_DUPLICATE_OBJECT),
8900 errmsg("constraint \"%s\" for relation \"%s\" already exists",
8901 newConstraint->conname,
8902 RelationGetRelationName(rel))));
8904 else
8905 newConstraint->conname =
8906 ChooseConstraintName(RelationGetRelationName(rel),
8907 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
8908 "fkey",
8909 RelationGetNamespace(rel),
8910 NIL);
8912 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
8913 newConstraint,
8914 recurse, false,
8915 lockmode);
8916 break;
8918 default:
8919 elog(ERROR, "unrecognized constraint type: %d",
8920 (int) newConstraint->contype);
8923 return address;
8927 * Generate the column-name portion of the constraint name for a new foreign
8928 * key given the list of column names that reference the referenced
8929 * table. This will be passed to ChooseConstraintName along with the parent
8930 * table name and the "fkey" suffix.
8932 * We know that less than NAMEDATALEN characters will actually be used, so we
8933 * can truncate the result once we've generated that many.
8935 * XXX see also ChooseExtendedStatisticNameAddition and
8936 * ChooseIndexNameAddition.
8938 static char *
8939 ChooseForeignKeyConstraintNameAddition(List *colnames)
8941 char buf[NAMEDATALEN * 2];
8942 int buflen = 0;
8943 ListCell *lc;
8945 buf[0] = '\0';
8946 foreach(lc, colnames)
8948 const char *name = strVal(lfirst(lc));
8950 if (buflen > 0)
8951 buf[buflen++] = '_'; /* insert _ between names */
8954 * At this point we have buflen <= NAMEDATALEN. name should be less
8955 * than NAMEDATALEN already, but use strlcpy for paranoia.
8957 strlcpy(buf + buflen, name, NAMEDATALEN);
8958 buflen += strlen(buf + buflen);
8959 if (buflen >= NAMEDATALEN)
8960 break;
8962 return pstrdup(buf);
8966 * Add a check constraint to a single table and its children. Returns the
8967 * address of the constraint added to the parent relation, if one gets added,
8968 * or InvalidObjectAddress otherwise.
8970 * Subroutine for ATExecAddConstraint.
8972 * We must recurse to child tables during execution, rather than using
8973 * ALTER TABLE's normal prep-time recursion. The reason is that all the
8974 * constraints *must* be given the same name, else they won't be seen as
8975 * related later. If the user didn't explicitly specify a name, then
8976 * AddRelationNewConstraints would normally assign different names to the
8977 * child constraints. To fix that, we must capture the name assigned at
8978 * the parent table and pass that down.
8980 static ObjectAddress
8981 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
8982 Constraint *constr, bool recurse, bool recursing,
8983 bool is_readd, LOCKMODE lockmode)
8985 List *newcons;
8986 ListCell *lcon;
8987 List *children;
8988 ListCell *child;
8989 ObjectAddress address = InvalidObjectAddress;
8991 /* At top level, permission check was done in ATPrepCmd, else do it */
8992 if (recursing)
8993 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8996 * Call AddRelationNewConstraints to do the work, making sure it works on
8997 * a copy of the Constraint so transformExpr can't modify the original. It
8998 * returns a list of cooked constraints.
9000 * If the constraint ends up getting merged with a pre-existing one, it's
9001 * omitted from the returned list, which is what we want: we do not need
9002 * to do any validation work. That can only happen at child tables,
9003 * though, since we disallow merging at the top level.
9005 newcons = AddRelationNewConstraints(rel, NIL,
9006 list_make1(copyObject(constr)),
9007 recursing || is_readd, /* allow_merge */
9008 !recursing, /* is_local */
9009 is_readd, /* is_internal */
9010 NULL); /* queryString not available
9011 * here */
9013 /* we don't expect more than one constraint here */
9014 Assert(list_length(newcons) <= 1);
9016 /* Add each to-be-validated constraint to Phase 3's queue */
9017 foreach(lcon, newcons)
9019 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9021 if (!ccon->skip_validation)
9023 NewConstraint *newcon;
9025 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9026 newcon->name = ccon->name;
9027 newcon->contype = ccon->contype;
9028 newcon->qual = ccon->expr;
9030 tab->constraints = lappend(tab->constraints, newcon);
9033 /* Save the actually assigned name if it was defaulted */
9034 if (constr->conname == NULL)
9035 constr->conname = ccon->name;
9037 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9040 /* At this point we must have a locked-down name to use */
9041 Assert(constr->conname != NULL);
9043 /* Advance command counter in case same table is visited multiple times */
9044 CommandCounterIncrement();
9047 * If the constraint got merged with an existing constraint, we're done.
9048 * We mustn't recurse to child tables in this case, because they've
9049 * already got the constraint, and visiting them again would lead to an
9050 * incorrect value for coninhcount.
9052 if (newcons == NIL)
9053 return address;
9056 * If adding a NO INHERIT constraint, no need to find our children.
9058 if (constr->is_no_inherit)
9059 return address;
9062 * Propagate to children as appropriate. Unlike most other ALTER
9063 * routines, we have to do this one level of recursion at a time; we can't
9064 * use find_all_inheritors to do it in one pass.
9066 children =
9067 find_inheritance_children(RelationGetRelid(rel), lockmode);
9070 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9071 * constraint creation only if there are no children currently. Error out
9072 * otherwise.
9074 if (!recurse && children != NIL)
9075 ereport(ERROR,
9076 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9077 errmsg("constraint must be added to child tables too")));
9079 foreach(child, children)
9081 Oid childrelid = lfirst_oid(child);
9082 Relation childrel;
9083 AlteredTableInfo *childtab;
9085 /* find_inheritance_children already got lock */
9086 childrel = table_open(childrelid, NoLock);
9087 CheckTableNotInUse(childrel, "ALTER TABLE");
9089 /* Find or create work queue entry for this table */
9090 childtab = ATGetQueueEntry(wqueue, childrel);
9092 /* Recurse to child */
9093 ATAddCheckConstraint(wqueue, childtab, childrel,
9094 constr, recurse, true, is_readd, lockmode);
9096 table_close(childrel, NoLock);
9099 return address;
9103 * Add a foreign-key constraint to a single table; return the new constraint's
9104 * address.
9106 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9107 * lock on the rel, and have done appropriate validity checks for it.
9108 * We do permissions checks here, however.
9110 * When the referenced or referencing tables (or both) are partitioned,
9111 * multiple pg_constraint rows are required -- one for each partitioned table
9112 * and each partition on each side (fortunately, not one for every combination
9113 * thereof). We also need action triggers on each leaf partition on the
9114 * referenced side, and check triggers on each leaf partition on the
9115 * referencing side.
9117 static ObjectAddress
9118 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9119 Constraint *fkconstraint,
9120 bool recurse, bool recursing, LOCKMODE lockmode)
9122 Relation pkrel;
9123 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9124 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9125 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9126 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9127 Oid opclasses[INDEX_MAX_KEYS] = {0};
9128 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9129 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9130 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9131 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9132 int i;
9133 int numfks,
9134 numpks,
9135 numfkdelsetcols;
9136 Oid indexOid;
9137 bool old_check_ok;
9138 ObjectAddress address;
9139 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9142 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9143 * delete rows out from under us.
9145 if (OidIsValid(fkconstraint->old_pktable_oid))
9146 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9147 else
9148 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9151 * Validity checks (permission checks wait till we have the column
9152 * numbers)
9154 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9156 if (!recurse)
9157 ereport(ERROR,
9158 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9159 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9160 RelationGetRelationName(rel),
9161 RelationGetRelationName(pkrel))));
9162 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9163 ereport(ERROR,
9164 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9165 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9166 RelationGetRelationName(rel),
9167 RelationGetRelationName(pkrel)),
9168 errdetail("This feature is not yet supported on partitioned tables.")));
9171 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9172 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9173 ereport(ERROR,
9174 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9175 errmsg("referenced relation \"%s\" is not a table",
9176 RelationGetRelationName(pkrel))));
9178 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9179 ereport(ERROR,
9180 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9181 errmsg("permission denied: \"%s\" is a system catalog",
9182 RelationGetRelationName(pkrel))));
9185 * References from permanent or unlogged tables to temp tables, and from
9186 * permanent tables to unlogged tables, are disallowed because the
9187 * referenced data can vanish out from under us. References from temp
9188 * tables to any other table type are also disallowed, because other
9189 * backends might need to run the RI triggers on the perm table, but they
9190 * can't reliably see tuples in the local buffers of other backends.
9192 switch (rel->rd_rel->relpersistence)
9194 case RELPERSISTENCE_PERMANENT:
9195 if (!RelationIsPermanent(pkrel))
9196 ereport(ERROR,
9197 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9198 errmsg("constraints on permanent tables may reference only permanent tables")));
9199 break;
9200 case RELPERSISTENCE_UNLOGGED:
9201 if (!RelationIsPermanent(pkrel)
9202 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9203 ereport(ERROR,
9204 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9205 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9206 break;
9207 case RELPERSISTENCE_TEMP:
9208 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9209 ereport(ERROR,
9210 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9211 errmsg("constraints on temporary tables may reference only temporary tables")));
9212 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9213 ereport(ERROR,
9214 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9215 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9216 break;
9220 * Look up the referencing attributes to make sure they exist, and record
9221 * their attnums and type OIDs.
9223 numfks = transformColumnNameList(RelationGetRelid(rel),
9224 fkconstraint->fk_attrs,
9225 fkattnum, fktypoid);
9227 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9228 fkconstraint->fk_del_set_cols,
9229 fkdelsetcols, NULL);
9230 validateFkOnDeleteSetColumns(numfks, fkattnum,
9231 numfkdelsetcols, fkdelsetcols,
9232 fkconstraint->fk_del_set_cols);
9235 * If the attribute list for the referenced table was omitted, lookup the
9236 * definition of the primary key and use it. Otherwise, validate the
9237 * supplied attribute list. In either case, discover the index OID and
9238 * index opclasses, and the attnums and type OIDs of the attributes.
9240 if (fkconstraint->pk_attrs == NIL)
9242 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9243 &fkconstraint->pk_attrs,
9244 pkattnum, pktypoid,
9245 opclasses);
9247 else
9249 numpks = transformColumnNameList(RelationGetRelid(pkrel),
9250 fkconstraint->pk_attrs,
9251 pkattnum, pktypoid);
9252 /* Look for an index matching the column list */
9253 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9254 opclasses);
9258 * Now we can check permissions.
9260 checkFkeyPermissions(pkrel, pkattnum, numpks);
9263 * Check some things for generated columns.
9265 for (i = 0; i < numfks; i++)
9267 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9269 if (attgenerated)
9272 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9274 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9275 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9276 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9277 ereport(ERROR,
9278 (errcode(ERRCODE_SYNTAX_ERROR),
9279 errmsg("invalid %s action for foreign key constraint containing generated column",
9280 "ON UPDATE")));
9281 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9282 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9283 ereport(ERROR,
9284 (errcode(ERRCODE_SYNTAX_ERROR),
9285 errmsg("invalid %s action for foreign key constraint containing generated column",
9286 "ON DELETE")));
9291 * Look up the equality operators to use in the constraint.
9293 * Note that we have to be careful about the difference between the actual
9294 * PK column type and the opclass' declared input type, which might be
9295 * only binary-compatible with it. The declared opcintype is the right
9296 * thing to probe pg_amop with.
9298 if (numfks != numpks)
9299 ereport(ERROR,
9300 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9301 errmsg("number of referencing and referenced columns for foreign key disagree")));
9304 * On the strength of a previous constraint, we might avoid scanning
9305 * tables to validate this one. See below.
9307 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9308 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9310 for (i = 0; i < numpks; i++)
9312 Oid pktype = pktypoid[i];
9313 Oid fktype = fktypoid[i];
9314 Oid fktyped;
9315 HeapTuple cla_ht;
9316 Form_pg_opclass cla_tup;
9317 Oid amid;
9318 Oid opfamily;
9319 Oid opcintype;
9320 Oid pfeqop;
9321 Oid ppeqop;
9322 Oid ffeqop;
9323 int16 eqstrategy;
9324 Oid pfeqop_right;
9326 /* We need several fields out of the pg_opclass entry */
9327 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9328 if (!HeapTupleIsValid(cla_ht))
9329 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9330 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9331 amid = cla_tup->opcmethod;
9332 opfamily = cla_tup->opcfamily;
9333 opcintype = cla_tup->opcintype;
9334 ReleaseSysCache(cla_ht);
9337 * Check it's a btree; currently this can never fail since no other
9338 * index AMs support unique indexes. If we ever did have other types
9339 * of unique indexes, we'd need a way to determine which operator
9340 * strategy number is equality. (Is it reasonable to insist that
9341 * every such index AM use btree's number for equality?)
9343 if (amid != BTREE_AM_OID)
9344 elog(ERROR, "only b-tree indexes are supported for foreign keys");
9345 eqstrategy = BTEqualStrategyNumber;
9348 * There had better be a primary equality operator for the index.
9349 * We'll use it for PK = PK comparisons.
9351 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9352 eqstrategy);
9354 if (!OidIsValid(ppeqop))
9355 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9356 eqstrategy, opcintype, opcintype, opfamily);
9359 * Are there equality operators that take exactly the FK type? Assume
9360 * we should look through any domain here.
9362 fktyped = getBaseType(fktype);
9364 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9365 eqstrategy);
9366 if (OidIsValid(pfeqop))
9368 pfeqop_right = fktyped;
9369 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9370 eqstrategy);
9372 else
9374 /* keep compiler quiet */
9375 pfeqop_right = InvalidOid;
9376 ffeqop = InvalidOid;
9379 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9382 * Otherwise, look for an implicit cast from the FK type to the
9383 * opcintype, and if found, use the primary equality operator.
9384 * This is a bit tricky because opcintype might be a polymorphic
9385 * type such as ANYARRAY or ANYENUM; so what we have to test is
9386 * whether the two actual column types can be concurrently cast to
9387 * that type. (Otherwise, we'd fail to reject combinations such
9388 * as int[] and point[].)
9390 Oid input_typeids[2];
9391 Oid target_typeids[2];
9393 input_typeids[0] = pktype;
9394 input_typeids[1] = fktype;
9395 target_typeids[0] = opcintype;
9396 target_typeids[1] = opcintype;
9397 if (can_coerce_type(2, input_typeids, target_typeids,
9398 COERCION_IMPLICIT))
9400 pfeqop = ffeqop = ppeqop;
9401 pfeqop_right = opcintype;
9405 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9406 ereport(ERROR,
9407 (errcode(ERRCODE_DATATYPE_MISMATCH),
9408 errmsg("foreign key constraint \"%s\" cannot be implemented",
9409 fkconstraint->conname),
9410 errdetail("Key columns \"%s\" and \"%s\" "
9411 "are of incompatible types: %s and %s.",
9412 strVal(list_nth(fkconstraint->fk_attrs, i)),
9413 strVal(list_nth(fkconstraint->pk_attrs, i)),
9414 format_type_be(fktype),
9415 format_type_be(pktype))));
9417 if (old_check_ok)
9420 * When a pfeqop changes, revalidate the constraint. We could
9421 * permit intra-opfamily changes, but that adds subtle complexity
9422 * without any concrete benefit for core types. We need not
9423 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9425 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9426 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9427 old_pfeqop_item);
9429 if (old_check_ok)
9431 Oid old_fktype;
9432 Oid new_fktype;
9433 CoercionPathType old_pathtype;
9434 CoercionPathType new_pathtype;
9435 Oid old_castfunc;
9436 Oid new_castfunc;
9437 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9438 fkattnum[i] - 1);
9441 * Identify coercion pathways from each of the old and new FK-side
9442 * column types to the right (foreign) operand type of the pfeqop.
9443 * We may assume that pg_constraint.conkey is not changing.
9445 old_fktype = attr->atttypid;
9446 new_fktype = fktype;
9447 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9448 &old_castfunc);
9449 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9450 &new_castfunc);
9453 * Upon a change to the cast from the FK column to its pfeqop
9454 * operand, revalidate the constraint. For this evaluation, a
9455 * binary coercion cast is equivalent to no cast at all. While
9456 * type implementors should design implicit casts with an eye
9457 * toward consistency of operations like equality, we cannot
9458 * assume here that they have done so.
9460 * A function with a polymorphic argument could change behavior
9461 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9462 * when the cast destination is polymorphic, we only avoid
9463 * revalidation if the input type has not changed at all. Given
9464 * just the core data types and operator classes, this requirement
9465 * prevents no would-be optimizations.
9467 * If the cast converts from a base type to a domain thereon, then
9468 * that domain type must be the opcintype of the unique index.
9469 * Necessarily, the primary key column must then be of the domain
9470 * type. Since the constraint was previously valid, all values on
9471 * the foreign side necessarily exist on the primary side and in
9472 * turn conform to the domain. Consequently, we need not treat
9473 * domains specially here.
9475 * Since we require that all collations share the same notion of
9476 * equality (which they do, because texteq reduces to bitwise
9477 * equality), we don't compare collation here.
9479 * We need not directly consider the PK type. It's necessarily
9480 * binary coercible to the opcintype of the unique index column,
9481 * and ri_triggers.c will only deal with PK datums in terms of
9482 * that opcintype. Changing the opcintype also changes pfeqop.
9484 old_check_ok = (new_pathtype == old_pathtype &&
9485 new_castfunc == old_castfunc &&
9486 (!IsPolymorphicType(pfeqop_right) ||
9487 new_fktype == old_fktype));
9490 pfeqoperators[i] = pfeqop;
9491 ppeqoperators[i] = ppeqop;
9492 ffeqoperators[i] = ffeqop;
9496 * Create all the constraint and trigger objects, recursing to partitions
9497 * as necessary. First handle the referenced side.
9499 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9500 indexOid,
9501 InvalidOid, /* no parent constraint */
9502 numfks,
9503 pkattnum,
9504 fkattnum,
9505 pfeqoperators,
9506 ppeqoperators,
9507 ffeqoperators,
9508 numfkdelsetcols,
9509 fkdelsetcols,
9510 old_check_ok,
9511 InvalidOid, InvalidOid);
9513 /* Now handle the referencing side. */
9514 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9515 indexOid,
9516 address.objectId,
9517 numfks,
9518 pkattnum,
9519 fkattnum,
9520 pfeqoperators,
9521 ppeqoperators,
9522 ffeqoperators,
9523 numfkdelsetcols,
9524 fkdelsetcols,
9525 old_check_ok,
9526 lockmode,
9527 InvalidOid, InvalidOid);
9530 * Done. Close pk table, but keep lock until we've committed.
9532 table_close(pkrel, NoLock);
9534 return address;
9538 * validateFkOnDeleteSetColumns
9539 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9540 * column lists are valid.
9542 void
9543 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
9544 int numfksetcols, const int16 *fksetcolsattnums,
9545 List *fksetcols)
9547 for (int i = 0; i < numfksetcols; i++)
9549 int16 setcol_attnum = fksetcolsattnums[i];
9550 bool seen = false;
9552 for (int j = 0; j < numfks; j++)
9554 if (fkattnums[j] == setcol_attnum)
9556 seen = true;
9557 break;
9561 if (!seen)
9563 char *col = strVal(list_nth(fksetcols, i));
9565 ereport(ERROR,
9566 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
9567 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
9573 * addFkRecurseReferenced
9574 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
9575 * side of the constraint
9577 * Create pg_constraint rows for the referenced side of the constraint,
9578 * referencing the parent of the referencing side; also create action triggers
9579 * on leaf partitions. If the table is partitioned, recurse to handle each
9580 * partition.
9582 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9583 * of an ALTER TABLE sequence.
9584 * fkconstraint is the constraint being added.
9585 * rel is the root referencing relation.
9586 * pkrel is the referenced relation; might be a partition, if recursing.
9587 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9588 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
9589 * top-level constraint.
9590 * numfks is the number of columns in the foreign key
9591 * pkattnum is the attnum array of referenced attributes.
9592 * fkattnum is the attnum array of referencing attributes.
9593 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9594 * (...) clause
9595 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9596 * NULL/DEFAULT clause
9597 * pf/pp/ffeqoperators are OID array of operators between columns.
9598 * old_check_ok signals that this constraint replaces an existing one that
9599 * was already validated (thus this one doesn't need validation).
9600 * parentDelTrigger and parentUpdTrigger, when being recursively called on
9601 * a partition, are the OIDs of the parent action triggers for DELETE and
9602 * UPDATE respectively.
9604 static ObjectAddress
9605 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
9606 Relation pkrel, Oid indexOid, Oid parentConstr,
9607 int numfks,
9608 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
9609 Oid *ppeqoperators, Oid *ffeqoperators,
9610 int numfkdelsetcols, int16 *fkdelsetcols,
9611 bool old_check_ok,
9612 Oid parentDelTrigger, Oid parentUpdTrigger)
9614 ObjectAddress address;
9615 Oid constrOid;
9616 char *conname;
9617 bool conislocal;
9618 int coninhcount;
9619 bool connoinherit;
9620 Oid deleteTriggerOid,
9621 updateTriggerOid;
9624 * Verify relkind for each referenced partition. At the top level, this
9625 * is redundant with a previous check, but we need it when recursing.
9627 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9628 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9629 ereport(ERROR,
9630 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9631 errmsg("referenced relation \"%s\" is not a table",
9632 RelationGetRelationName(pkrel))));
9635 * Caller supplies us with a constraint name; however, it may be used in
9636 * this partition, so come up with a different one in that case.
9638 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9639 RelationGetRelid(rel),
9640 fkconstraint->conname))
9641 conname = ChooseConstraintName(RelationGetRelationName(rel),
9642 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9643 "fkey",
9644 RelationGetNamespace(rel), NIL);
9645 else
9646 conname = fkconstraint->conname;
9648 if (OidIsValid(parentConstr))
9650 conislocal = false;
9651 coninhcount = 1;
9652 connoinherit = false;
9654 else
9656 conislocal = true;
9657 coninhcount = 0;
9660 * always inherit for partitioned tables, never for legacy inheritance
9662 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
9666 * Record the FK constraint in pg_constraint.
9668 constrOid = CreateConstraintEntry(conname,
9669 RelationGetNamespace(rel),
9670 CONSTRAINT_FOREIGN,
9671 fkconstraint->deferrable,
9672 fkconstraint->initdeferred,
9673 fkconstraint->initially_valid,
9674 parentConstr,
9675 RelationGetRelid(rel),
9676 fkattnum,
9677 numfks,
9678 numfks,
9679 InvalidOid, /* not a domain constraint */
9680 indexOid,
9681 RelationGetRelid(pkrel),
9682 pkattnum,
9683 pfeqoperators,
9684 ppeqoperators,
9685 ffeqoperators,
9686 numfks,
9687 fkconstraint->fk_upd_action,
9688 fkconstraint->fk_del_action,
9689 fkdelsetcols,
9690 numfkdelsetcols,
9691 fkconstraint->fk_matchtype,
9692 NULL, /* no exclusion constraint */
9693 NULL, /* no check constraint */
9694 NULL,
9695 conislocal, /* islocal */
9696 coninhcount, /* inhcount */
9697 connoinherit, /* conNoInherit */
9698 false); /* is_internal */
9700 ObjectAddressSet(address, ConstraintRelationId, constrOid);
9703 * Mark the child constraint as part of the parent constraint; it must not
9704 * be dropped on its own. (This constraint is deleted when the partition
9705 * is detached, but a special check needs to occur that the partition
9706 * contains no referenced values.)
9708 if (OidIsValid(parentConstr))
9710 ObjectAddress referenced;
9712 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
9713 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
9716 /* make new constraint visible, in case we add more */
9717 CommandCounterIncrement();
9720 * Create the action triggers that enforce the constraint.
9722 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
9723 fkconstraint,
9724 constrOid, indexOid,
9725 parentDelTrigger, parentUpdTrigger,
9726 &deleteTriggerOid, &updateTriggerOid);
9729 * If the referenced table is partitioned, recurse on ourselves to handle
9730 * each partition. We need one pg_constraint row created for each
9731 * partition in addition to the pg_constraint row for the parent table.
9733 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9735 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
9737 for (int i = 0; i < pd->nparts; i++)
9739 Relation partRel;
9740 AttrMap *map;
9741 AttrNumber *mapped_pkattnum;
9742 Oid partIndexId;
9744 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
9747 * Map the attribute numbers in the referenced side of the FK
9748 * definition to match the partition's column layout.
9750 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
9751 RelationGetDescr(pkrel),
9752 false);
9753 if (map)
9755 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
9756 for (int j = 0; j < numfks; j++)
9757 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
9759 else
9760 mapped_pkattnum = pkattnum;
9762 /* do the deed */
9763 partIndexId = index_get_partition(partRel, indexOid);
9764 if (!OidIsValid(partIndexId))
9765 elog(ERROR, "index for %u not found in partition %s",
9766 indexOid, RelationGetRelationName(partRel));
9767 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
9768 partIndexId, constrOid, numfks,
9769 mapped_pkattnum, fkattnum,
9770 pfeqoperators, ppeqoperators, ffeqoperators,
9771 numfkdelsetcols, fkdelsetcols,
9772 old_check_ok,
9773 deleteTriggerOid, updateTriggerOid);
9775 /* Done -- clean up (but keep the lock) */
9776 table_close(partRel, NoLock);
9777 if (map)
9779 pfree(mapped_pkattnum);
9780 free_attrmap(map);
9785 return address;
9789 * addFkRecurseReferencing
9790 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
9792 * If the referencing relation is a plain relation, create the necessary check
9793 * triggers that implement the constraint, and set up for Phase 3 constraint
9794 * verification. If the referencing relation is a partitioned table, then
9795 * we create a pg_constraint row for it and recurse on this routine for each
9796 * partition.
9798 * We assume that the referenced relation is locked against concurrent
9799 * deletions. If it's a partitioned relation, every partition must be so
9800 * locked.
9802 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
9803 * of an ALTER TABLE sequence.
9804 * fkconstraint is the constraint being added.
9805 * rel is the referencing relation; might be a partition, if recursing.
9806 * pkrel is the root referenced relation.
9807 * indexOid is the OID of the index (on pkrel) implementing this constraint.
9808 * parentConstr is the OID of the parent constraint (there is always one).
9809 * numfks is the number of columns in the foreign key
9810 * pkattnum is the attnum array of referenced attributes.
9811 * fkattnum is the attnum array of referencing attributes.
9812 * pf/pp/ffeqoperators are OID array of operators between columns.
9813 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
9814 * (...) clause
9815 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
9816 * NULL/DEFAULT clause
9817 * old_check_ok signals that this constraint replaces an existing one that
9818 * was already validated (thus this one doesn't need validation).
9819 * lockmode is the lockmode to acquire on partitions when recursing.
9820 * parentInsTrigger and parentUpdTrigger, when being recursively called on
9821 * a partition, are the OIDs of the parent check triggers for INSERT and
9822 * UPDATE respectively.
9824 static void
9825 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
9826 Relation pkrel, Oid indexOid, Oid parentConstr,
9827 int numfks, int16 *pkattnum, int16 *fkattnum,
9828 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
9829 int numfkdelsetcols, int16 *fkdelsetcols,
9830 bool old_check_ok, LOCKMODE lockmode,
9831 Oid parentInsTrigger, Oid parentUpdTrigger)
9833 Oid insertTriggerOid,
9834 updateTriggerOid;
9836 Assert(OidIsValid(parentConstr));
9838 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
9839 ereport(ERROR,
9840 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9841 errmsg("foreign key constraints are not supported on foreign tables")));
9844 * Add the check triggers to it and, if necessary, schedule it to be
9845 * checked in Phase 3.
9847 * If the relation is partitioned, drill down to do it to its partitions.
9849 createForeignKeyCheckTriggers(RelationGetRelid(rel),
9850 RelationGetRelid(pkrel),
9851 fkconstraint,
9852 parentConstr,
9853 indexOid,
9854 parentInsTrigger, parentUpdTrigger,
9855 &insertTriggerOid, &updateTriggerOid);
9857 if (rel->rd_rel->relkind == RELKIND_RELATION)
9860 * Tell Phase 3 to check that the constraint is satisfied by existing
9861 * rows. We can skip this during table creation, when requested
9862 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
9863 * and when we're recreating a constraint following a SET DATA TYPE
9864 * operation that did not impugn its validity.
9866 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
9868 NewConstraint *newcon;
9869 AlteredTableInfo *tab;
9871 tab = ATGetQueueEntry(wqueue, rel);
9873 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9874 newcon->name = get_constraint_name(parentConstr);
9875 newcon->contype = CONSTR_FOREIGN;
9876 newcon->refrelid = RelationGetRelid(pkrel);
9877 newcon->refindid = indexOid;
9878 newcon->conid = parentConstr;
9879 newcon->qual = (Node *) fkconstraint;
9881 tab->constraints = lappend(tab->constraints, newcon);
9884 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9886 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
9887 Relation trigrel;
9890 * Triggers of the foreign keys will be manipulated a bunch of times
9891 * in the loop below. To avoid repeatedly opening/closing the trigger
9892 * catalog relation, we open it here and pass it to the subroutines
9893 * called below.
9895 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
9898 * Recurse to take appropriate action on each partition; either we
9899 * find an existing constraint to reparent to ours, or we create a new
9900 * one.
9902 for (int i = 0; i < pd->nparts; i++)
9904 Oid partitionId = pd->oids[i];
9905 Relation partition = table_open(partitionId, lockmode);
9906 List *partFKs;
9907 AttrMap *attmap;
9908 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
9909 bool attached;
9910 char *conname;
9911 Oid constrOid;
9912 ObjectAddress address,
9913 referenced;
9914 ListCell *cell;
9916 CheckTableNotInUse(partition, "ALTER TABLE");
9918 attmap = build_attrmap_by_name(RelationGetDescr(partition),
9919 RelationGetDescr(rel),
9920 false);
9921 for (int j = 0; j < numfks; j++)
9922 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
9924 /* Check whether an existing constraint can be repurposed */
9925 partFKs = copyObject(RelationGetFKeyList(partition));
9926 attached = false;
9927 foreach(cell, partFKs)
9929 ForeignKeyCacheInfo *fk;
9931 fk = lfirst_node(ForeignKeyCacheInfo, cell);
9932 if (tryAttachPartitionForeignKey(fk,
9933 partitionId,
9934 parentConstr,
9935 numfks,
9936 mapped_fkattnum,
9937 pkattnum,
9938 pfeqoperators,
9939 insertTriggerOid,
9940 updateTriggerOid,
9941 trigrel))
9943 attached = true;
9944 break;
9947 if (attached)
9949 table_close(partition, NoLock);
9950 continue;
9954 * No luck finding a good constraint to reuse; create our own.
9956 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9957 RelationGetRelid(partition),
9958 fkconstraint->conname))
9959 conname = ChooseConstraintName(RelationGetRelationName(partition),
9960 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
9961 "fkey",
9962 RelationGetNamespace(partition), NIL);
9963 else
9964 conname = fkconstraint->conname;
9965 constrOid =
9966 CreateConstraintEntry(conname,
9967 RelationGetNamespace(partition),
9968 CONSTRAINT_FOREIGN,
9969 fkconstraint->deferrable,
9970 fkconstraint->initdeferred,
9971 fkconstraint->initially_valid,
9972 parentConstr,
9973 partitionId,
9974 mapped_fkattnum,
9975 numfks,
9976 numfks,
9977 InvalidOid,
9978 indexOid,
9979 RelationGetRelid(pkrel),
9980 pkattnum,
9981 pfeqoperators,
9982 ppeqoperators,
9983 ffeqoperators,
9984 numfks,
9985 fkconstraint->fk_upd_action,
9986 fkconstraint->fk_del_action,
9987 fkdelsetcols,
9988 numfkdelsetcols,
9989 fkconstraint->fk_matchtype,
9990 NULL,
9991 NULL,
9992 NULL,
9993 false,
9995 false,
9996 false);
9999 * Give this constraint partition-type dependencies on the parent
10000 * constraint as well as the table.
10002 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10003 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10004 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10005 ObjectAddressSet(referenced, RelationRelationId, partitionId);
10006 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10008 /* Make all this visible before recursing */
10009 CommandCounterIncrement();
10011 /* call ourselves to finalize the creation and we're done */
10012 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10013 indexOid,
10014 constrOid,
10015 numfks,
10016 pkattnum,
10017 mapped_fkattnum,
10018 pfeqoperators,
10019 ppeqoperators,
10020 ffeqoperators,
10021 numfkdelsetcols,
10022 fkdelsetcols,
10023 old_check_ok,
10024 lockmode,
10025 insertTriggerOid,
10026 updateTriggerOid);
10028 table_close(partition, NoLock);
10031 table_close(trigrel, RowExclusiveLock);
10036 * CloneForeignKeyConstraints
10037 * Clone foreign keys from a partitioned table to a newly acquired
10038 * partition.
10040 * partitionRel is a partition of parentRel, so we can be certain that it has
10041 * the same columns with the same datatypes. The columns may be in different
10042 * order, though.
10044 * wqueue must be passed to set up phase 3 constraint checking, unless the
10045 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10046 * PARTITION OF).
10048 static void
10049 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10050 Relation partitionRel)
10052 /* This only works for declarative partitioning */
10053 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10056 * Clone constraints for which the parent is on the referenced side.
10058 CloneFkReferenced(parentRel, partitionRel);
10061 * Now clone constraints where the parent is on the referencing side.
10063 CloneFkReferencing(wqueue, parentRel, partitionRel);
10067 * CloneFkReferenced
10068 * Subroutine for CloneForeignKeyConstraints
10070 * Find all the FKs that have the parent relation on the referenced side;
10071 * clone those constraints to the given partition. This is to be called
10072 * when the partition is being created or attached.
10074 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10076 * This recurses to partitions, if the relation being attached is partitioned.
10077 * Recursion is done by calling addFkRecurseReferenced.
10079 static void
10080 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10082 Relation pg_constraint;
10083 AttrMap *attmap;
10084 ListCell *cell;
10085 SysScanDesc scan;
10086 ScanKeyData key[2];
10087 HeapTuple tuple;
10088 List *clone = NIL;
10089 Relation trigrel;
10092 * Search for any constraints where this partition's parent is in the
10093 * referenced side. However, we must not clone any constraint whose
10094 * parent constraint is also going to be cloned, to avoid duplicates. So
10095 * do it in two steps: first construct the list of constraints to clone,
10096 * then go over that list cloning those whose parents are not in the list.
10097 * (We must not rely on the parent being seen first, since the catalog
10098 * scan could return children first.)
10100 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10101 ScanKeyInit(&key[0],
10102 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10103 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10104 ScanKeyInit(&key[1],
10105 Anum_pg_constraint_contype, BTEqualStrategyNumber,
10106 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10107 /* This is a seqscan, as we don't have a usable index ... */
10108 scan = systable_beginscan(pg_constraint, InvalidOid, true,
10109 NULL, 2, key);
10110 while ((tuple = systable_getnext(scan)) != NULL)
10112 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10114 clone = lappend_oid(clone, constrForm->oid);
10116 systable_endscan(scan);
10117 table_close(pg_constraint, RowShareLock);
10120 * Triggers of the foreign keys will be manipulated a bunch of times in
10121 * the loop below. To avoid repeatedly opening/closing the trigger
10122 * catalog relation, we open it here and pass it to the subroutines called
10123 * below.
10125 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10127 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10128 RelationGetDescr(parentRel),
10129 false);
10130 foreach(cell, clone)
10132 Oid constrOid = lfirst_oid(cell);
10133 Form_pg_constraint constrForm;
10134 Relation fkRel;
10135 Oid indexOid;
10136 Oid partIndexId;
10137 int numfks;
10138 AttrNumber conkey[INDEX_MAX_KEYS];
10139 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10140 AttrNumber confkey[INDEX_MAX_KEYS];
10141 Oid conpfeqop[INDEX_MAX_KEYS];
10142 Oid conppeqop[INDEX_MAX_KEYS];
10143 Oid conffeqop[INDEX_MAX_KEYS];
10144 int numfkdelsetcols;
10145 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10146 Constraint *fkconstraint;
10147 Oid deleteTriggerOid,
10148 updateTriggerOid;
10150 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10151 if (!HeapTupleIsValid(tuple))
10152 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10153 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10156 * As explained above: don't try to clone a constraint for which we're
10157 * going to clone the parent.
10159 if (list_member_oid(clone, constrForm->conparentid))
10161 ReleaseSysCache(tuple);
10162 continue;
10166 * Don't clone self-referencing foreign keys, which can be in the
10167 * partitioned table or in the partition-to-be.
10169 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10170 constrForm->conrelid == RelationGetRelid(partitionRel))
10172 ReleaseSysCache(tuple);
10173 continue;
10177 * Because we're only expanding the key space at the referenced side,
10178 * we don't need to prevent any operation in the referencing table, so
10179 * AccessShareLock suffices (assumes that dropping the constraint
10180 * acquires AEL).
10182 fkRel = table_open(constrForm->conrelid, AccessShareLock);
10184 indexOid = constrForm->conindid;
10185 DeconstructFkConstraintRow(tuple,
10186 &numfks,
10187 conkey,
10188 confkey,
10189 conpfeqop,
10190 conppeqop,
10191 conffeqop,
10192 &numfkdelsetcols,
10193 confdelsetcols);
10195 for (int i = 0; i < numfks; i++)
10196 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10198 fkconstraint = makeNode(Constraint);
10199 fkconstraint->contype = CONSTRAINT_FOREIGN;
10200 fkconstraint->conname = NameStr(constrForm->conname);
10201 fkconstraint->deferrable = constrForm->condeferrable;
10202 fkconstraint->initdeferred = constrForm->condeferred;
10203 fkconstraint->location = -1;
10204 fkconstraint->pktable = NULL;
10205 /* ->fk_attrs determined below */
10206 fkconstraint->pk_attrs = NIL;
10207 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10208 fkconstraint->fk_upd_action = constrForm->confupdtype;
10209 fkconstraint->fk_del_action = constrForm->confdeltype;
10210 fkconstraint->fk_del_set_cols = NIL;
10211 fkconstraint->old_conpfeqop = NIL;
10212 fkconstraint->old_pktable_oid = InvalidOid;
10213 fkconstraint->skip_validation = false;
10214 fkconstraint->initially_valid = true;
10216 /* set up colnames that are used to generate the constraint name */
10217 for (int i = 0; i < numfks; i++)
10219 Form_pg_attribute att;
10221 att = TupleDescAttr(RelationGetDescr(fkRel),
10222 conkey[i] - 1);
10223 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10224 makeString(NameStr(att->attname)));
10228 * Add the new foreign key constraint pointing to the new partition.
10229 * Because this new partition appears in the referenced side of the
10230 * constraint, we don't need to set up for Phase 3 check.
10232 partIndexId = index_get_partition(partitionRel, indexOid);
10233 if (!OidIsValid(partIndexId))
10234 elog(ERROR, "index for %u not found in partition %s",
10235 indexOid, RelationGetRelationName(partitionRel));
10238 * Get the "action" triggers belonging to the constraint to pass as
10239 * parent OIDs for similar triggers that will be created on the
10240 * partition in addFkRecurseReferenced().
10242 GetForeignKeyActionTriggers(trigrel, constrOid,
10243 constrForm->confrelid, constrForm->conrelid,
10244 &deleteTriggerOid, &updateTriggerOid);
10246 addFkRecurseReferenced(NULL,
10247 fkconstraint,
10248 fkRel,
10249 partitionRel,
10250 partIndexId,
10251 constrOid,
10252 numfks,
10253 mapped_confkey,
10254 conkey,
10255 conpfeqop,
10256 conppeqop,
10257 conffeqop,
10258 numfkdelsetcols,
10259 confdelsetcols,
10260 true,
10261 deleteTriggerOid,
10262 updateTriggerOid);
10264 table_close(fkRel, NoLock);
10265 ReleaseSysCache(tuple);
10268 table_close(trigrel, RowExclusiveLock);
10272 * CloneFkReferencing
10273 * Subroutine for CloneForeignKeyConstraints
10275 * For each FK constraint of the parent relation in the given list, find an
10276 * equivalent constraint in its partition relation that can be reparented;
10277 * if one cannot be found, create a new constraint in the partition as its
10278 * child.
10280 * If wqueue is given, it is used to set up phase-3 verification for each
10281 * cloned constraint; if omitted, we assume that such verification is not
10282 * needed (example: the partition is being created anew).
10284 static void
10285 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
10287 AttrMap *attmap;
10288 List *partFKs;
10289 List *clone = NIL;
10290 ListCell *cell;
10291 Relation trigrel;
10293 /* obtain a list of constraints that we need to clone */
10294 foreach(cell, RelationGetFKeyList(parentRel))
10296 ForeignKeyCacheInfo *fk = lfirst(cell);
10298 clone = lappend_oid(clone, fk->conoid);
10302 * Silently do nothing if there's nothing to do. In particular, this
10303 * avoids throwing a spurious error for foreign tables.
10305 if (clone == NIL)
10306 return;
10308 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10309 ereport(ERROR,
10310 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10311 errmsg("foreign key constraints are not supported on foreign tables")));
10314 * Triggers of the foreign keys will be manipulated a bunch of times in
10315 * the loop below. To avoid repeatedly opening/closing the trigger
10316 * catalog relation, we open it here and pass it to the subroutines called
10317 * below.
10319 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10322 * The constraint key may differ, if the columns in the partition are
10323 * different. This map is used to convert them.
10325 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10326 RelationGetDescr(parentRel),
10327 false);
10329 partFKs = copyObject(RelationGetFKeyList(partRel));
10331 foreach(cell, clone)
10333 Oid parentConstrOid = lfirst_oid(cell);
10334 Form_pg_constraint constrForm;
10335 Relation pkrel;
10336 HeapTuple tuple;
10337 int numfks;
10338 AttrNumber conkey[INDEX_MAX_KEYS];
10339 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10340 AttrNumber confkey[INDEX_MAX_KEYS];
10341 Oid conpfeqop[INDEX_MAX_KEYS];
10342 Oid conppeqop[INDEX_MAX_KEYS];
10343 Oid conffeqop[INDEX_MAX_KEYS];
10344 int numfkdelsetcols;
10345 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10346 Constraint *fkconstraint;
10347 bool attached;
10348 Oid indexOid;
10349 Oid constrOid;
10350 ObjectAddress address,
10351 referenced;
10352 ListCell *lc;
10353 Oid insertTriggerOid,
10354 updateTriggerOid;
10356 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
10357 if (!HeapTupleIsValid(tuple))
10358 elog(ERROR, "cache lookup failed for constraint %u",
10359 parentConstrOid);
10360 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10362 /* Don't clone constraints whose parents are being cloned */
10363 if (list_member_oid(clone, constrForm->conparentid))
10365 ReleaseSysCache(tuple);
10366 continue;
10370 * Need to prevent concurrent deletions. If pkrel is a partitioned
10371 * relation, that means to lock all partitions.
10373 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10374 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10375 (void) find_all_inheritors(RelationGetRelid(pkrel),
10376 ShareRowExclusiveLock, NULL);
10378 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
10379 conpfeqop, conppeqop, conffeqop,
10380 &numfkdelsetcols, confdelsetcols);
10381 for (int i = 0; i < numfks; i++)
10382 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
10385 * Get the "check" triggers belonging to the constraint to pass as
10386 * parent OIDs for similar triggers that will be created on the
10387 * partition in addFkRecurseReferencing(). They are also passed to
10388 * tryAttachPartitionForeignKey() below to simply assign as parents to
10389 * the partition's existing "check" triggers, that is, if the
10390 * corresponding constraints is deemed attachable to the parent
10391 * constraint.
10393 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10394 constrForm->confrelid, constrForm->conrelid,
10395 &insertTriggerOid, &updateTriggerOid);
10398 * Before creating a new constraint, see whether any existing FKs are
10399 * fit for the purpose. If one is, attach the parent constraint to
10400 * it, and don't clone anything. This way we avoid the expensive
10401 * verification step and don't end up with a duplicate FK, and we
10402 * don't need to recurse to partitions for this constraint.
10404 attached = false;
10405 foreach(lc, partFKs)
10407 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
10409 if (tryAttachPartitionForeignKey(fk,
10410 RelationGetRelid(partRel),
10411 parentConstrOid,
10412 numfks,
10413 mapped_conkey,
10414 confkey,
10415 conpfeqop,
10416 insertTriggerOid,
10417 updateTriggerOid,
10418 trigrel))
10420 attached = true;
10421 table_close(pkrel, NoLock);
10422 break;
10425 if (attached)
10427 ReleaseSysCache(tuple);
10428 continue;
10431 /* No dice. Set up to create our own constraint */
10432 fkconstraint = makeNode(Constraint);
10433 fkconstraint->contype = CONSTRAINT_FOREIGN;
10434 /* ->conname determined below */
10435 fkconstraint->deferrable = constrForm->condeferrable;
10436 fkconstraint->initdeferred = constrForm->condeferred;
10437 fkconstraint->location = -1;
10438 fkconstraint->pktable = NULL;
10439 /* ->fk_attrs determined below */
10440 fkconstraint->pk_attrs = NIL;
10441 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10442 fkconstraint->fk_upd_action = constrForm->confupdtype;
10443 fkconstraint->fk_del_action = constrForm->confdeltype;
10444 fkconstraint->fk_del_set_cols = NIL;
10445 fkconstraint->old_conpfeqop = NIL;
10446 fkconstraint->old_pktable_oid = InvalidOid;
10447 fkconstraint->skip_validation = false;
10448 fkconstraint->initially_valid = true;
10449 for (int i = 0; i < numfks; i++)
10451 Form_pg_attribute att;
10453 att = TupleDescAttr(RelationGetDescr(partRel),
10454 mapped_conkey[i] - 1);
10455 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10456 makeString(NameStr(att->attname)));
10458 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10459 RelationGetRelid(partRel),
10460 NameStr(constrForm->conname)))
10461 fkconstraint->conname =
10462 ChooseConstraintName(RelationGetRelationName(partRel),
10463 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10464 "fkey",
10465 RelationGetNamespace(partRel), NIL);
10466 else
10467 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10469 indexOid = constrForm->conindid;
10470 constrOid =
10471 CreateConstraintEntry(fkconstraint->conname,
10472 constrForm->connamespace,
10473 CONSTRAINT_FOREIGN,
10474 fkconstraint->deferrable,
10475 fkconstraint->initdeferred,
10476 constrForm->convalidated,
10477 parentConstrOid,
10478 RelationGetRelid(partRel),
10479 mapped_conkey,
10480 numfks,
10481 numfks,
10482 InvalidOid, /* not a domain constraint */
10483 indexOid,
10484 constrForm->confrelid, /* same foreign rel */
10485 confkey,
10486 conpfeqop,
10487 conppeqop,
10488 conffeqop,
10489 numfks,
10490 fkconstraint->fk_upd_action,
10491 fkconstraint->fk_del_action,
10492 confdelsetcols,
10493 numfkdelsetcols,
10494 fkconstraint->fk_matchtype,
10495 NULL,
10496 NULL,
10497 NULL,
10498 false, /* islocal */
10499 1, /* inhcount */
10500 false, /* conNoInherit */
10501 true);
10503 /* Set up partition dependencies for the new constraint */
10504 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10505 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10506 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10507 ObjectAddressSet(referenced, RelationRelationId,
10508 RelationGetRelid(partRel));
10509 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10511 /* Done with the cloned constraint's tuple */
10512 ReleaseSysCache(tuple);
10514 /* Make all this visible before recursing */
10515 CommandCounterIncrement();
10517 addFkRecurseReferencing(wqueue,
10518 fkconstraint,
10519 partRel,
10520 pkrel,
10521 indexOid,
10522 constrOid,
10523 numfks,
10524 confkey,
10525 mapped_conkey,
10526 conpfeqop,
10527 conppeqop,
10528 conffeqop,
10529 numfkdelsetcols,
10530 confdelsetcols,
10531 false, /* no old check exists */
10532 AccessExclusiveLock,
10533 insertTriggerOid,
10534 updateTriggerOid);
10535 table_close(pkrel, NoLock);
10538 table_close(trigrel, RowExclusiveLock);
10542 * When the parent of a partition receives [the referencing side of] a foreign
10543 * key, we must propagate that foreign key to the partition. However, the
10544 * partition might already have an equivalent foreign key; this routine
10545 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
10546 * by the other parameters. If they are equivalent, create the link between
10547 * the two constraints and return true.
10549 * If the given FK does not match the one defined by rest of the params,
10550 * return false.
10552 static bool
10553 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
10554 Oid partRelid,
10555 Oid parentConstrOid,
10556 int numfks,
10557 AttrNumber *mapped_conkey,
10558 AttrNumber *confkey,
10559 Oid *conpfeqop,
10560 Oid parentInsTrigger,
10561 Oid parentUpdTrigger,
10562 Relation trigrel)
10564 HeapTuple parentConstrTup;
10565 Form_pg_constraint parentConstr;
10566 HeapTuple partcontup;
10567 Form_pg_constraint partConstr;
10568 ScanKeyData key;
10569 SysScanDesc scan;
10570 HeapTuple trigtup;
10571 Oid insertTriggerOid,
10572 updateTriggerOid;
10574 parentConstrTup = SearchSysCache1(CONSTROID,
10575 ObjectIdGetDatum(parentConstrOid));
10576 if (!HeapTupleIsValid(parentConstrTup))
10577 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
10578 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
10581 * Do some quick & easy initial checks. If any of these fail, we cannot
10582 * use this constraint.
10584 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
10586 ReleaseSysCache(parentConstrTup);
10587 return false;
10589 for (int i = 0; i < numfks; i++)
10591 if (fk->conkey[i] != mapped_conkey[i] ||
10592 fk->confkey[i] != confkey[i] ||
10593 fk->conpfeqop[i] != conpfeqop[i])
10595 ReleaseSysCache(parentConstrTup);
10596 return false;
10601 * Looks good so far; do some more extensive checks. Presumably the check
10602 * for 'convalidated' could be dropped, since we don't really care about
10603 * that, but let's be careful for now.
10605 partcontup = SearchSysCache1(CONSTROID,
10606 ObjectIdGetDatum(fk->conoid));
10607 if (!HeapTupleIsValid(partcontup))
10608 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
10609 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
10610 if (OidIsValid(partConstr->conparentid) ||
10611 !partConstr->convalidated ||
10612 partConstr->condeferrable != parentConstr->condeferrable ||
10613 partConstr->condeferred != parentConstr->condeferred ||
10614 partConstr->confupdtype != parentConstr->confupdtype ||
10615 partConstr->confdeltype != parentConstr->confdeltype ||
10616 partConstr->confmatchtype != parentConstr->confmatchtype)
10618 ReleaseSysCache(parentConstrTup);
10619 ReleaseSysCache(partcontup);
10620 return false;
10623 ReleaseSysCache(partcontup);
10624 ReleaseSysCache(parentConstrTup);
10627 * Looks good! Attach this constraint. The action triggers in the new
10628 * partition become redundant -- the parent table already has equivalent
10629 * ones, and those will be able to reach the partition. Remove the ones
10630 * in the partition. We identify them because they have our constraint
10631 * OID, as well as being on the referenced rel.
10633 ScanKeyInit(&key,
10634 Anum_pg_trigger_tgconstraint,
10635 BTEqualStrategyNumber, F_OIDEQ,
10636 ObjectIdGetDatum(fk->conoid));
10637 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
10638 NULL, 1, &key);
10639 while ((trigtup = systable_getnext(scan)) != NULL)
10641 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
10642 ObjectAddress trigger;
10644 if (trgform->tgconstrrelid != fk->conrelid)
10645 continue;
10646 if (trgform->tgrelid != fk->confrelid)
10647 continue;
10650 * The constraint is originally set up to contain this trigger as an
10651 * implementation object, so there's a dependency record that links
10652 * the two; however, since the trigger is no longer needed, we remove
10653 * the dependency link in order to be able to drop the trigger while
10654 * keeping the constraint intact.
10656 deleteDependencyRecordsFor(TriggerRelationId,
10657 trgform->oid,
10658 false);
10659 /* make dependency deletion visible to performDeletion */
10660 CommandCounterIncrement();
10661 ObjectAddressSet(trigger, TriggerRelationId,
10662 trgform->oid);
10663 performDeletion(&trigger, DROP_RESTRICT, 0);
10664 /* make trigger drop visible, in case the loop iterates */
10665 CommandCounterIncrement();
10668 systable_endscan(scan);
10670 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
10673 * Like the constraint, attach partition's "check" triggers to the
10674 * corresponding parent triggers.
10676 GetForeignKeyCheckTriggers(trigrel,
10677 fk->conoid, fk->confrelid, fk->conrelid,
10678 &insertTriggerOid, &updateTriggerOid);
10679 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
10680 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
10681 partRelid);
10682 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
10683 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
10684 partRelid);
10686 CommandCounterIncrement();
10687 return true;
10691 * GetForeignKeyActionTriggers
10692 * Returns delete and update "action" triggers of the given relation
10693 * belonging to the given constraint
10695 static void
10696 GetForeignKeyActionTriggers(Relation trigrel,
10697 Oid conoid, Oid confrelid, Oid conrelid,
10698 Oid *deleteTriggerOid,
10699 Oid *updateTriggerOid)
10701 ScanKeyData key;
10702 SysScanDesc scan;
10703 HeapTuple trigtup;
10705 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
10706 ScanKeyInit(&key,
10707 Anum_pg_trigger_tgconstraint,
10708 BTEqualStrategyNumber, F_OIDEQ,
10709 ObjectIdGetDatum(conoid));
10711 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
10712 NULL, 1, &key);
10713 while ((trigtup = systable_getnext(scan)) != NULL)
10715 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
10717 if (trgform->tgconstrrelid != conrelid)
10718 continue;
10719 if (trgform->tgrelid != confrelid)
10720 continue;
10721 /* Only ever look at "action" triggers on the PK side. */
10722 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
10723 continue;
10724 if (TRIGGER_FOR_DELETE(trgform->tgtype))
10726 Assert(*deleteTriggerOid == InvalidOid);
10727 *deleteTriggerOid = trgform->oid;
10729 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
10731 Assert(*updateTriggerOid == InvalidOid);
10732 *updateTriggerOid = trgform->oid;
10734 #ifndef USE_ASSERT_CHECKING
10735 /* In an assert-enabled build, continue looking to find duplicates */
10736 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
10737 break;
10738 #endif
10741 if (!OidIsValid(*deleteTriggerOid))
10742 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
10743 conoid);
10744 if (!OidIsValid(*updateTriggerOid))
10745 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
10746 conoid);
10748 systable_endscan(scan);
10752 * GetForeignKeyCheckTriggers
10753 * Returns insert and update "check" triggers of the given relation
10754 * belonging to the given constraint
10756 static void
10757 GetForeignKeyCheckTriggers(Relation trigrel,
10758 Oid conoid, Oid confrelid, Oid conrelid,
10759 Oid *insertTriggerOid,
10760 Oid *updateTriggerOid)
10762 ScanKeyData key;
10763 SysScanDesc scan;
10764 HeapTuple trigtup;
10766 *insertTriggerOid = *updateTriggerOid = InvalidOid;
10767 ScanKeyInit(&key,
10768 Anum_pg_trigger_tgconstraint,
10769 BTEqualStrategyNumber, F_OIDEQ,
10770 ObjectIdGetDatum(conoid));
10772 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
10773 NULL, 1, &key);
10774 while ((trigtup = systable_getnext(scan)) != NULL)
10776 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
10778 if (trgform->tgconstrrelid != confrelid)
10779 continue;
10780 if (trgform->tgrelid != conrelid)
10781 continue;
10782 /* Only ever look at "check" triggers on the FK side. */
10783 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
10784 continue;
10785 if (TRIGGER_FOR_INSERT(trgform->tgtype))
10787 Assert(*insertTriggerOid == InvalidOid);
10788 *insertTriggerOid = trgform->oid;
10790 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
10792 Assert(*updateTriggerOid == InvalidOid);
10793 *updateTriggerOid = trgform->oid;
10795 #ifndef USE_ASSERT_CHECKING
10796 /* In an assert-enabled build, continue looking to find duplicates. */
10797 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
10798 break;
10799 #endif
10802 if (!OidIsValid(*insertTriggerOid))
10803 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
10804 conoid);
10805 if (!OidIsValid(*updateTriggerOid))
10806 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
10807 conoid);
10809 systable_endscan(scan);
10813 * ALTER TABLE ALTER CONSTRAINT
10815 * Update the attributes of a constraint.
10817 * Currently only works for Foreign Key constraints.
10819 * If the constraint is modified, returns its address; otherwise, return
10820 * InvalidObjectAddress.
10822 static ObjectAddress
10823 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
10824 bool recursing, LOCKMODE lockmode)
10826 Constraint *cmdcon;
10827 Relation conrel;
10828 Relation tgrel;
10829 SysScanDesc scan;
10830 ScanKeyData skey[3];
10831 HeapTuple contuple;
10832 Form_pg_constraint currcon;
10833 ObjectAddress address;
10834 List *otherrelids = NIL;
10835 ListCell *lc;
10837 cmdcon = castNode(Constraint, cmd->def);
10839 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
10840 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
10843 * Find and check the target constraint
10845 ScanKeyInit(&skey[0],
10846 Anum_pg_constraint_conrelid,
10847 BTEqualStrategyNumber, F_OIDEQ,
10848 ObjectIdGetDatum(RelationGetRelid(rel)));
10849 ScanKeyInit(&skey[1],
10850 Anum_pg_constraint_contypid,
10851 BTEqualStrategyNumber, F_OIDEQ,
10852 ObjectIdGetDatum(InvalidOid));
10853 ScanKeyInit(&skey[2],
10854 Anum_pg_constraint_conname,
10855 BTEqualStrategyNumber, F_NAMEEQ,
10856 CStringGetDatum(cmdcon->conname));
10857 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
10858 true, NULL, 3, skey);
10860 /* There can be at most one matching row */
10861 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
10862 ereport(ERROR,
10863 (errcode(ERRCODE_UNDEFINED_OBJECT),
10864 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
10865 cmdcon->conname, RelationGetRelationName(rel))));
10867 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
10868 if (currcon->contype != CONSTRAINT_FOREIGN)
10869 ereport(ERROR,
10870 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10871 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
10872 cmdcon->conname, RelationGetRelationName(rel))));
10875 * If it's not the topmost constraint, raise an error.
10877 * Altering a non-topmost constraint leaves some triggers untouched, since
10878 * they are not directly connected to this constraint; also, pg_dump would
10879 * ignore the deferrability status of the individual constraint, since it
10880 * only dumps topmost constraints. Avoid these problems by refusing this
10881 * operation and telling the user to alter the parent constraint instead.
10883 if (OidIsValid(currcon->conparentid))
10885 HeapTuple tp;
10886 Oid parent = currcon->conparentid;
10887 char *ancestorname = NULL;
10888 char *ancestortable = NULL;
10890 /* Loop to find the topmost constraint */
10891 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
10893 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
10895 /* If no parent, this is the constraint we want */
10896 if (!OidIsValid(contup->conparentid))
10898 ancestorname = pstrdup(NameStr(contup->conname));
10899 ancestortable = get_rel_name(contup->conrelid);
10900 ReleaseSysCache(tp);
10901 break;
10904 parent = contup->conparentid;
10905 ReleaseSysCache(tp);
10908 ereport(ERROR,
10909 (errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
10910 cmdcon->conname, RelationGetRelationName(rel)),
10911 ancestorname && ancestortable ?
10912 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
10913 cmdcon->conname, ancestorname, ancestortable) : 0,
10914 errhint("You may alter the constraint it derives from instead.")));
10918 * Do the actual catalog work. We can skip changing if already in the
10919 * desired state, but not if a partitioned table: partitions need to be
10920 * processed regardless, in case they had the constraint locally changed.
10922 address = InvalidObjectAddress;
10923 if (currcon->condeferrable != cmdcon->deferrable ||
10924 currcon->condeferred != cmdcon->initdeferred ||
10925 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10927 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
10928 &otherrelids, lockmode))
10929 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
10933 * ATExecAlterConstrRecurse already invalidated relcache for the relations
10934 * having the constraint itself; here we also invalidate for relations
10935 * that have any triggers that are part of the constraint.
10937 foreach(lc, otherrelids)
10938 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
10940 systable_endscan(scan);
10942 table_close(tgrel, RowExclusiveLock);
10943 table_close(conrel, RowExclusiveLock);
10945 return address;
10949 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
10950 * constraint is altered.
10952 * *otherrelids is appended OIDs of relations containing affected triggers.
10954 * Note that we must recurse even when the values are correct, in case
10955 * indirect descendants have had their constraints altered locally.
10956 * (This could be avoided if we forbade altering constraints in partitions
10957 * but existing releases don't do that.)
10959 static bool
10960 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
10961 Relation rel, HeapTuple contuple, List **otherrelids,
10962 LOCKMODE lockmode)
10964 Form_pg_constraint currcon;
10965 Oid conoid;
10966 Oid refrelid;
10967 bool changed = false;
10969 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
10970 conoid = currcon->oid;
10971 refrelid = currcon->confrelid;
10974 * Update pg_constraint with the flags from cmdcon.
10976 * If called to modify a constraint that's already in the desired state,
10977 * silently do nothing.
10979 if (currcon->condeferrable != cmdcon->deferrable ||
10980 currcon->condeferred != cmdcon->initdeferred)
10982 HeapTuple copyTuple;
10983 Form_pg_constraint copy_con;
10984 HeapTuple tgtuple;
10985 ScanKeyData tgkey;
10986 SysScanDesc tgscan;
10988 copyTuple = heap_copytuple(contuple);
10989 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
10990 copy_con->condeferrable = cmdcon->deferrable;
10991 copy_con->condeferred = cmdcon->initdeferred;
10992 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
10994 InvokeObjectPostAlterHook(ConstraintRelationId,
10995 conoid, 0);
10997 heap_freetuple(copyTuple);
10998 changed = true;
11000 /* Make new constraint flags visible to others */
11001 CacheInvalidateRelcache(rel);
11004 * Now we need to update the multiple entries in pg_trigger that
11005 * implement the constraint.
11007 ScanKeyInit(&tgkey,
11008 Anum_pg_trigger_tgconstraint,
11009 BTEqualStrategyNumber, F_OIDEQ,
11010 ObjectIdGetDatum(conoid));
11011 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11012 NULL, 1, &tgkey);
11013 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11015 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11016 Form_pg_trigger copy_tg;
11017 HeapTuple tgCopyTuple;
11020 * Remember OIDs of other relation(s) involved in FK constraint.
11021 * (Note: it's likely that we could skip forcing a relcache inval
11022 * for other rels that don't have a trigger whose properties
11023 * change, but let's be conservative.)
11025 if (tgform->tgrelid != RelationGetRelid(rel))
11026 *otherrelids = list_append_unique_oid(*otherrelids,
11027 tgform->tgrelid);
11030 * Update deferrability of RI_FKey_noaction_del,
11031 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11032 * triggers, but not others; see createForeignKeyActionTriggers
11033 * and CreateFKCheckTrigger.
11035 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11036 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11037 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11038 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11039 continue;
11041 tgCopyTuple = heap_copytuple(tgtuple);
11042 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11044 copy_tg->tgdeferrable = cmdcon->deferrable;
11045 copy_tg->tginitdeferred = cmdcon->initdeferred;
11046 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11048 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11050 heap_freetuple(tgCopyTuple);
11053 systable_endscan(tgscan);
11057 * If the table at either end of the constraint is partitioned, we need to
11058 * recurse and handle every constraint that is a child of this one.
11060 * (This assumes that the recurse flag is forcibly set for partitioned
11061 * tables, and not set for legacy inheritance, though we don't check for
11062 * that here.)
11064 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11065 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11067 ScanKeyData pkey;
11068 SysScanDesc pscan;
11069 HeapTuple childtup;
11071 ScanKeyInit(&pkey,
11072 Anum_pg_constraint_conparentid,
11073 BTEqualStrategyNumber, F_OIDEQ,
11074 ObjectIdGetDatum(conoid));
11076 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11077 true, NULL, 1, &pkey);
11079 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11081 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11082 Relation childrel;
11084 childrel = table_open(childcon->conrelid, lockmode);
11085 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11086 otherrelids, lockmode);
11087 table_close(childrel, NoLock);
11090 systable_endscan(pscan);
11093 return changed;
11097 * ALTER TABLE VALIDATE CONSTRAINT
11099 * XXX The reason we handle recursion here rather than at Phase 1 is because
11100 * there's no good way to skip recursing when handling foreign keys: there is
11101 * no need to lock children in that case, yet we wouldn't be able to avoid
11102 * doing so at that level.
11104 * Return value is the address of the validated constraint. If the constraint
11105 * was already validated, InvalidObjectAddress is returned.
11107 static ObjectAddress
11108 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11109 bool recurse, bool recursing, LOCKMODE lockmode)
11111 Relation conrel;
11112 SysScanDesc scan;
11113 ScanKeyData skey[3];
11114 HeapTuple tuple;
11115 Form_pg_constraint con;
11116 ObjectAddress address;
11118 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11121 * Find and check the target constraint
11123 ScanKeyInit(&skey[0],
11124 Anum_pg_constraint_conrelid,
11125 BTEqualStrategyNumber, F_OIDEQ,
11126 ObjectIdGetDatum(RelationGetRelid(rel)));
11127 ScanKeyInit(&skey[1],
11128 Anum_pg_constraint_contypid,
11129 BTEqualStrategyNumber, F_OIDEQ,
11130 ObjectIdGetDatum(InvalidOid));
11131 ScanKeyInit(&skey[2],
11132 Anum_pg_constraint_conname,
11133 BTEqualStrategyNumber, F_NAMEEQ,
11134 CStringGetDatum(constrName));
11135 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11136 true, NULL, 3, skey);
11138 /* There can be at most one matching row */
11139 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11140 ereport(ERROR,
11141 (errcode(ERRCODE_UNDEFINED_OBJECT),
11142 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11143 constrName, RelationGetRelationName(rel))));
11145 con = (Form_pg_constraint) GETSTRUCT(tuple);
11146 if (con->contype != CONSTRAINT_FOREIGN &&
11147 con->contype != CONSTRAINT_CHECK)
11148 ereport(ERROR,
11149 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11150 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11151 constrName, RelationGetRelationName(rel))));
11153 if (!con->convalidated)
11155 AlteredTableInfo *tab;
11156 HeapTuple copyTuple;
11157 Form_pg_constraint copy_con;
11159 if (con->contype == CONSTRAINT_FOREIGN)
11161 NewConstraint *newcon;
11162 Constraint *fkconstraint;
11164 /* Queue validation for phase 3 */
11165 fkconstraint = makeNode(Constraint);
11166 /* for now this is all we need */
11167 fkconstraint->conname = constrName;
11169 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11170 newcon->name = constrName;
11171 newcon->contype = CONSTR_FOREIGN;
11172 newcon->refrelid = con->confrelid;
11173 newcon->refindid = con->conindid;
11174 newcon->conid = con->oid;
11175 newcon->qual = (Node *) fkconstraint;
11177 /* Find or create work queue entry for this table */
11178 tab = ATGetQueueEntry(wqueue, rel);
11179 tab->constraints = lappend(tab->constraints, newcon);
11182 * We disallow creating invalid foreign keys to or from
11183 * partitioned tables, so ignoring the recursion bit is okay.
11186 else if (con->contype == CONSTRAINT_CHECK)
11188 List *children = NIL;
11189 ListCell *child;
11190 NewConstraint *newcon;
11191 Datum val;
11192 char *conbin;
11195 * If we're recursing, the parent has already done this, so skip
11196 * it. Also, if the constraint is a NO INHERIT constraint, we
11197 * shouldn't try to look for it in the children.
11199 if (!recursing && !con->connoinherit)
11200 children = find_all_inheritors(RelationGetRelid(rel),
11201 lockmode, NULL);
11204 * For CHECK constraints, we must ensure that we only mark the
11205 * constraint as validated on the parent if it's already validated
11206 * on the children.
11208 * We recurse before validating on the parent, to reduce risk of
11209 * deadlocks.
11211 foreach(child, children)
11213 Oid childoid = lfirst_oid(child);
11214 Relation childrel;
11216 if (childoid == RelationGetRelid(rel))
11217 continue;
11220 * If we are told not to recurse, there had better not be any
11221 * child tables, because we can't mark the constraint on the
11222 * parent valid unless it is valid for all child tables.
11224 if (!recurse)
11225 ereport(ERROR,
11226 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11227 errmsg("constraint must be validated on child tables too")));
11229 /* find_all_inheritors already got lock */
11230 childrel = table_open(childoid, NoLock);
11232 ATExecValidateConstraint(wqueue, childrel, constrName, false,
11233 true, lockmode);
11234 table_close(childrel, NoLock);
11237 /* Queue validation for phase 3 */
11238 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11239 newcon->name = constrName;
11240 newcon->contype = CONSTR_CHECK;
11241 newcon->refrelid = InvalidOid;
11242 newcon->refindid = InvalidOid;
11243 newcon->conid = con->oid;
11245 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11246 Anum_pg_constraint_conbin);
11247 conbin = TextDatumGetCString(val);
11248 newcon->qual = (Node *) stringToNode(conbin);
11250 /* Find or create work queue entry for this table */
11251 tab = ATGetQueueEntry(wqueue, rel);
11252 tab->constraints = lappend(tab->constraints, newcon);
11255 * Invalidate relcache so that others see the new validated
11256 * constraint.
11258 CacheInvalidateRelcache(rel);
11262 * Now update the catalog, while we have the door open.
11264 copyTuple = heap_copytuple(tuple);
11265 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11266 copy_con->convalidated = true;
11267 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11269 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11271 heap_freetuple(copyTuple);
11273 ObjectAddressSet(address, ConstraintRelationId, con->oid);
11275 else
11276 address = InvalidObjectAddress; /* already validated */
11278 systable_endscan(scan);
11280 table_close(conrel, RowExclusiveLock);
11282 return address;
11287 * transformColumnNameList - transform list of column names
11289 * Lookup each name and return its attnum and, optionally, type OID
11291 * Note: the name of this function suggests that it's general-purpose,
11292 * but actually it's only used to look up names appearing in foreign-key
11293 * clauses. The error messages would need work to use it in other cases,
11294 * and perhaps the validity checks as well.
11296 static int
11297 transformColumnNameList(Oid relId, List *colList,
11298 int16 *attnums, Oid *atttypids)
11300 ListCell *l;
11301 int attnum;
11303 attnum = 0;
11304 foreach(l, colList)
11306 char *attname = strVal(lfirst(l));
11307 HeapTuple atttuple;
11308 Form_pg_attribute attform;
11310 atttuple = SearchSysCacheAttName(relId, attname);
11311 if (!HeapTupleIsValid(atttuple))
11312 ereport(ERROR,
11313 (errcode(ERRCODE_UNDEFINED_COLUMN),
11314 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11315 attname)));
11316 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
11317 if (attform->attnum < 0)
11318 ereport(ERROR,
11319 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11320 errmsg("system columns cannot be used in foreign keys")));
11321 if (attnum >= INDEX_MAX_KEYS)
11322 ereport(ERROR,
11323 (errcode(ERRCODE_TOO_MANY_COLUMNS),
11324 errmsg("cannot have more than %d keys in a foreign key",
11325 INDEX_MAX_KEYS)));
11326 attnums[attnum] = attform->attnum;
11327 if (atttypids != NULL)
11328 atttypids[attnum] = attform->atttypid;
11329 ReleaseSysCache(atttuple);
11330 attnum++;
11333 return attnum;
11337 * transformFkeyGetPrimaryKey -
11339 * Look up the names, attnums, and types of the primary key attributes
11340 * for the pkrel. Also return the index OID and index opclasses of the
11341 * index supporting the primary key.
11343 * All parameters except pkrel are output parameters. Also, the function
11344 * return value is the number of attributes in the primary key.
11346 * Used when the column list in the REFERENCES specification is omitted.
11348 static int
11349 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
11350 List **attnamelist,
11351 int16 *attnums, Oid *atttypids,
11352 Oid *opclasses)
11354 List *indexoidlist;
11355 ListCell *indexoidscan;
11356 HeapTuple indexTuple = NULL;
11357 Form_pg_index indexStruct = NULL;
11358 Datum indclassDatum;
11359 oidvector *indclass;
11360 int i;
11363 * Get the list of index OIDs for the table from the relcache, and look up
11364 * each one in the pg_index syscache until we find one marked primary key
11365 * (hopefully there isn't more than one such). Insist it's valid, too.
11367 *indexOid = InvalidOid;
11369 indexoidlist = RelationGetIndexList(pkrel);
11371 foreach(indexoidscan, indexoidlist)
11373 Oid indexoid = lfirst_oid(indexoidscan);
11375 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11376 if (!HeapTupleIsValid(indexTuple))
11377 elog(ERROR, "cache lookup failed for index %u", indexoid);
11378 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11379 if (indexStruct->indisprimary && indexStruct->indisvalid)
11382 * Refuse to use a deferrable primary key. This is per SQL spec,
11383 * and there would be a lot of interesting semantic problems if we
11384 * tried to allow it.
11386 if (!indexStruct->indimmediate)
11387 ereport(ERROR,
11388 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11389 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11390 RelationGetRelationName(pkrel))));
11392 *indexOid = indexoid;
11393 break;
11395 ReleaseSysCache(indexTuple);
11398 list_free(indexoidlist);
11401 * Check that we found it
11403 if (!OidIsValid(*indexOid))
11404 ereport(ERROR,
11405 (errcode(ERRCODE_UNDEFINED_OBJECT),
11406 errmsg("there is no primary key for referenced table \"%s\"",
11407 RelationGetRelationName(pkrel))));
11409 /* Must get indclass the hard way */
11410 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11411 Anum_pg_index_indclass);
11412 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11415 * Now build the list of PK attributes from the indkey definition (we
11416 * assume a primary key cannot have expressional elements)
11418 *attnamelist = NIL;
11419 for (i = 0; i < indexStruct->indnkeyatts; i++)
11421 int pkattno = indexStruct->indkey.values[i];
11423 attnums[i] = pkattno;
11424 atttypids[i] = attnumTypeId(pkrel, pkattno);
11425 opclasses[i] = indclass->values[i];
11426 *attnamelist = lappend(*attnamelist,
11427 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11430 ReleaseSysCache(indexTuple);
11432 return i;
11436 * transformFkeyCheckAttrs -
11438 * Make sure that the attributes of a referenced table belong to a unique
11439 * (or primary key) constraint. Return the OID of the index supporting
11440 * the constraint, as well as the opclasses associated with the index
11441 * columns.
11443 static Oid
11444 transformFkeyCheckAttrs(Relation pkrel,
11445 int numattrs, int16 *attnums,
11446 Oid *opclasses) /* output parameter */
11448 Oid indexoid = InvalidOid;
11449 bool found = false;
11450 bool found_deferrable = false;
11451 List *indexoidlist;
11452 ListCell *indexoidscan;
11453 int i,
11457 * Reject duplicate appearances of columns in the referenced-columns list.
11458 * Such a case is forbidden by the SQL standard, and even if we thought it
11459 * useful to allow it, there would be ambiguity about how to match the
11460 * list to unique indexes (in particular, it'd be unclear which index
11461 * opclass goes with which FK column).
11463 for (i = 0; i < numattrs; i++)
11465 for (j = i + 1; j < numattrs; j++)
11467 if (attnums[i] == attnums[j])
11468 ereport(ERROR,
11469 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11470 errmsg("foreign key referenced-columns list must not contain duplicates")));
11475 * Get the list of index OIDs for the table from the relcache, and look up
11476 * each one in the pg_index syscache, and match unique indexes to the list
11477 * of attnums we are given.
11479 indexoidlist = RelationGetIndexList(pkrel);
11481 foreach(indexoidscan, indexoidlist)
11483 HeapTuple indexTuple;
11484 Form_pg_index indexStruct;
11486 indexoid = lfirst_oid(indexoidscan);
11487 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11488 if (!HeapTupleIsValid(indexTuple))
11489 elog(ERROR, "cache lookup failed for index %u", indexoid);
11490 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11493 * Must have the right number of columns; must be unique and not a
11494 * partial index; forget it if there are any expressions, too. Invalid
11495 * indexes are out as well.
11497 if (indexStruct->indnkeyatts == numattrs &&
11498 indexStruct->indisunique &&
11499 indexStruct->indisvalid &&
11500 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11501 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
11503 Datum indclassDatum;
11504 oidvector *indclass;
11506 /* Must get indclass the hard way */
11507 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11508 Anum_pg_index_indclass);
11509 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11512 * The given attnum list may match the index columns in any order.
11513 * Check for a match, and extract the appropriate opclasses while
11514 * we're at it.
11516 * We know that attnums[] is duplicate-free per the test at the
11517 * start of this function, and we checked above that the number of
11518 * index columns agrees, so if we find a match for each attnums[]
11519 * entry then we must have a one-to-one match in some order.
11521 for (i = 0; i < numattrs; i++)
11523 found = false;
11524 for (j = 0; j < numattrs; j++)
11526 if (attnums[i] == indexStruct->indkey.values[j])
11528 opclasses[i] = indclass->values[j];
11529 found = true;
11530 break;
11533 if (!found)
11534 break;
11538 * Refuse to use a deferrable unique/primary key. This is per SQL
11539 * spec, and there would be a lot of interesting semantic problems
11540 * if we tried to allow it.
11542 if (found && !indexStruct->indimmediate)
11545 * Remember that we found an otherwise matching index, so that
11546 * we can generate a more appropriate error message.
11548 found_deferrable = true;
11549 found = false;
11552 ReleaseSysCache(indexTuple);
11553 if (found)
11554 break;
11557 if (!found)
11559 if (found_deferrable)
11560 ereport(ERROR,
11561 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11562 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
11563 RelationGetRelationName(pkrel))));
11564 else
11565 ereport(ERROR,
11566 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11567 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
11568 RelationGetRelationName(pkrel))));
11571 list_free(indexoidlist);
11573 return indexoid;
11577 * findFkeyCast -
11579 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
11580 * Caller has equal regard for binary coercibility and for an exact match.
11582 static CoercionPathType
11583 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
11585 CoercionPathType ret;
11587 if (targetTypeId == sourceTypeId)
11589 ret = COERCION_PATH_RELABELTYPE;
11590 *funcid = InvalidOid;
11592 else
11594 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
11595 COERCION_IMPLICIT, funcid);
11596 if (ret == COERCION_PATH_NONE)
11597 /* A previously-relied-upon cast is now gone. */
11598 elog(ERROR, "could not find cast from %u to %u",
11599 sourceTypeId, targetTypeId);
11602 return ret;
11606 * Permissions checks on the referenced table for ADD FOREIGN KEY
11608 * Note: we have already checked that the user owns the referencing table,
11609 * else we'd have failed much earlier; no additional checks are needed for it.
11611 static void
11612 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
11614 Oid roleid = GetUserId();
11615 AclResult aclresult;
11616 int i;
11618 /* Okay if we have relation-level REFERENCES permission */
11619 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
11620 ACL_REFERENCES);
11621 if (aclresult == ACLCHECK_OK)
11622 return;
11623 /* Else we must have REFERENCES on each column */
11624 for (i = 0; i < natts; i++)
11626 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
11627 roleid, ACL_REFERENCES);
11628 if (aclresult != ACLCHECK_OK)
11629 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
11630 RelationGetRelationName(rel));
11635 * Scan the existing rows in a table to verify they meet a proposed FK
11636 * constraint.
11638 * Caller must have opened and locked both relations appropriately.
11640 static void
11641 validateForeignKeyConstraint(char *conname,
11642 Relation rel,
11643 Relation pkrel,
11644 Oid pkindOid,
11645 Oid constraintOid)
11647 TupleTableSlot *slot;
11648 TableScanDesc scan;
11649 Trigger trig = {0};
11650 Snapshot snapshot;
11651 MemoryContext oldcxt;
11652 MemoryContext perTupCxt;
11654 ereport(DEBUG1,
11655 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
11658 * Build a trigger call structure; we'll need it either way.
11660 trig.tgoid = InvalidOid;
11661 trig.tgname = conname;
11662 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
11663 trig.tgisinternal = true;
11664 trig.tgconstrrelid = RelationGetRelid(pkrel);
11665 trig.tgconstrindid = pkindOid;
11666 trig.tgconstraint = constraintOid;
11667 trig.tgdeferrable = false;
11668 trig.tginitdeferred = false;
11669 /* we needn't fill in remaining fields */
11672 * See if we can do it with a single LEFT JOIN query. A false result
11673 * indicates we must proceed with the fire-the-trigger method.
11675 if (RI_Initial_Check(&trig, rel, pkrel))
11676 return;
11679 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
11680 * if that tuple had just been inserted. If any of those fail, it should
11681 * ereport(ERROR) and that's that.
11683 snapshot = RegisterSnapshot(GetLatestSnapshot());
11684 slot = table_slot_create(rel, NULL);
11685 scan = table_beginscan(rel, snapshot, 0, NULL);
11687 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
11688 "validateForeignKeyConstraint",
11689 ALLOCSET_SMALL_SIZES);
11690 oldcxt = MemoryContextSwitchTo(perTupCxt);
11692 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
11694 LOCAL_FCINFO(fcinfo, 0);
11695 TriggerData trigdata = {0};
11697 CHECK_FOR_INTERRUPTS();
11700 * Make a call to the trigger function
11702 * No parameters are passed, but we do set a context
11704 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
11707 * We assume RI_FKey_check_ins won't look at flinfo...
11709 trigdata.type = T_TriggerData;
11710 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
11711 trigdata.tg_relation = rel;
11712 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
11713 trigdata.tg_trigslot = slot;
11714 trigdata.tg_trigger = &trig;
11716 fcinfo->context = (Node *) &trigdata;
11718 RI_FKey_check_ins(fcinfo);
11720 MemoryContextReset(perTupCxt);
11723 MemoryContextSwitchTo(oldcxt);
11724 MemoryContextDelete(perTupCxt);
11725 table_endscan(scan);
11726 UnregisterSnapshot(snapshot);
11727 ExecDropSingleTupleTableSlot(slot);
11731 * CreateFKCheckTrigger
11732 * Creates the insert (on_insert=true) or update "check" trigger that
11733 * implements a given foreign key
11735 * Returns the OID of the so created trigger.
11737 static Oid
11738 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
11739 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
11740 bool on_insert)
11742 ObjectAddress trigAddress;
11743 CreateTrigStmt *fk_trigger;
11746 * Note: for a self-referential FK (referencing and referenced tables are
11747 * the same), it is important that the ON UPDATE action fires before the
11748 * CHECK action, since both triggers will fire on the same row during an
11749 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
11750 * state of the row. Triggers fire in name order, so we ensure this by
11751 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
11752 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
11754 fk_trigger = makeNode(CreateTrigStmt);
11755 fk_trigger->replace = false;
11756 fk_trigger->isconstraint = true;
11757 fk_trigger->trigname = "RI_ConstraintTrigger_c";
11758 fk_trigger->relation = NULL;
11760 /* Either ON INSERT or ON UPDATE */
11761 if (on_insert)
11763 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
11764 fk_trigger->events = TRIGGER_TYPE_INSERT;
11766 else
11768 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
11769 fk_trigger->events = TRIGGER_TYPE_UPDATE;
11772 fk_trigger->args = NIL;
11773 fk_trigger->row = true;
11774 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11775 fk_trigger->columns = NIL;
11776 fk_trigger->whenClause = NULL;
11777 fk_trigger->transitionRels = NIL;
11778 fk_trigger->deferrable = fkconstraint->deferrable;
11779 fk_trigger->initdeferred = fkconstraint->initdeferred;
11780 fk_trigger->constrrel = NULL;
11782 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
11783 constraintOid, indexOid, InvalidOid,
11784 parentTrigOid, NULL, true, false);
11786 /* Make changes-so-far visible */
11787 CommandCounterIncrement();
11789 return trigAddress.objectId;
11793 * createForeignKeyActionTriggers
11794 * Create the referenced-side "action" triggers that implement a foreign
11795 * key.
11797 * Returns the OIDs of the so created triggers in *deleteTrigOid and
11798 * *updateTrigOid.
11800 static void
11801 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
11802 Oid constraintOid, Oid indexOid,
11803 Oid parentDelTrigger, Oid parentUpdTrigger,
11804 Oid *deleteTrigOid, Oid *updateTrigOid)
11806 CreateTrigStmt *fk_trigger;
11807 ObjectAddress trigAddress;
11810 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11811 * DELETE action on the referenced table.
11813 fk_trigger = makeNode(CreateTrigStmt);
11814 fk_trigger->replace = false;
11815 fk_trigger->isconstraint = true;
11816 fk_trigger->trigname = "RI_ConstraintTrigger_a";
11817 fk_trigger->relation = NULL;
11818 fk_trigger->args = NIL;
11819 fk_trigger->row = true;
11820 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11821 fk_trigger->events = TRIGGER_TYPE_DELETE;
11822 fk_trigger->columns = NIL;
11823 fk_trigger->whenClause = NULL;
11824 fk_trigger->transitionRels = NIL;
11825 fk_trigger->constrrel = NULL;
11826 switch (fkconstraint->fk_del_action)
11828 case FKCONSTR_ACTION_NOACTION:
11829 fk_trigger->deferrable = fkconstraint->deferrable;
11830 fk_trigger->initdeferred = fkconstraint->initdeferred;
11831 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
11832 break;
11833 case FKCONSTR_ACTION_RESTRICT:
11834 fk_trigger->deferrable = false;
11835 fk_trigger->initdeferred = false;
11836 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
11837 break;
11838 case FKCONSTR_ACTION_CASCADE:
11839 fk_trigger->deferrable = false;
11840 fk_trigger->initdeferred = false;
11841 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
11842 break;
11843 case FKCONSTR_ACTION_SETNULL:
11844 fk_trigger->deferrable = false;
11845 fk_trigger->initdeferred = false;
11846 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
11847 break;
11848 case FKCONSTR_ACTION_SETDEFAULT:
11849 fk_trigger->deferrable = false;
11850 fk_trigger->initdeferred = false;
11851 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
11852 break;
11853 default:
11854 elog(ERROR, "unrecognized FK action type: %d",
11855 (int) fkconstraint->fk_del_action);
11856 break;
11859 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
11860 RelationGetRelid(rel),
11861 constraintOid, indexOid, InvalidOid,
11862 parentDelTrigger, NULL, true, false);
11863 if (deleteTrigOid)
11864 *deleteTrigOid = trigAddress.objectId;
11866 /* Make changes-so-far visible */
11867 CommandCounterIncrement();
11870 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
11871 * UPDATE action on the referenced table.
11873 fk_trigger = makeNode(CreateTrigStmt);
11874 fk_trigger->replace = false;
11875 fk_trigger->isconstraint = true;
11876 fk_trigger->trigname = "RI_ConstraintTrigger_a";
11877 fk_trigger->relation = NULL;
11878 fk_trigger->args = NIL;
11879 fk_trigger->row = true;
11880 fk_trigger->timing = TRIGGER_TYPE_AFTER;
11881 fk_trigger->events = TRIGGER_TYPE_UPDATE;
11882 fk_trigger->columns = NIL;
11883 fk_trigger->whenClause = NULL;
11884 fk_trigger->transitionRels = NIL;
11885 fk_trigger->constrrel = NULL;
11886 switch (fkconstraint->fk_upd_action)
11888 case FKCONSTR_ACTION_NOACTION:
11889 fk_trigger->deferrable = fkconstraint->deferrable;
11890 fk_trigger->initdeferred = fkconstraint->initdeferred;
11891 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
11892 break;
11893 case FKCONSTR_ACTION_RESTRICT:
11894 fk_trigger->deferrable = false;
11895 fk_trigger->initdeferred = false;
11896 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
11897 break;
11898 case FKCONSTR_ACTION_CASCADE:
11899 fk_trigger->deferrable = false;
11900 fk_trigger->initdeferred = false;
11901 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
11902 break;
11903 case FKCONSTR_ACTION_SETNULL:
11904 fk_trigger->deferrable = false;
11905 fk_trigger->initdeferred = false;
11906 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
11907 break;
11908 case FKCONSTR_ACTION_SETDEFAULT:
11909 fk_trigger->deferrable = false;
11910 fk_trigger->initdeferred = false;
11911 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
11912 break;
11913 default:
11914 elog(ERROR, "unrecognized FK action type: %d",
11915 (int) fkconstraint->fk_upd_action);
11916 break;
11919 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
11920 RelationGetRelid(rel),
11921 constraintOid, indexOid, InvalidOid,
11922 parentUpdTrigger, NULL, true, false);
11923 if (updateTrigOid)
11924 *updateTrigOid = trigAddress.objectId;
11928 * createForeignKeyCheckTriggers
11929 * Create the referencing-side "check" triggers that implement a foreign
11930 * key.
11932 * Returns the OIDs of the so created triggers in *insertTrigOid and
11933 * *updateTrigOid.
11935 static void
11936 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
11937 Constraint *fkconstraint, Oid constraintOid,
11938 Oid indexOid,
11939 Oid parentInsTrigger, Oid parentUpdTrigger,
11940 Oid *insertTrigOid, Oid *updateTrigOid)
11942 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
11943 constraintOid, indexOid,
11944 parentInsTrigger, true);
11945 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
11946 constraintOid, indexOid,
11947 parentUpdTrigger, false);
11951 * ALTER TABLE DROP CONSTRAINT
11953 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
11955 static void
11956 ATExecDropConstraint(Relation rel, const char *constrName,
11957 DropBehavior behavior,
11958 bool recurse, bool recursing,
11959 bool missing_ok, LOCKMODE lockmode)
11961 List *children;
11962 ListCell *child;
11963 Relation conrel;
11964 Form_pg_constraint con;
11965 SysScanDesc scan;
11966 ScanKeyData skey[3];
11967 HeapTuple tuple;
11968 bool found = false;
11969 bool is_no_inherit_constraint = false;
11970 char contype;
11972 /* At top level, permission check was done in ATPrepCmd, else do it */
11973 if (recursing)
11974 ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
11976 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11979 * Find and drop the target constraint
11981 ScanKeyInit(&skey[0],
11982 Anum_pg_constraint_conrelid,
11983 BTEqualStrategyNumber, F_OIDEQ,
11984 ObjectIdGetDatum(RelationGetRelid(rel)));
11985 ScanKeyInit(&skey[1],
11986 Anum_pg_constraint_contypid,
11987 BTEqualStrategyNumber, F_OIDEQ,
11988 ObjectIdGetDatum(InvalidOid));
11989 ScanKeyInit(&skey[2],
11990 Anum_pg_constraint_conname,
11991 BTEqualStrategyNumber, F_NAMEEQ,
11992 CStringGetDatum(constrName));
11993 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11994 true, NULL, 3, skey);
11996 /* There can be at most one matching row */
11997 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
11999 ObjectAddress conobj;
12001 con = (Form_pg_constraint) GETSTRUCT(tuple);
12003 /* Don't drop inherited constraints */
12004 if (con->coninhcount > 0 && !recursing)
12005 ereport(ERROR,
12006 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12007 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12008 constrName, RelationGetRelationName(rel))));
12010 is_no_inherit_constraint = con->connoinherit;
12011 contype = con->contype;
12014 * If it's a foreign-key constraint, we'd better lock the referenced
12015 * table and check that that's not in use, just as we've already done
12016 * for the constrained table (else we might, eg, be dropping a trigger
12017 * that has unfired events). But we can/must skip that in the
12018 * self-referential case.
12020 if (contype == CONSTRAINT_FOREIGN &&
12021 con->confrelid != RelationGetRelid(rel))
12023 Relation frel;
12025 /* Must match lock taken by RemoveTriggerById: */
12026 frel = table_open(con->confrelid, AccessExclusiveLock);
12027 CheckTableNotInUse(frel, "ALTER TABLE");
12028 table_close(frel, NoLock);
12032 * Perform the actual constraint deletion
12034 conobj.classId = ConstraintRelationId;
12035 conobj.objectId = con->oid;
12036 conobj.objectSubId = 0;
12038 performDeletion(&conobj, behavior, 0);
12040 found = true;
12043 systable_endscan(scan);
12045 if (!found)
12047 if (!missing_ok)
12049 ereport(ERROR,
12050 (errcode(ERRCODE_UNDEFINED_OBJECT),
12051 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12052 constrName, RelationGetRelationName(rel))));
12054 else
12056 ereport(NOTICE,
12057 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12058 constrName, RelationGetRelationName(rel))));
12059 table_close(conrel, RowExclusiveLock);
12060 return;
12065 * For partitioned tables, non-CHECK inherited constraints are dropped via
12066 * the dependency mechanism, so we're done here.
12068 if (contype != CONSTRAINT_CHECK &&
12069 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12071 table_close(conrel, RowExclusiveLock);
12072 return;
12076 * Propagate to children as appropriate. Unlike most other ALTER
12077 * routines, we have to do this one level of recursion at a time; we can't
12078 * use find_all_inheritors to do it in one pass.
12080 if (!is_no_inherit_constraint)
12081 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12082 else
12083 children = NIL;
12086 * For a partitioned table, if partitions exist and we are told not to
12087 * recurse, it's a user error. It doesn't make sense to have a constraint
12088 * be defined only on the parent, especially if it's a partitioned table.
12090 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
12091 children != NIL && !recurse)
12092 ereport(ERROR,
12093 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12094 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12095 errhint("Do not specify the ONLY keyword.")));
12097 foreach(child, children)
12099 Oid childrelid = lfirst_oid(child);
12100 Relation childrel;
12101 HeapTuple copy_tuple;
12103 /* find_inheritance_children already got lock */
12104 childrel = table_open(childrelid, NoLock);
12105 CheckTableNotInUse(childrel, "ALTER TABLE");
12107 ScanKeyInit(&skey[0],
12108 Anum_pg_constraint_conrelid,
12109 BTEqualStrategyNumber, F_OIDEQ,
12110 ObjectIdGetDatum(childrelid));
12111 ScanKeyInit(&skey[1],
12112 Anum_pg_constraint_contypid,
12113 BTEqualStrategyNumber, F_OIDEQ,
12114 ObjectIdGetDatum(InvalidOid));
12115 ScanKeyInit(&skey[2],
12116 Anum_pg_constraint_conname,
12117 BTEqualStrategyNumber, F_NAMEEQ,
12118 CStringGetDatum(constrName));
12119 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12120 true, NULL, 3, skey);
12122 /* There can be at most one matching row */
12123 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12124 ereport(ERROR,
12125 (errcode(ERRCODE_UNDEFINED_OBJECT),
12126 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12127 constrName,
12128 RelationGetRelationName(childrel))));
12130 copy_tuple = heap_copytuple(tuple);
12132 systable_endscan(scan);
12134 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
12136 /* Right now only CHECK constraints can be inherited */
12137 if (con->contype != CONSTRAINT_CHECK)
12138 elog(ERROR, "inherited constraint is not a CHECK constraint");
12140 if (con->coninhcount <= 0) /* shouldn't happen */
12141 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
12142 childrelid, constrName);
12144 if (recurse)
12147 * If the child constraint has other definition sources, just
12148 * decrement its inheritance count; if not, recurse to delete it.
12150 if (con->coninhcount == 1 && !con->conislocal)
12152 /* Time to delete this child constraint, too */
12153 ATExecDropConstraint(childrel, constrName, behavior,
12154 true, true,
12155 false, lockmode);
12157 else
12159 /* Child constraint must survive my deletion */
12160 con->coninhcount--;
12161 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12163 /* Make update visible */
12164 CommandCounterIncrement();
12167 else
12170 * If we were told to drop ONLY in this table (no recursion), we
12171 * need to mark the inheritors' constraints as locally defined
12172 * rather than inherited.
12174 con->coninhcount--;
12175 con->conislocal = true;
12177 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12179 /* Make update visible */
12180 CommandCounterIncrement();
12183 heap_freetuple(copy_tuple);
12185 table_close(childrel, NoLock);
12188 table_close(conrel, RowExclusiveLock);
12192 * ALTER COLUMN TYPE
12194 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12195 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12196 * transformed (and must be, because we rely on some transformed fields).
12198 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12199 * table will be done "in parallel" during phase 3, so all the USING
12200 * expressions should be parsed assuming the original column types. Also,
12201 * this allows a USING expression to refer to a field that will be dropped.
12203 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12204 * the first two execution steps in phase 2; they must not see the effects
12205 * of any other subcommand types, since the USING expressions are parsed
12206 * against the unmodified table's state.
12208 static void
12209 ATPrepAlterColumnType(List **wqueue,
12210 AlteredTableInfo *tab, Relation rel,
12211 bool recurse, bool recursing,
12212 AlterTableCmd *cmd, LOCKMODE lockmode,
12213 AlterTableUtilityContext *context)
12215 char *colName = cmd->name;
12216 ColumnDef *def = (ColumnDef *) cmd->def;
12217 TypeName *typeName = def->typeName;
12218 Node *transform = def->cooked_default;
12219 HeapTuple tuple;
12220 Form_pg_attribute attTup;
12221 AttrNumber attnum;
12222 Oid targettype;
12223 int32 targettypmod;
12224 Oid targetcollid;
12225 NewColumnValue *newval;
12226 ParseState *pstate = make_parsestate(NULL);
12227 AclResult aclresult;
12228 bool is_expr;
12230 if (rel->rd_rel->reloftype && !recursing)
12231 ereport(ERROR,
12232 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12233 errmsg("cannot alter column type of typed table")));
12235 /* lookup the attribute so we can check inheritance status */
12236 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12237 if (!HeapTupleIsValid(tuple))
12238 ereport(ERROR,
12239 (errcode(ERRCODE_UNDEFINED_COLUMN),
12240 errmsg("column \"%s\" of relation \"%s\" does not exist",
12241 colName, RelationGetRelationName(rel))));
12242 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12243 attnum = attTup->attnum;
12245 /* Can't alter a system attribute */
12246 if (attnum <= 0)
12247 ereport(ERROR,
12248 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12249 errmsg("cannot alter system column \"%s\"",
12250 colName)));
12253 * Don't alter inherited columns. At outer level, there had better not be
12254 * any inherited definition; when recursing, we assume this was checked at
12255 * the parent level (see below).
12257 if (attTup->attinhcount > 0 && !recursing)
12258 ereport(ERROR,
12259 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12260 errmsg("cannot alter inherited column \"%s\"",
12261 colName)));
12263 /* Don't alter columns used in the partition key */
12264 if (has_partition_attrs(rel,
12265 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
12266 &is_expr))
12267 ereport(ERROR,
12268 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12269 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12270 colName, RelationGetRelationName(rel))));
12272 /* Look up the target type */
12273 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
12275 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
12276 if (aclresult != ACLCHECK_OK)
12277 aclcheck_error_type(aclresult, targettype);
12279 /* And the collation */
12280 targetcollid = GetColumnDefCollation(NULL, def, targettype);
12282 /* make sure datatype is legal for a column */
12283 CheckAttributeType(colName, targettype, targetcollid,
12284 list_make1_oid(rel->rd_rel->reltype),
12287 if (tab->relkind == RELKIND_RELATION ||
12288 tab->relkind == RELKIND_PARTITIONED_TABLE)
12291 * Set up an expression to transform the old data value to the new
12292 * type. If a USING option was given, use the expression as
12293 * transformed by transformAlterTableStmt, else just take the old
12294 * value and try to coerce it. We do this first so that type
12295 * incompatibility can be detected before we waste effort, and because
12296 * we need the expression to be parsed against the original table row
12297 * type.
12299 if (!transform)
12301 transform = (Node *) makeVar(1, attnum,
12302 attTup->atttypid, attTup->atttypmod,
12303 attTup->attcollation,
12307 transform = coerce_to_target_type(pstate,
12308 transform, exprType(transform),
12309 targettype, targettypmod,
12310 COERCION_ASSIGNMENT,
12311 COERCE_IMPLICIT_CAST,
12312 -1);
12313 if (transform == NULL)
12315 /* error text depends on whether USING was specified or not */
12316 if (def->cooked_default != NULL)
12317 ereport(ERROR,
12318 (errcode(ERRCODE_DATATYPE_MISMATCH),
12319 errmsg("result of USING clause for column \"%s\""
12320 " cannot be cast automatically to type %s",
12321 colName, format_type_be(targettype)),
12322 errhint("You might need to add an explicit cast.")));
12323 else
12324 ereport(ERROR,
12325 (errcode(ERRCODE_DATATYPE_MISMATCH),
12326 errmsg("column \"%s\" cannot be cast automatically to type %s",
12327 colName, format_type_be(targettype)),
12328 /* translator: USING is SQL, don't translate it */
12329 errhint("You might need to specify \"USING %s::%s\".",
12330 quote_identifier(colName),
12331 format_type_with_typemod(targettype,
12332 targettypmod))));
12335 /* Fix collations after all else */
12336 assign_expr_collations(pstate, transform);
12338 /* Plan the expr now so we can accurately assess the need to rewrite. */
12339 transform = (Node *) expression_planner((Expr *) transform);
12342 * Add a work queue item to make ATRewriteTable update the column
12343 * contents.
12345 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
12346 newval->attnum = attnum;
12347 newval->expr = (Expr *) transform;
12348 newval->is_generated = false;
12350 tab->newvals = lappend(tab->newvals, newval);
12351 if (ATColumnChangeRequiresRewrite(transform, attnum))
12352 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
12354 else if (transform)
12355 ereport(ERROR,
12356 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12357 errmsg("\"%s\" is not a table",
12358 RelationGetRelationName(rel))));
12360 if (!RELKIND_HAS_STORAGE(tab->relkind))
12363 * For relations without storage, do this check now. Regular tables
12364 * will check it later when the table is being rewritten.
12366 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
12369 ReleaseSysCache(tuple);
12372 * Recurse manually by queueing a new command for each child, if
12373 * necessary. We cannot apply ATSimpleRecursion here because we need to
12374 * remap attribute numbers in the USING expression, if any.
12376 * If we are told not to recurse, there had better not be any child
12377 * tables; else the alter would put them out of step.
12379 if (recurse)
12381 Oid relid = RelationGetRelid(rel);
12382 List *child_oids,
12383 *child_numparents;
12384 ListCell *lo,
12385 *li;
12387 child_oids = find_all_inheritors(relid, lockmode,
12388 &child_numparents);
12391 * find_all_inheritors does the recursive search of the inheritance
12392 * hierarchy, so all we have to do is process all of the relids in the
12393 * list that it returns.
12395 forboth(lo, child_oids, li, child_numparents)
12397 Oid childrelid = lfirst_oid(lo);
12398 int numparents = lfirst_int(li);
12399 Relation childrel;
12400 HeapTuple childtuple;
12401 Form_pg_attribute childattTup;
12403 if (childrelid == relid)
12404 continue;
12406 /* find_all_inheritors already got lock */
12407 childrel = relation_open(childrelid, NoLock);
12408 CheckTableNotInUse(childrel, "ALTER TABLE");
12411 * Verify that the child doesn't have any inherited definitions of
12412 * this column that came from outside this inheritance hierarchy.
12413 * (renameatt makes a similar test, though in a different way
12414 * because of its different recursion mechanism.)
12416 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
12417 colName);
12418 if (!HeapTupleIsValid(childtuple))
12419 ereport(ERROR,
12420 (errcode(ERRCODE_UNDEFINED_COLUMN),
12421 errmsg("column \"%s\" of relation \"%s\" does not exist",
12422 colName, RelationGetRelationName(childrel))));
12423 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
12425 if (childattTup->attinhcount > numparents)
12426 ereport(ERROR,
12427 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12428 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12429 colName, RelationGetRelationName(childrel))));
12431 ReleaseSysCache(childtuple);
12434 * Remap the attribute numbers. If no USING expression was
12435 * specified, there is no need for this step.
12437 if (def->cooked_default)
12439 AttrMap *attmap;
12440 bool found_whole_row;
12442 /* create a copy to scribble on */
12443 cmd = copyObject(cmd);
12445 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
12446 RelationGetDescr(rel),
12447 false);
12448 ((ColumnDef *) cmd->def)->cooked_default =
12449 map_variable_attnos(def->cooked_default,
12450 1, 0,
12451 attmap,
12452 InvalidOid, &found_whole_row);
12453 if (found_whole_row)
12454 ereport(ERROR,
12455 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12456 errmsg("cannot convert whole-row table reference"),
12457 errdetail("USING expression contains a whole-row table reference.")));
12458 pfree(attmap);
12460 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
12461 relation_close(childrel, NoLock);
12464 else if (!recursing &&
12465 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
12466 ereport(ERROR,
12467 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12468 errmsg("type of inherited column \"%s\" must be changed in child tables too",
12469 colName)));
12471 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
12472 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
12476 * When the data type of a column is changed, a rewrite might not be required
12477 * if the new type is sufficiently identical to the old one, and the USING
12478 * clause isn't trying to insert some other value. It's safe to skip the
12479 * rewrite in these cases:
12481 * - the old type is binary coercible to the new type
12482 * - the new type is an unconstrained domain over the old type
12483 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
12485 * In the case of a constrained domain, we could get by with scanning the
12486 * table and checking the constraint rather than actually rewriting it, but we
12487 * don't currently try to do that.
12489 static bool
12490 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
12492 Assert(expr != NULL);
12494 for (;;)
12496 /* only one varno, so no need to check that */
12497 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
12498 return false;
12499 else if (IsA(expr, RelabelType))
12500 expr = (Node *) ((RelabelType *) expr)->arg;
12501 else if (IsA(expr, CoerceToDomain))
12503 CoerceToDomain *d = (CoerceToDomain *) expr;
12505 if (DomainHasConstraints(d->resulttype))
12506 return true;
12507 expr = (Node *) d->arg;
12509 else if (IsA(expr, FuncExpr))
12511 FuncExpr *f = (FuncExpr *) expr;
12513 switch (f->funcid)
12515 case F_TIMESTAMPTZ_TIMESTAMP:
12516 case F_TIMESTAMP_TIMESTAMPTZ:
12517 if (TimestampTimestampTzRequiresRewrite())
12518 return true;
12519 else
12520 expr = linitial(f->args);
12521 break;
12522 default:
12523 return true;
12526 else
12527 return true;
12532 * ALTER COLUMN .. SET DATA TYPE
12534 * Return the address of the modified column.
12536 static ObjectAddress
12537 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
12538 AlterTableCmd *cmd, LOCKMODE lockmode)
12540 char *colName = cmd->name;
12541 ColumnDef *def = (ColumnDef *) cmd->def;
12542 TypeName *typeName = def->typeName;
12543 HeapTuple heapTup;
12544 Form_pg_attribute attTup,
12545 attOldTup;
12546 AttrNumber attnum;
12547 HeapTuple typeTuple;
12548 Form_pg_type tform;
12549 Oid targettype;
12550 int32 targettypmod;
12551 Oid targetcollid;
12552 Node *defaultexpr;
12553 Relation attrelation;
12554 Relation depRel;
12555 ScanKeyData key[3];
12556 SysScanDesc scan;
12557 HeapTuple depTup;
12558 ObjectAddress address;
12561 * Clear all the missing values if we're rewriting the table, since this
12562 * renders them pointless.
12564 if (tab->rewrite)
12566 Relation newrel;
12568 newrel = table_open(RelationGetRelid(rel), NoLock);
12569 RelationClearMissing(newrel);
12570 relation_close(newrel, NoLock);
12571 /* make sure we don't conflict with later attribute modifications */
12572 CommandCounterIncrement();
12575 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
12577 /* Look up the target column */
12578 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
12579 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
12580 ereport(ERROR,
12581 (errcode(ERRCODE_UNDEFINED_COLUMN),
12582 errmsg("column \"%s\" of relation \"%s\" does not exist",
12583 colName, RelationGetRelationName(rel))));
12584 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
12585 attnum = attTup->attnum;
12586 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
12588 /* Check for multiple ALTER TYPE on same column --- can't cope */
12589 if (attTup->atttypid != attOldTup->atttypid ||
12590 attTup->atttypmod != attOldTup->atttypmod)
12591 ereport(ERROR,
12592 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12593 errmsg("cannot alter type of column \"%s\" twice",
12594 colName)));
12596 /* Look up the target type (should not fail, since prep found it) */
12597 typeTuple = typenameType(NULL, typeName, &targettypmod);
12598 tform = (Form_pg_type) GETSTRUCT(typeTuple);
12599 targettype = tform->oid;
12600 /* And the collation */
12601 targetcollid = GetColumnDefCollation(NULL, def, targettype);
12604 * If there is a default expression for the column, get it and ensure we
12605 * can coerce it to the new datatype. (We must do this before changing
12606 * the column type, because build_column_default itself will try to
12607 * coerce, and will not issue the error message we want if it fails.)
12609 * We remove any implicit coercion steps at the top level of the old
12610 * default expression; this has been agreed to satisfy the principle of
12611 * least surprise. (The conversion to the new column type should act like
12612 * it started from what the user sees as the stored expression, and the
12613 * implicit coercions aren't going to be shown.)
12615 if (attTup->atthasdef)
12617 defaultexpr = build_column_default(rel, attnum);
12618 Assert(defaultexpr);
12619 defaultexpr = strip_implicit_coercions(defaultexpr);
12620 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
12621 defaultexpr, exprType(defaultexpr),
12622 targettype, targettypmod,
12623 COERCION_ASSIGNMENT,
12624 COERCE_IMPLICIT_CAST,
12625 -1);
12626 if (defaultexpr == NULL)
12628 if (attTup->attgenerated)
12629 ereport(ERROR,
12630 (errcode(ERRCODE_DATATYPE_MISMATCH),
12631 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
12632 colName, format_type_be(targettype))));
12633 else
12634 ereport(ERROR,
12635 (errcode(ERRCODE_DATATYPE_MISMATCH),
12636 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
12637 colName, format_type_be(targettype))));
12640 else
12641 defaultexpr = NULL;
12644 * Find everything that depends on the column (constraints, indexes, etc),
12645 * and record enough information to let us recreate the objects.
12647 * The actual recreation does not happen here, but only after we have
12648 * performed all the individual ALTER TYPE operations. We have to save
12649 * the info before executing ALTER TYPE, though, else the deparser will
12650 * get confused.
12652 depRel = table_open(DependRelationId, RowExclusiveLock);
12654 ScanKeyInit(&key[0],
12655 Anum_pg_depend_refclassid,
12656 BTEqualStrategyNumber, F_OIDEQ,
12657 ObjectIdGetDatum(RelationRelationId));
12658 ScanKeyInit(&key[1],
12659 Anum_pg_depend_refobjid,
12660 BTEqualStrategyNumber, F_OIDEQ,
12661 ObjectIdGetDatum(RelationGetRelid(rel)));
12662 ScanKeyInit(&key[2],
12663 Anum_pg_depend_refobjsubid,
12664 BTEqualStrategyNumber, F_INT4EQ,
12665 Int32GetDatum((int32) attnum));
12667 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
12668 NULL, 3, key);
12670 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
12672 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
12673 ObjectAddress foundObject;
12675 foundObject.classId = foundDep->classid;
12676 foundObject.objectId = foundDep->objid;
12677 foundObject.objectSubId = foundDep->objsubid;
12679 switch (getObjectClass(&foundObject))
12681 case OCLASS_CLASS:
12683 char relKind = get_rel_relkind(foundObject.objectId);
12685 if (relKind == RELKIND_INDEX ||
12686 relKind == RELKIND_PARTITIONED_INDEX)
12688 Assert(foundObject.objectSubId == 0);
12689 RememberIndexForRebuilding(foundObject.objectId, tab);
12691 else if (relKind == RELKIND_SEQUENCE)
12694 * This must be a SERIAL column's sequence. We need
12695 * not do anything to it.
12697 Assert(foundObject.objectSubId == 0);
12699 else
12701 /* Not expecting any other direct dependencies... */
12702 elog(ERROR, "unexpected object depending on column: %s",
12703 getObjectDescription(&foundObject, false));
12705 break;
12708 case OCLASS_CONSTRAINT:
12709 Assert(foundObject.objectSubId == 0);
12710 RememberConstraintForRebuilding(foundObject.objectId, tab);
12711 break;
12713 case OCLASS_REWRITE:
12714 /* XXX someday see if we can cope with revising views */
12715 ereport(ERROR,
12716 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12717 errmsg("cannot alter type of a column used by a view or rule"),
12718 errdetail("%s depends on column \"%s\"",
12719 getObjectDescription(&foundObject, false),
12720 colName)));
12721 break;
12723 case OCLASS_TRIGGER:
12726 * A trigger can depend on a column because the column is
12727 * specified as an update target, or because the column is
12728 * used in the trigger's WHEN condition. The first case would
12729 * not require any extra work, but the second case would
12730 * require updating the WHEN expression, which will take a
12731 * significant amount of new code. Since we can't easily tell
12732 * which case applies, we punt for both. FIXME someday.
12734 ereport(ERROR,
12735 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12736 errmsg("cannot alter type of a column used in a trigger definition"),
12737 errdetail("%s depends on column \"%s\"",
12738 getObjectDescription(&foundObject, false),
12739 colName)));
12740 break;
12742 case OCLASS_POLICY:
12745 * A policy can depend on a column because the column is
12746 * specified in the policy's USING or WITH CHECK qual
12747 * expressions. It might be possible to rewrite and recheck
12748 * the policy expression, but punt for now. It's certainly
12749 * easy enough to remove and recreate the policy; still, FIXME
12750 * someday.
12752 ereport(ERROR,
12753 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12754 errmsg("cannot alter type of a column used in a policy definition"),
12755 errdetail("%s depends on column \"%s\"",
12756 getObjectDescription(&foundObject, false),
12757 colName)));
12758 break;
12760 case OCLASS_DEFAULT:
12762 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
12764 if (col.objectId == RelationGetRelid(rel) &&
12765 col.objectSubId == attnum)
12768 * Ignore the column's own default expression, which
12769 * we will deal with below.
12771 Assert(defaultexpr);
12773 else
12776 * This must be a reference from the expression of a
12777 * generated column elsewhere in the same table.
12778 * Changing the type of a column that is used by a
12779 * generated column is not allowed by SQL standard, so
12780 * just punt for now. It might be doable with some
12781 * thinking and effort.
12783 ereport(ERROR,
12784 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12785 errmsg("cannot alter type of a column used by a generated column"),
12786 errdetail("Column \"%s\" is used by generated column \"%s\".",
12787 colName,
12788 get_attname(col.objectId,
12789 col.objectSubId,
12790 false))));
12792 break;
12795 case OCLASS_STATISTIC_EXT:
12798 * Give the extended-stats machinery a chance to fix anything
12799 * that this column type change would break.
12801 RememberStatisticsForRebuilding(foundObject.objectId, tab);
12802 break;
12804 case OCLASS_PROC:
12805 case OCLASS_TYPE:
12806 case OCLASS_CAST:
12807 case OCLASS_COLLATION:
12808 case OCLASS_CONVERSION:
12809 case OCLASS_LANGUAGE:
12810 case OCLASS_LARGEOBJECT:
12811 case OCLASS_OPERATOR:
12812 case OCLASS_OPCLASS:
12813 case OCLASS_OPFAMILY:
12814 case OCLASS_AM:
12815 case OCLASS_AMOP:
12816 case OCLASS_AMPROC:
12817 case OCLASS_SCHEMA:
12818 case OCLASS_TSPARSER:
12819 case OCLASS_TSDICT:
12820 case OCLASS_TSTEMPLATE:
12821 case OCLASS_TSCONFIG:
12822 case OCLASS_ROLE:
12823 case OCLASS_ROLE_MEMBERSHIP:
12824 case OCLASS_DATABASE:
12825 case OCLASS_TBLSPACE:
12826 case OCLASS_FDW:
12827 case OCLASS_FOREIGN_SERVER:
12828 case OCLASS_USER_MAPPING:
12829 case OCLASS_DEFACL:
12830 case OCLASS_EXTENSION:
12831 case OCLASS_EVENT_TRIGGER:
12832 case OCLASS_PARAMETER_ACL:
12833 case OCLASS_PUBLICATION:
12834 case OCLASS_PUBLICATION_NAMESPACE:
12835 case OCLASS_PUBLICATION_REL:
12836 case OCLASS_SUBSCRIPTION:
12837 case OCLASS_TRANSFORM:
12840 * We don't expect any of these sorts of objects to depend on
12841 * a column.
12843 elog(ERROR, "unexpected object depending on column: %s",
12844 getObjectDescription(&foundObject, false));
12845 break;
12848 * There's intentionally no default: case here; we want the
12849 * compiler to warn if a new OCLASS hasn't been handled above.
12854 systable_endscan(scan);
12857 * Now scan for dependencies of this column on other things. The only
12858 * things we should find are the dependency on the column datatype and
12859 * possibly a collation dependency. Those can be removed.
12861 ScanKeyInit(&key[0],
12862 Anum_pg_depend_classid,
12863 BTEqualStrategyNumber, F_OIDEQ,
12864 ObjectIdGetDatum(RelationRelationId));
12865 ScanKeyInit(&key[1],
12866 Anum_pg_depend_objid,
12867 BTEqualStrategyNumber, F_OIDEQ,
12868 ObjectIdGetDatum(RelationGetRelid(rel)));
12869 ScanKeyInit(&key[2],
12870 Anum_pg_depend_objsubid,
12871 BTEqualStrategyNumber, F_INT4EQ,
12872 Int32GetDatum((int32) attnum));
12874 scan = systable_beginscan(depRel, DependDependerIndexId, true,
12875 NULL, 3, key);
12877 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
12879 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
12880 ObjectAddress foundObject;
12882 foundObject.classId = foundDep->refclassid;
12883 foundObject.objectId = foundDep->refobjid;
12884 foundObject.objectSubId = foundDep->refobjsubid;
12886 if (foundDep->deptype != DEPENDENCY_NORMAL)
12887 elog(ERROR, "found unexpected dependency type '%c'",
12888 foundDep->deptype);
12889 if (!(foundDep->refclassid == TypeRelationId &&
12890 foundDep->refobjid == attTup->atttypid) &&
12891 !(foundDep->refclassid == CollationRelationId &&
12892 foundDep->refobjid == attTup->attcollation))
12893 elog(ERROR, "found unexpected dependency for column: %s",
12894 getObjectDescription(&foundObject, false));
12896 CatalogTupleDelete(depRel, &depTup->t_self);
12899 systable_endscan(scan);
12901 table_close(depRel, RowExclusiveLock);
12904 * Here we go --- change the recorded column type and collation. (Note
12905 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
12906 * fix up the missing value if any.
12908 if (attTup->atthasmissing)
12910 Datum missingval;
12911 bool missingNull;
12913 /* if rewrite is true the missing value should already be cleared */
12914 Assert(tab->rewrite == 0);
12916 /* Get the missing value datum */
12917 missingval = heap_getattr(heapTup,
12918 Anum_pg_attribute_attmissingval,
12919 attrelation->rd_att,
12920 &missingNull);
12922 /* if it's a null array there is nothing to do */
12924 if (!missingNull)
12927 * Get the datum out of the array and repack it in a new array
12928 * built with the new type data. We assume that since the table
12929 * doesn't need rewriting, the actual Datum doesn't need to be
12930 * changed, only the array metadata.
12933 int one = 1;
12934 bool isNull;
12935 Datum valuesAtt[Natts_pg_attribute] = {0};
12936 bool nullsAtt[Natts_pg_attribute] = {0};
12937 bool replacesAtt[Natts_pg_attribute] = {0};
12938 HeapTuple newTup;
12940 missingval = array_get_element(missingval,
12942 &one,
12944 attTup->attlen,
12945 attTup->attbyval,
12946 attTup->attalign,
12947 &isNull);
12948 missingval = PointerGetDatum(construct_array(&missingval,
12950 targettype,
12951 tform->typlen,
12952 tform->typbyval,
12953 tform->typalign));
12955 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
12956 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
12957 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
12959 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
12960 valuesAtt, nullsAtt, replacesAtt);
12961 heap_freetuple(heapTup);
12962 heapTup = newTup;
12963 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
12967 attTup->atttypid = targettype;
12968 attTup->atttypmod = targettypmod;
12969 attTup->attcollation = targetcollid;
12970 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
12971 ereport(ERROR,
12972 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
12973 errmsg("too many array dimensions"));
12974 attTup->attndims = list_length(typeName->arrayBounds);
12975 attTup->attlen = tform->typlen;
12976 attTup->attbyval = tform->typbyval;
12977 attTup->attalign = tform->typalign;
12978 attTup->attstorage = tform->typstorage;
12979 attTup->attcompression = InvalidCompressionMethod;
12981 ReleaseSysCache(typeTuple);
12983 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
12985 table_close(attrelation, RowExclusiveLock);
12987 /* Install dependencies on new datatype and collation */
12988 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
12989 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
12992 * Drop any pg_statistic entry for the column, since it's now wrong type
12994 RemoveStatistics(RelationGetRelid(rel), attnum);
12996 InvokeObjectPostAlterHook(RelationRelationId,
12997 RelationGetRelid(rel), attnum);
13000 * Update the default, if present, by brute force --- remove and re-add
13001 * the default. Probably unsafe to take shortcuts, since the new version
13002 * may well have additional dependencies. (It's okay to do this now,
13003 * rather than after other ALTER TYPE commands, since the default won't
13004 * depend on other column types.)
13006 if (defaultexpr)
13009 * If it's a GENERATED default, drop its dependency records, in
13010 * particular its INTERNAL dependency on the column, which would
13011 * otherwise cause dependency.c to refuse to perform the deletion.
13013 if (attTup->attgenerated)
13015 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13017 if (!OidIsValid(attrdefoid))
13018 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13019 RelationGetRelid(rel), attnum);
13020 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13024 * Make updates-so-far visible, particularly the new pg_attribute row
13025 * which will be updated again.
13027 CommandCounterIncrement();
13030 * We use RESTRICT here for safety, but at present we do not expect
13031 * anything to depend on the default.
13033 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13034 true);
13036 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13039 ObjectAddressSubSet(address, RelationRelationId,
13040 RelationGetRelid(rel), attnum);
13042 /* Cleanup */
13043 heap_freetuple(heapTup);
13045 return address;
13049 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13050 * needs to be reset.
13052 static void
13053 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
13055 if (!get_index_isreplident(indoid))
13056 return;
13058 if (tab->replicaIdentityIndex)
13059 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
13061 tab->replicaIdentityIndex = get_rel_name(indoid);
13065 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13067 static void
13068 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
13070 if (!get_index_isclustered(indoid))
13071 return;
13073 if (tab->clusterOnIndex)
13074 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13076 tab->clusterOnIndex = get_rel_name(indoid);
13080 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13081 * to be rebuilt (which we might already know).
13083 static void
13084 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
13087 * This de-duplication check is critical for two independent reasons: we
13088 * mustn't try to recreate the same constraint twice, and if a constraint
13089 * depends on more than one column whose type is to be altered, we must
13090 * capture its definition string before applying any of the column type
13091 * changes. ruleutils.c will get confused if we ask again later.
13093 if (!list_member_oid(tab->changedConstraintOids, conoid))
13095 /* OK, capture the constraint's existing definition string */
13096 char *defstring = pg_get_constraintdef_command(conoid);
13097 Oid indoid;
13099 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
13100 conoid);
13101 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
13102 defstring);
13105 * For the index of a constraint, if any, remember if it is used for
13106 * the table's replica identity or if it is a clustered index, so that
13107 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13108 * those properties.
13110 indoid = get_constraint_index(conoid);
13111 if (OidIsValid(indoid))
13113 RememberReplicaIdentityForRebuilding(indoid, tab);
13114 RememberClusterOnForRebuilding(indoid, tab);
13120 * Subroutine for ATExecAlterColumnType: remember that an index needs
13121 * to be rebuilt (which we might already know).
13123 static void
13124 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
13127 * This de-duplication check is critical for two independent reasons: we
13128 * mustn't try to recreate the same index twice, and if an index depends
13129 * on more than one column whose type is to be altered, we must capture
13130 * its definition string before applying any of the column type changes.
13131 * ruleutils.c will get confused if we ask again later.
13133 if (!list_member_oid(tab->changedIndexOids, indoid))
13136 * Before adding it as an index-to-rebuild, we'd better see if it
13137 * belongs to a constraint, and if so rebuild the constraint instead.
13138 * Typically this check fails, because constraint indexes normally
13139 * have only dependencies on their constraint. But it's possible for
13140 * such an index to also have direct dependencies on table columns,
13141 * for example with a partial exclusion constraint.
13143 Oid conoid = get_index_constraint(indoid);
13145 if (OidIsValid(conoid))
13147 RememberConstraintForRebuilding(conoid, tab);
13149 else
13151 /* OK, capture the index's existing definition string */
13152 char *defstring = pg_get_indexdef_string(indoid);
13154 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
13155 indoid);
13156 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
13157 defstring);
13160 * Remember if this index is used for the table's replica identity
13161 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13162 * can queue up commands necessary to restore those properties.
13164 RememberReplicaIdentityForRebuilding(indoid, tab);
13165 RememberClusterOnForRebuilding(indoid, tab);
13171 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13172 * needs to be rebuilt (which we might already know).
13174 static void
13175 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13178 * This de-duplication check is critical for two independent reasons: we
13179 * mustn't try to recreate the same statistics object twice, and if the
13180 * statistics object depends on more than one column whose type is to be
13181 * altered, we must capture its definition string before applying any of
13182 * the type changes. ruleutils.c will get confused if we ask again later.
13184 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
13186 /* OK, capture the statistics object's existing definition string */
13187 char *defstring = pg_get_statisticsobjdef_string(stxoid);
13189 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
13190 stxoid);
13191 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
13192 defstring);
13197 * Cleanup after we've finished all the ALTER TYPE operations for a
13198 * particular relation. We have to drop and recreate all the indexes
13199 * and constraints that depend on the altered columns. We do the
13200 * actual dropping here, but re-creation is managed by adding work
13201 * queue entries to do those steps later.
13203 static void
13204 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
13206 ObjectAddress obj;
13207 ObjectAddresses *objects;
13208 ListCell *def_item;
13209 ListCell *oid_item;
13212 * Collect all the constraints and indexes to drop so we can process them
13213 * in a single call. That way we don't have to worry about dependencies
13214 * among them.
13216 objects = new_object_addresses();
13219 * Re-parse the index and constraint definitions, and attach them to the
13220 * appropriate work queue entries. We do this before dropping because in
13221 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13222 * lock on the table the constraint is attached to, and we need to get
13223 * that before reparsing/dropping.
13225 * We can't rely on the output of deparsing to tell us which relation to
13226 * operate on, because concurrent activity might have made the name
13227 * resolve differently. Instead, we've got to use the OID of the
13228 * constraint or index we're processing to figure out which relation to
13229 * operate on.
13231 forboth(oid_item, tab->changedConstraintOids,
13232 def_item, tab->changedConstraintDefs)
13234 Oid oldId = lfirst_oid(oid_item);
13235 HeapTuple tup;
13236 Form_pg_constraint con;
13237 Oid relid;
13238 Oid confrelid;
13239 char contype;
13240 bool conislocal;
13242 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13243 if (!HeapTupleIsValid(tup)) /* should not happen */
13244 elog(ERROR, "cache lookup failed for constraint %u", oldId);
13245 con = (Form_pg_constraint) GETSTRUCT(tup);
13246 if (OidIsValid(con->conrelid))
13247 relid = con->conrelid;
13248 else
13250 /* must be a domain constraint */
13251 relid = get_typ_typrelid(getBaseType(con->contypid));
13252 if (!OidIsValid(relid))
13253 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13255 confrelid = con->confrelid;
13256 contype = con->contype;
13257 conislocal = con->conislocal;
13258 ReleaseSysCache(tup);
13260 ObjectAddressSet(obj, ConstraintRelationId, oldId);
13261 add_exact_object_address(&obj, objects);
13264 * If the constraint is inherited (only), we don't want to inject a
13265 * new definition here; it'll get recreated when ATAddCheckConstraint
13266 * recurses from adding the parent table's constraint. But we had to
13267 * carry the info this far so that we can drop the constraint below.
13269 if (!conislocal)
13270 continue;
13273 * When rebuilding an FK constraint that references the table we're
13274 * modifying, we might not yet have any lock on the FK's table, so get
13275 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13276 * step, so there's no value in asking for anything weaker.
13278 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
13279 LockRelationOid(relid, AccessExclusiveLock);
13281 ATPostAlterTypeParse(oldId, relid, confrelid,
13282 (char *) lfirst(def_item),
13283 wqueue, lockmode, tab->rewrite);
13285 forboth(oid_item, tab->changedIndexOids,
13286 def_item, tab->changedIndexDefs)
13288 Oid oldId = lfirst_oid(oid_item);
13289 Oid relid;
13291 relid = IndexGetRelation(oldId, false);
13292 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13293 (char *) lfirst(def_item),
13294 wqueue, lockmode, tab->rewrite);
13296 ObjectAddressSet(obj, RelationRelationId, oldId);
13297 add_exact_object_address(&obj, objects);
13300 /* add dependencies for new statistics */
13301 forboth(oid_item, tab->changedStatisticsOids,
13302 def_item, tab->changedStatisticsDefs)
13304 Oid oldId = lfirst_oid(oid_item);
13305 Oid relid;
13307 relid = StatisticsGetRelation(oldId, false);
13308 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13309 (char *) lfirst(def_item),
13310 wqueue, lockmode, tab->rewrite);
13312 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
13313 add_exact_object_address(&obj, objects);
13317 * Queue up command to restore replica identity index marking
13319 if (tab->replicaIdentityIndex)
13321 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13322 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
13324 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13325 subcmd->name = tab->replicaIdentityIndex;
13326 cmd->subtype = AT_ReplicaIdentity;
13327 cmd->def = (Node *) subcmd;
13329 /* do it after indexes and constraints */
13330 tab->subcmds[AT_PASS_OLD_CONSTR] =
13331 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13335 * Queue up command to restore marking of index used for cluster.
13337 if (tab->clusterOnIndex)
13339 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13341 cmd->subtype = AT_ClusterOn;
13342 cmd->name = tab->clusterOnIndex;
13344 /* do it after indexes and constraints */
13345 tab->subcmds[AT_PASS_OLD_CONSTR] =
13346 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13350 * It should be okay to use DROP_RESTRICT here, since nothing else should
13351 * be depending on these objects.
13353 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
13355 free_object_addresses(objects);
13358 * The objects will get recreated during subsequent passes over the work
13359 * queue.
13364 * Parse the previously-saved definition string for a constraint, index or
13365 * statistics object against the newly-established column data type(s), and
13366 * queue up the resulting command parsetrees for execution.
13368 * This might fail if, for example, you have a WHERE clause that uses an
13369 * operator that's not available for the new column type.
13371 static void
13372 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
13373 List **wqueue, LOCKMODE lockmode, bool rewrite)
13375 List *raw_parsetree_list;
13376 List *querytree_list;
13377 ListCell *list_item;
13378 Relation rel;
13381 * We expect that we will get only ALTER TABLE and CREATE INDEX
13382 * statements. Hence, there is no need to pass them through
13383 * parse_analyze_*() or the rewriter, but instead we need to pass them
13384 * through parse_utilcmd.c to make them ready for execution.
13386 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
13387 querytree_list = NIL;
13388 foreach(list_item, raw_parsetree_list)
13390 RawStmt *rs = lfirst_node(RawStmt, list_item);
13391 Node *stmt = rs->stmt;
13393 if (IsA(stmt, IndexStmt))
13394 querytree_list = lappend(querytree_list,
13395 transformIndexStmt(oldRelId,
13396 (IndexStmt *) stmt,
13397 cmd));
13398 else if (IsA(stmt, AlterTableStmt))
13400 List *beforeStmts;
13401 List *afterStmts;
13403 stmt = (Node *) transformAlterTableStmt(oldRelId,
13404 (AlterTableStmt *) stmt,
13405 cmd,
13406 &beforeStmts,
13407 &afterStmts);
13408 querytree_list = list_concat(querytree_list, beforeStmts);
13409 querytree_list = lappend(querytree_list, stmt);
13410 querytree_list = list_concat(querytree_list, afterStmts);
13412 else if (IsA(stmt, CreateStatsStmt))
13413 querytree_list = lappend(querytree_list,
13414 transformStatsStmt(oldRelId,
13415 (CreateStatsStmt *) stmt,
13416 cmd));
13417 else
13418 querytree_list = lappend(querytree_list, stmt);
13421 /* Caller should already have acquired whatever lock we need. */
13422 rel = relation_open(oldRelId, NoLock);
13425 * Attach each generated command to the proper place in the work queue.
13426 * Note this could result in creation of entirely new work-queue entries.
13428 * Also note that we have to tweak the command subtypes, because it turns
13429 * out that re-creation of indexes and constraints has to act a bit
13430 * differently from initial creation.
13432 foreach(list_item, querytree_list)
13434 Node *stm = (Node *) lfirst(list_item);
13435 AlteredTableInfo *tab;
13437 tab = ATGetQueueEntry(wqueue, rel);
13439 if (IsA(stm, IndexStmt))
13441 IndexStmt *stmt = (IndexStmt *) stm;
13442 AlterTableCmd *newcmd;
13444 if (!rewrite)
13445 TryReuseIndex(oldId, stmt);
13446 stmt->reset_default_tblspc = true;
13447 /* keep the index's comment */
13448 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
13450 newcmd = makeNode(AlterTableCmd);
13451 newcmd->subtype = AT_ReAddIndex;
13452 newcmd->def = (Node *) stmt;
13453 tab->subcmds[AT_PASS_OLD_INDEX] =
13454 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
13456 else if (IsA(stm, AlterTableStmt))
13458 AlterTableStmt *stmt = (AlterTableStmt *) stm;
13459 ListCell *lcmd;
13461 foreach(lcmd, stmt->cmds)
13463 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
13465 if (cmd->subtype == AT_AddIndex)
13467 IndexStmt *indstmt;
13468 Oid indoid;
13470 indstmt = castNode(IndexStmt, cmd->def);
13471 indoid = get_constraint_index(oldId);
13473 if (!rewrite)
13474 TryReuseIndex(indoid, indstmt);
13475 /* keep any comment on the index */
13476 indstmt->idxcomment = GetComment(indoid,
13477 RelationRelationId, 0);
13478 indstmt->reset_default_tblspc = true;
13480 cmd->subtype = AT_ReAddIndex;
13481 tab->subcmds[AT_PASS_OLD_INDEX] =
13482 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
13484 /* recreate any comment on the constraint */
13485 RebuildConstraintComment(tab,
13486 AT_PASS_OLD_INDEX,
13487 oldId,
13488 rel,
13489 NIL,
13490 indstmt->idxname);
13492 else if (cmd->subtype == AT_AddConstraint)
13494 Constraint *con = castNode(Constraint, cmd->def);
13496 con->old_pktable_oid = refRelId;
13497 /* rewriting neither side of a FK */
13498 if (con->contype == CONSTR_FOREIGN &&
13499 !rewrite && tab->rewrite == 0)
13500 TryReuseForeignKey(oldId, con);
13501 con->reset_default_tblspc = true;
13502 cmd->subtype = AT_ReAddConstraint;
13503 tab->subcmds[AT_PASS_OLD_CONSTR] =
13504 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13506 /* recreate any comment on the constraint */
13507 RebuildConstraintComment(tab,
13508 AT_PASS_OLD_CONSTR,
13509 oldId,
13510 rel,
13511 NIL,
13512 con->conname);
13514 else if (cmd->subtype == AT_SetNotNull)
13517 * The parser will create AT_SetNotNull subcommands for
13518 * columns of PRIMARY KEY indexes/constraints, but we need
13519 * not do anything with them here, because the columns'
13520 * NOT NULL marks will already have been propagated into
13521 * the new table definition.
13524 else
13525 elog(ERROR, "unexpected statement subtype: %d",
13526 (int) cmd->subtype);
13529 else if (IsA(stm, AlterDomainStmt))
13531 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
13533 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
13535 Constraint *con = castNode(Constraint, stmt->def);
13536 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13538 cmd->subtype = AT_ReAddDomainConstraint;
13539 cmd->def = (Node *) stmt;
13540 tab->subcmds[AT_PASS_OLD_CONSTR] =
13541 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13543 /* recreate any comment on the constraint */
13544 RebuildConstraintComment(tab,
13545 AT_PASS_OLD_CONSTR,
13546 oldId,
13547 NULL,
13548 stmt->typeName,
13549 con->conname);
13551 else
13552 elog(ERROR, "unexpected statement subtype: %d",
13553 (int) stmt->subtype);
13555 else if (IsA(stm, CreateStatsStmt))
13557 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
13558 AlterTableCmd *newcmd;
13560 /* keep the statistics object's comment */
13561 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
13563 newcmd = makeNode(AlterTableCmd);
13564 newcmd->subtype = AT_ReAddStatistics;
13565 newcmd->def = (Node *) stmt;
13566 tab->subcmds[AT_PASS_MISC] =
13567 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
13569 else
13570 elog(ERROR, "unexpected statement type: %d",
13571 (int) nodeTag(stm));
13574 relation_close(rel, NoLock);
13578 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
13579 * for a table or domain constraint that is being rebuilt.
13581 * objid is the OID of the constraint.
13582 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
13583 * as a string list) for a domain constraint.
13584 * (We could dig that info, as well as the conname, out of the pg_constraint
13585 * entry; but callers already have them so might as well pass them.)
13587 static void
13588 RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
13589 Relation rel, List *domname,
13590 const char *conname)
13592 CommentStmt *cmd;
13593 char *comment_str;
13594 AlterTableCmd *newcmd;
13596 /* Look for comment for object wanted, and leave if none */
13597 comment_str = GetComment(objid, ConstraintRelationId, 0);
13598 if (comment_str == NULL)
13599 return;
13601 /* Build CommentStmt node, copying all input data for safety */
13602 cmd = makeNode(CommentStmt);
13603 if (rel)
13605 cmd->objtype = OBJECT_TABCONSTRAINT;
13606 cmd->object = (Node *)
13607 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
13608 makeString(pstrdup(RelationGetRelationName(rel))),
13609 makeString(pstrdup(conname)));
13611 else
13613 cmd->objtype = OBJECT_DOMCONSTRAINT;
13614 cmd->object = (Node *)
13615 list_make2(makeTypeNameFromNameList(copyObject(domname)),
13616 makeString(pstrdup(conname)));
13618 cmd->comment = comment_str;
13620 /* Append it to list of commands */
13621 newcmd = makeNode(AlterTableCmd);
13622 newcmd->subtype = AT_ReAddComment;
13623 newcmd->def = (Node *) cmd;
13624 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
13628 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
13629 * for the real analysis, then mutates the IndexStmt based on that verdict.
13631 static void
13632 TryReuseIndex(Oid oldId, IndexStmt *stmt)
13634 if (CheckIndexCompatible(oldId,
13635 stmt->accessMethod,
13636 stmt->indexParams,
13637 stmt->excludeOpNames))
13639 Relation irel = index_open(oldId, NoLock);
13641 /* If it's a partitioned index, there is no storage to share. */
13642 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
13644 stmt->oldNumber = irel->rd_locator.relNumber;
13645 stmt->oldCreateSubid = irel->rd_createSubid;
13646 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
13648 index_close(irel, NoLock);
13653 * Subroutine for ATPostAlterTypeParse().
13655 * Stash the old P-F equality operator into the Constraint node, for possible
13656 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
13657 * this constraint can be skipped.
13659 static void
13660 TryReuseForeignKey(Oid oldId, Constraint *con)
13662 HeapTuple tup;
13663 Datum adatum;
13664 ArrayType *arr;
13665 Oid *rawarr;
13666 int numkeys;
13667 int i;
13669 Assert(con->contype == CONSTR_FOREIGN);
13670 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
13672 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13673 if (!HeapTupleIsValid(tup)) /* should not happen */
13674 elog(ERROR, "cache lookup failed for constraint %u", oldId);
13676 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
13677 Anum_pg_constraint_conpfeqop);
13678 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
13679 numkeys = ARR_DIMS(arr)[0];
13680 /* test follows the one in ri_FetchConstraintInfo() */
13681 if (ARR_NDIM(arr) != 1 ||
13682 ARR_HASNULL(arr) ||
13683 ARR_ELEMTYPE(arr) != OIDOID)
13684 elog(ERROR, "conpfeqop is not a 1-D Oid array");
13685 rawarr = (Oid *) ARR_DATA_PTR(arr);
13687 /* stash a List of the operator Oids in our Constraint node */
13688 for (i = 0; i < numkeys; i++)
13689 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
13691 ReleaseSysCache(tup);
13695 * ALTER COLUMN .. OPTIONS ( ... )
13697 * Returns the address of the modified column
13699 static ObjectAddress
13700 ATExecAlterColumnGenericOptions(Relation rel,
13701 const char *colName,
13702 List *options,
13703 LOCKMODE lockmode)
13705 Relation ftrel;
13706 Relation attrel;
13707 ForeignServer *server;
13708 ForeignDataWrapper *fdw;
13709 HeapTuple tuple;
13710 HeapTuple newtuple;
13711 bool isnull;
13712 Datum repl_val[Natts_pg_attribute];
13713 bool repl_null[Natts_pg_attribute];
13714 bool repl_repl[Natts_pg_attribute];
13715 Datum datum;
13716 Form_pg_foreign_table fttableform;
13717 Form_pg_attribute atttableform;
13718 AttrNumber attnum;
13719 ObjectAddress address;
13721 if (options == NIL)
13722 return InvalidObjectAddress;
13724 /* First, determine FDW validator associated to the foreign table. */
13725 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
13726 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
13727 if (!HeapTupleIsValid(tuple))
13728 ereport(ERROR,
13729 (errcode(ERRCODE_UNDEFINED_OBJECT),
13730 errmsg("foreign table \"%s\" does not exist",
13731 RelationGetRelationName(rel))));
13732 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
13733 server = GetForeignServer(fttableform->ftserver);
13734 fdw = GetForeignDataWrapper(server->fdwid);
13736 table_close(ftrel, AccessShareLock);
13737 ReleaseSysCache(tuple);
13739 attrel = table_open(AttributeRelationId, RowExclusiveLock);
13740 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
13741 if (!HeapTupleIsValid(tuple))
13742 ereport(ERROR,
13743 (errcode(ERRCODE_UNDEFINED_COLUMN),
13744 errmsg("column \"%s\" of relation \"%s\" does not exist",
13745 colName, RelationGetRelationName(rel))));
13747 /* Prevent them from altering a system attribute */
13748 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
13749 attnum = atttableform->attnum;
13750 if (attnum <= 0)
13751 ereport(ERROR,
13752 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13753 errmsg("cannot alter system column \"%s\"", colName)));
13756 /* Initialize buffers for new tuple values */
13757 memset(repl_val, 0, sizeof(repl_val));
13758 memset(repl_null, false, sizeof(repl_null));
13759 memset(repl_repl, false, sizeof(repl_repl));
13761 /* Extract the current options */
13762 datum = SysCacheGetAttr(ATTNAME,
13763 tuple,
13764 Anum_pg_attribute_attfdwoptions,
13765 &isnull);
13766 if (isnull)
13767 datum = PointerGetDatum(NULL);
13769 /* Transform the options */
13770 datum = transformGenericOptions(AttributeRelationId,
13771 datum,
13772 options,
13773 fdw->fdwvalidator);
13775 if (PointerIsValid(DatumGetPointer(datum)))
13776 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
13777 else
13778 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
13780 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
13782 /* Everything looks good - update the tuple */
13784 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
13785 repl_val, repl_null, repl_repl);
13787 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
13789 InvokeObjectPostAlterHook(RelationRelationId,
13790 RelationGetRelid(rel),
13791 atttableform->attnum);
13792 ObjectAddressSubSet(address, RelationRelationId,
13793 RelationGetRelid(rel), attnum);
13795 ReleaseSysCache(tuple);
13797 table_close(attrel, RowExclusiveLock);
13799 heap_freetuple(newtuple);
13801 return address;
13805 * ALTER TABLE OWNER
13807 * recursing is true if we are recursing from a table to its indexes,
13808 * sequences, or toast table. We don't allow the ownership of those things to
13809 * be changed separately from the parent table. Also, we can skip permission
13810 * checks (this is necessary not just an optimization, else we'd fail to
13811 * handle toast tables properly).
13813 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
13814 * free-standing composite type.
13816 void
13817 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
13819 Relation target_rel;
13820 Relation class_rel;
13821 HeapTuple tuple;
13822 Form_pg_class tuple_class;
13825 * Get exclusive lock till end of transaction on the target table. Use
13826 * relation_open so that we can work on indexes and sequences.
13828 target_rel = relation_open(relationOid, lockmode);
13830 /* Get its pg_class tuple, too */
13831 class_rel = table_open(RelationRelationId, RowExclusiveLock);
13833 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
13834 if (!HeapTupleIsValid(tuple))
13835 elog(ERROR, "cache lookup failed for relation %u", relationOid);
13836 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
13838 /* Can we change the ownership of this tuple? */
13839 switch (tuple_class->relkind)
13841 case RELKIND_RELATION:
13842 case RELKIND_VIEW:
13843 case RELKIND_MATVIEW:
13844 case RELKIND_FOREIGN_TABLE:
13845 case RELKIND_PARTITIONED_TABLE:
13846 /* ok to change owner */
13847 break;
13848 case RELKIND_INDEX:
13849 if (!recursing)
13852 * Because ALTER INDEX OWNER used to be allowed, and in fact
13853 * is generated by old versions of pg_dump, we give a warning
13854 * and do nothing rather than erroring out. Also, to avoid
13855 * unnecessary chatter while restoring those old dumps, say
13856 * nothing at all if the command would be a no-op anyway.
13858 if (tuple_class->relowner != newOwnerId)
13859 ereport(WARNING,
13860 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13861 errmsg("cannot change owner of index \"%s\"",
13862 NameStr(tuple_class->relname)),
13863 errhint("Change the ownership of the index's table instead.")));
13864 /* quick hack to exit via the no-op path */
13865 newOwnerId = tuple_class->relowner;
13867 break;
13868 case RELKIND_PARTITIONED_INDEX:
13869 if (recursing)
13870 break;
13871 ereport(ERROR,
13872 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13873 errmsg("cannot change owner of index \"%s\"",
13874 NameStr(tuple_class->relname)),
13875 errhint("Change the ownership of the index's table instead.")));
13876 break;
13877 case RELKIND_SEQUENCE:
13878 if (!recursing &&
13879 tuple_class->relowner != newOwnerId)
13881 /* if it's an owned sequence, disallow changing it by itself */
13882 Oid tableId;
13883 int32 colId;
13885 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
13886 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
13887 ereport(ERROR,
13888 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13889 errmsg("cannot change owner of sequence \"%s\"",
13890 NameStr(tuple_class->relname)),
13891 errdetail("Sequence \"%s\" is linked to table \"%s\".",
13892 NameStr(tuple_class->relname),
13893 get_rel_name(tableId))));
13895 break;
13896 case RELKIND_COMPOSITE_TYPE:
13897 if (recursing)
13898 break;
13899 ereport(ERROR,
13900 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13901 errmsg("\"%s\" is a composite type",
13902 NameStr(tuple_class->relname)),
13903 errhint("Use ALTER TYPE instead.")));
13904 break;
13905 case RELKIND_TOASTVALUE:
13906 if (recursing)
13907 break;
13908 /* FALL THRU */
13909 default:
13910 ereport(ERROR,
13911 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
13912 errmsg("cannot change owner of relation \"%s\"",
13913 NameStr(tuple_class->relname)),
13914 errdetail_relkind_not_supported(tuple_class->relkind)));
13918 * If the new owner is the same as the existing owner, consider the
13919 * command to have succeeded. This is for dump restoration purposes.
13921 if (tuple_class->relowner != newOwnerId)
13923 Datum repl_val[Natts_pg_class];
13924 bool repl_null[Natts_pg_class];
13925 bool repl_repl[Natts_pg_class];
13926 Acl *newAcl;
13927 Datum aclDatum;
13928 bool isNull;
13929 HeapTuple newtuple;
13931 /* skip permission checks when recursing to index or toast table */
13932 if (!recursing)
13934 /* Superusers can always do it */
13935 if (!superuser())
13937 Oid namespaceOid = tuple_class->relnamespace;
13938 AclResult aclresult;
13940 /* Otherwise, must be owner of the existing object */
13941 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
13942 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
13943 RelationGetRelationName(target_rel));
13945 /* Must be able to become new owner */
13946 check_can_set_role(GetUserId(), newOwnerId);
13948 /* New owner must have CREATE privilege on namespace */
13949 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
13950 ACL_CREATE);
13951 if (aclresult != ACLCHECK_OK)
13952 aclcheck_error(aclresult, OBJECT_SCHEMA,
13953 get_namespace_name(namespaceOid));
13957 memset(repl_null, false, sizeof(repl_null));
13958 memset(repl_repl, false, sizeof(repl_repl));
13960 repl_repl[Anum_pg_class_relowner - 1] = true;
13961 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
13964 * Determine the modified ACL for the new owner. This is only
13965 * necessary when the ACL is non-null.
13967 aclDatum = SysCacheGetAttr(RELOID, tuple,
13968 Anum_pg_class_relacl,
13969 &isNull);
13970 if (!isNull)
13972 newAcl = aclnewowner(DatumGetAclP(aclDatum),
13973 tuple_class->relowner, newOwnerId);
13974 repl_repl[Anum_pg_class_relacl - 1] = true;
13975 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
13978 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
13980 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
13982 heap_freetuple(newtuple);
13985 * We must similarly update any per-column ACLs to reflect the new
13986 * owner; for neatness reasons that's split out as a subroutine.
13988 change_owner_fix_column_acls(relationOid,
13989 tuple_class->relowner,
13990 newOwnerId);
13993 * Update owner dependency reference, if any. A composite type has
13994 * none, because it's tracked for the pg_type entry instead of here;
13995 * indexes and TOAST tables don't have their own entries either.
13997 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
13998 tuple_class->relkind != RELKIND_INDEX &&
13999 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
14000 tuple_class->relkind != RELKIND_TOASTVALUE)
14001 changeDependencyOnOwner(RelationRelationId, relationOid,
14002 newOwnerId);
14005 * Also change the ownership of the table's row type, if it has one
14007 if (OidIsValid(tuple_class->reltype))
14008 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
14011 * If we are operating on a table or materialized view, also change
14012 * the ownership of any indexes and sequences that belong to the
14013 * relation, as well as its toast table (if it has one).
14015 if (tuple_class->relkind == RELKIND_RELATION ||
14016 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
14017 tuple_class->relkind == RELKIND_MATVIEW ||
14018 tuple_class->relkind == RELKIND_TOASTVALUE)
14020 List *index_oid_list;
14021 ListCell *i;
14023 /* Find all the indexes belonging to this relation */
14024 index_oid_list = RelationGetIndexList(target_rel);
14026 /* For each index, recursively change its ownership */
14027 foreach(i, index_oid_list)
14028 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14030 list_free(index_oid_list);
14033 /* If it has a toast table, recurse to change its ownership */
14034 if (tuple_class->reltoastrelid != InvalidOid)
14035 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14036 true, lockmode);
14038 /* If it has dependent sequences, recurse to change them too */
14039 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
14042 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14044 ReleaseSysCache(tuple);
14045 table_close(class_rel, RowExclusiveLock);
14046 relation_close(target_rel, NoLock);
14050 * change_owner_fix_column_acls
14052 * Helper function for ATExecChangeOwner. Scan the columns of the table
14053 * and fix any non-null column ACLs to reflect the new owner.
14055 static void
14056 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
14058 Relation attRelation;
14059 SysScanDesc scan;
14060 ScanKeyData key[1];
14061 HeapTuple attributeTuple;
14063 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
14064 ScanKeyInit(&key[0],
14065 Anum_pg_attribute_attrelid,
14066 BTEqualStrategyNumber, F_OIDEQ,
14067 ObjectIdGetDatum(relationOid));
14068 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
14069 true, NULL, 1, key);
14070 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14072 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14073 Datum repl_val[Natts_pg_attribute];
14074 bool repl_null[Natts_pg_attribute];
14075 bool repl_repl[Natts_pg_attribute];
14076 Acl *newAcl;
14077 Datum aclDatum;
14078 bool isNull;
14079 HeapTuple newtuple;
14081 /* Ignore dropped columns */
14082 if (att->attisdropped)
14083 continue;
14085 aclDatum = heap_getattr(attributeTuple,
14086 Anum_pg_attribute_attacl,
14087 RelationGetDescr(attRelation),
14088 &isNull);
14089 /* Null ACLs do not require changes */
14090 if (isNull)
14091 continue;
14093 memset(repl_null, false, sizeof(repl_null));
14094 memset(repl_repl, false, sizeof(repl_repl));
14096 newAcl = aclnewowner(DatumGetAclP(aclDatum),
14097 oldOwnerId, newOwnerId);
14098 repl_repl[Anum_pg_attribute_attacl - 1] = true;
14099 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
14101 newtuple = heap_modify_tuple(attributeTuple,
14102 RelationGetDescr(attRelation),
14103 repl_val, repl_null, repl_repl);
14105 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14107 heap_freetuple(newtuple);
14109 systable_endscan(scan);
14110 table_close(attRelation, RowExclusiveLock);
14114 * change_owner_recurse_to_sequences
14116 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14117 * for sequences that are dependent on serial columns, and changes their
14118 * ownership.
14120 static void
14121 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
14123 Relation depRel;
14124 SysScanDesc scan;
14125 ScanKeyData key[2];
14126 HeapTuple tup;
14129 * SERIAL sequences are those having an auto dependency on one of the
14130 * table's columns (we don't care *which* column, exactly).
14132 depRel = table_open(DependRelationId, AccessShareLock);
14134 ScanKeyInit(&key[0],
14135 Anum_pg_depend_refclassid,
14136 BTEqualStrategyNumber, F_OIDEQ,
14137 ObjectIdGetDatum(RelationRelationId));
14138 ScanKeyInit(&key[1],
14139 Anum_pg_depend_refobjid,
14140 BTEqualStrategyNumber, F_OIDEQ,
14141 ObjectIdGetDatum(relationOid));
14142 /* we leave refobjsubid unspecified */
14144 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14145 NULL, 2, key);
14147 while (HeapTupleIsValid(tup = systable_getnext(scan)))
14149 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14150 Relation seqRel;
14152 /* skip dependencies other than auto dependencies on columns */
14153 if (depForm->refobjsubid == 0 ||
14154 depForm->classid != RelationRelationId ||
14155 depForm->objsubid != 0 ||
14156 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14157 continue;
14159 /* Use relation_open just in case it's an index */
14160 seqRel = relation_open(depForm->objid, lockmode);
14162 /* skip non-sequence relations */
14163 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14165 /* No need to keep the lock */
14166 relation_close(seqRel, lockmode);
14167 continue;
14170 /* We don't need to close the sequence while we alter it. */
14171 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14173 /* Now we can close it. Keep the lock till end of transaction. */
14174 relation_close(seqRel, NoLock);
14177 systable_endscan(scan);
14179 relation_close(depRel, AccessShareLock);
14183 * ALTER TABLE CLUSTER ON
14185 * The only thing we have to do is to change the indisclustered bits.
14187 * Return the address of the new clustering index.
14189 static ObjectAddress
14190 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
14192 Oid indexOid;
14193 ObjectAddress address;
14195 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14197 if (!OidIsValid(indexOid))
14198 ereport(ERROR,
14199 (errcode(ERRCODE_UNDEFINED_OBJECT),
14200 errmsg("index \"%s\" for table \"%s\" does not exist",
14201 indexName, RelationGetRelationName(rel))));
14203 /* Check index is valid to cluster on */
14204 check_index_is_clusterable(rel, indexOid, lockmode);
14206 /* And do the work */
14207 mark_index_clustered(rel, indexOid, false);
14209 ObjectAddressSet(address,
14210 RelationRelationId, indexOid);
14212 return address;
14216 * ALTER TABLE SET WITHOUT CLUSTER
14218 * We have to find any indexes on the table that have indisclustered bit
14219 * set and turn it off.
14221 static void
14222 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
14224 mark_index_clustered(rel, InvalidOid, false);
14228 * Preparation phase for SET ACCESS METHOD
14230 * Check that access method exists. If it is the same as the table's current
14231 * access method, it is a no-op. Otherwise, a table rewrite is necessary.
14233 static void
14234 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
14236 Oid amoid;
14238 /* Check that the table access method exists */
14239 amoid = get_table_am_oid(amname, false);
14241 if (rel->rd_rel->relam == amoid)
14242 return;
14244 /* Save info for Phase 3 to do the real work */
14245 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
14246 tab->newAccessMethod = amoid;
14250 * ALTER TABLE SET TABLESPACE
14252 static void
14253 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
14255 Oid tablespaceId;
14257 /* Check that the tablespace exists */
14258 tablespaceId = get_tablespace_oid(tablespacename, false);
14260 /* Check permissions except when moving to database's default */
14261 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14263 AclResult aclresult;
14265 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
14266 if (aclresult != ACLCHECK_OK)
14267 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
14270 /* Save info for Phase 3 to do the real work */
14271 if (OidIsValid(tab->newTableSpace))
14272 ereport(ERROR,
14273 (errcode(ERRCODE_SYNTAX_ERROR),
14274 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14276 tab->newTableSpace = tablespaceId;
14280 * Set, reset, or replace reloptions.
14282 static void
14283 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
14284 LOCKMODE lockmode)
14286 Oid relid;
14287 Relation pgclass;
14288 HeapTuple tuple;
14289 HeapTuple newtuple;
14290 Datum datum;
14291 bool isnull;
14292 Datum newOptions;
14293 Datum repl_val[Natts_pg_class];
14294 bool repl_null[Natts_pg_class];
14295 bool repl_repl[Natts_pg_class];
14296 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14298 if (defList == NIL && operation != AT_ReplaceRelOptions)
14299 return; /* nothing to do */
14301 pgclass = table_open(RelationRelationId, RowExclusiveLock);
14303 /* Fetch heap tuple */
14304 relid = RelationGetRelid(rel);
14305 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14306 if (!HeapTupleIsValid(tuple))
14307 elog(ERROR, "cache lookup failed for relation %u", relid);
14309 if (operation == AT_ReplaceRelOptions)
14312 * If we're supposed to replace the reloptions list, we just pretend
14313 * there were none before.
14315 datum = (Datum) 0;
14316 isnull = true;
14318 else
14320 /* Get the old reloptions */
14321 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14322 &isnull);
14325 /* Generate new proposed reloptions (text array) */
14326 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14327 defList, NULL, validnsps, false,
14328 operation == AT_ResetRelOptions);
14330 /* Validate */
14331 switch (rel->rd_rel->relkind)
14333 case RELKIND_RELATION:
14334 case RELKIND_TOASTVALUE:
14335 case RELKIND_MATVIEW:
14336 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
14337 break;
14338 case RELKIND_PARTITIONED_TABLE:
14339 (void) partitioned_table_reloptions(newOptions, true);
14340 break;
14341 case RELKIND_VIEW:
14342 (void) view_reloptions(newOptions, true);
14343 break;
14344 case RELKIND_INDEX:
14345 case RELKIND_PARTITIONED_INDEX:
14346 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
14347 break;
14348 default:
14349 ereport(ERROR,
14350 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14351 errmsg("cannot set options for relation \"%s\"",
14352 RelationGetRelationName(rel)),
14353 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
14354 break;
14357 /* Special-case validation of view options */
14358 if (rel->rd_rel->relkind == RELKIND_VIEW)
14360 Query *view_query = get_view_query(rel);
14361 List *view_options = untransformRelOptions(newOptions);
14362 ListCell *cell;
14363 bool check_option = false;
14365 foreach(cell, view_options)
14367 DefElem *defel = (DefElem *) lfirst(cell);
14369 if (strcmp(defel->defname, "check_option") == 0)
14370 check_option = true;
14374 * If the check option is specified, look to see if the view is
14375 * actually auto-updatable or not.
14377 if (check_option)
14379 const char *view_updatable_error =
14380 view_query_is_auto_updatable(view_query, true);
14382 if (view_updatable_error)
14383 ereport(ERROR,
14384 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14385 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14386 errhint("%s", _(view_updatable_error))));
14391 * All we need do here is update the pg_class row; the new options will be
14392 * propagated into relcaches during post-commit cache inval.
14394 memset(repl_val, 0, sizeof(repl_val));
14395 memset(repl_null, false, sizeof(repl_null));
14396 memset(repl_repl, false, sizeof(repl_repl));
14398 if (newOptions != (Datum) 0)
14399 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
14400 else
14401 repl_null[Anum_pg_class_reloptions - 1] = true;
14403 repl_repl[Anum_pg_class_reloptions - 1] = true;
14405 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
14406 repl_val, repl_null, repl_repl);
14408 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
14410 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14412 heap_freetuple(newtuple);
14414 ReleaseSysCache(tuple);
14416 /* repeat the whole exercise for the toast table, if there's one */
14417 if (OidIsValid(rel->rd_rel->reltoastrelid))
14419 Relation toastrel;
14420 Oid toastid = rel->rd_rel->reltoastrelid;
14422 toastrel = table_open(toastid, lockmode);
14424 /* Fetch heap tuple */
14425 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
14426 if (!HeapTupleIsValid(tuple))
14427 elog(ERROR, "cache lookup failed for relation %u", toastid);
14429 if (operation == AT_ReplaceRelOptions)
14432 * If we're supposed to replace the reloptions list, we just
14433 * pretend there were none before.
14435 datum = (Datum) 0;
14436 isnull = true;
14438 else
14440 /* Get the old reloptions */
14441 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14442 &isnull);
14445 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14446 defList, "toast", validnsps, false,
14447 operation == AT_ResetRelOptions);
14449 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
14451 memset(repl_val, 0, sizeof(repl_val));
14452 memset(repl_null, false, sizeof(repl_null));
14453 memset(repl_repl, false, sizeof(repl_repl));
14455 if (newOptions != (Datum) 0)
14456 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
14457 else
14458 repl_null[Anum_pg_class_reloptions - 1] = true;
14460 repl_repl[Anum_pg_class_reloptions - 1] = true;
14462 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
14463 repl_val, repl_null, repl_repl);
14465 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
14467 InvokeObjectPostAlterHookArg(RelationRelationId,
14468 RelationGetRelid(toastrel), 0,
14469 InvalidOid, true);
14471 heap_freetuple(newtuple);
14473 ReleaseSysCache(tuple);
14475 table_close(toastrel, NoLock);
14478 table_close(pgclass, RowExclusiveLock);
14482 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
14483 * rewriting to be done, so we just want to copy the data as fast as possible.
14485 static void
14486 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
14488 Relation rel;
14489 Oid reltoastrelid;
14490 RelFileNumber newrelfilenumber;
14491 RelFileLocator newrlocator;
14492 List *reltoastidxids = NIL;
14493 ListCell *lc;
14496 * Need lock here in case we are recursing to toast table or index
14498 rel = relation_open(tableOid, lockmode);
14500 /* Check first if relation can be moved to new tablespace */
14501 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
14503 InvokeObjectPostAlterHook(RelationRelationId,
14504 RelationGetRelid(rel), 0);
14505 relation_close(rel, NoLock);
14506 return;
14509 reltoastrelid = rel->rd_rel->reltoastrelid;
14510 /* Fetch the list of indexes on toast relation if necessary */
14511 if (OidIsValid(reltoastrelid))
14513 Relation toastRel = relation_open(reltoastrelid, lockmode);
14515 reltoastidxids = RelationGetIndexList(toastRel);
14516 relation_close(toastRel, lockmode);
14520 * Relfilenumbers are not unique in databases across tablespaces, so we
14521 * need to allocate a new one in the new tablespace.
14523 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
14524 rel->rd_rel->relpersistence);
14526 /* Open old and new relation */
14527 newrlocator = rel->rd_locator;
14528 newrlocator.relNumber = newrelfilenumber;
14529 newrlocator.spcOid = newTableSpace;
14531 /* hand off to AM to actually create new rel storage and copy the data */
14532 if (rel->rd_rel->relkind == RELKIND_INDEX)
14534 index_copy_data(rel, newrlocator);
14536 else
14538 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
14539 table_relation_copy_data(rel, &newrlocator);
14543 * Update the pg_class row.
14545 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
14546 * executed on pg_class or its indexes (the above copy wouldn't contain
14547 * the updated pg_class entry), but that's forbidden with
14548 * CheckRelationTableSpaceMove().
14550 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
14552 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14554 RelationAssumeNewRelfilelocator(rel);
14556 relation_close(rel, NoLock);
14558 /* Make sure the reltablespace change is visible */
14559 CommandCounterIncrement();
14561 /* Move associated toast relation and/or indexes, too */
14562 if (OidIsValid(reltoastrelid))
14563 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
14564 foreach(lc, reltoastidxids)
14565 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
14567 /* Clean up */
14568 list_free(reltoastidxids);
14572 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
14573 * storage that have an interest in preserving tablespace.
14575 * Since these have no storage the tablespace can be updated with a simple
14576 * metadata only operation to update the tablespace.
14578 static void
14579 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
14582 * Shouldn't be called on relations having storage; these are processed in
14583 * phase 3.
14585 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
14587 /* check if relation can be moved to its new tablespace */
14588 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
14590 InvokeObjectPostAlterHook(RelationRelationId,
14591 RelationGetRelid(rel),
14593 return;
14596 /* Update can be done, so change reltablespace */
14597 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
14599 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14601 /* Make sure the reltablespace change is visible */
14602 CommandCounterIncrement();
14606 * Alter Table ALL ... SET TABLESPACE
14608 * Allows a user to move all objects of some type in a given tablespace in the
14609 * current database to another tablespace. Objects can be chosen based on the
14610 * owner of the object also, to allow users to move only their objects.
14611 * The user must have CREATE rights on the new tablespace, as usual. The main
14612 * permissions handling is done by the lower-level table move function.
14614 * All to-be-moved objects are locked first. If NOWAIT is specified and the
14615 * lock can't be acquired then we ereport(ERROR).
14618 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
14620 List *relations = NIL;
14621 ListCell *l;
14622 ScanKeyData key[1];
14623 Relation rel;
14624 TableScanDesc scan;
14625 HeapTuple tuple;
14626 Oid orig_tablespaceoid;
14627 Oid new_tablespaceoid;
14628 List *role_oids = roleSpecsToIds(stmt->roles);
14630 /* Ensure we were not asked to move something we can't */
14631 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
14632 stmt->objtype != OBJECT_MATVIEW)
14633 ereport(ERROR,
14634 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
14635 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
14637 /* Get the orig and new tablespace OIDs */
14638 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
14639 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
14641 /* Can't move shared relations in to or out of pg_global */
14642 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
14643 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
14644 new_tablespaceoid == GLOBALTABLESPACE_OID)
14645 ereport(ERROR,
14646 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
14647 errmsg("cannot move relations in to or out of pg_global tablespace")));
14650 * Must have CREATE rights on the new tablespace, unless it is the
14651 * database default tablespace (which all users implicitly have CREATE
14652 * rights on).
14654 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
14656 AclResult aclresult;
14658 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
14659 ACL_CREATE);
14660 if (aclresult != ACLCHECK_OK)
14661 aclcheck_error(aclresult, OBJECT_TABLESPACE,
14662 get_tablespace_name(new_tablespaceoid));
14666 * Now that the checks are done, check if we should set either to
14667 * InvalidOid because it is our database's default tablespace.
14669 if (orig_tablespaceoid == MyDatabaseTableSpace)
14670 orig_tablespaceoid = InvalidOid;
14672 if (new_tablespaceoid == MyDatabaseTableSpace)
14673 new_tablespaceoid = InvalidOid;
14675 /* no-op */
14676 if (orig_tablespaceoid == new_tablespaceoid)
14677 return new_tablespaceoid;
14680 * Walk the list of objects in the tablespace and move them. This will
14681 * only find objects in our database, of course.
14683 ScanKeyInit(&key[0],
14684 Anum_pg_class_reltablespace,
14685 BTEqualStrategyNumber, F_OIDEQ,
14686 ObjectIdGetDatum(orig_tablespaceoid));
14688 rel = table_open(RelationRelationId, AccessShareLock);
14689 scan = table_beginscan_catalog(rel, 1, key);
14690 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
14692 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
14693 Oid relOid = relForm->oid;
14696 * Do not move objects in pg_catalog as part of this, if an admin
14697 * really wishes to do so, they can issue the individual ALTER
14698 * commands directly.
14700 * Also, explicitly avoid any shared tables, temp tables, or TOAST
14701 * (TOAST will be moved with the main table).
14703 if (IsCatalogNamespace(relForm->relnamespace) ||
14704 relForm->relisshared ||
14705 isAnyTempNamespace(relForm->relnamespace) ||
14706 IsToastNamespace(relForm->relnamespace))
14707 continue;
14709 /* Only move the object type requested */
14710 if ((stmt->objtype == OBJECT_TABLE &&
14711 relForm->relkind != RELKIND_RELATION &&
14712 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
14713 (stmt->objtype == OBJECT_INDEX &&
14714 relForm->relkind != RELKIND_INDEX &&
14715 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
14716 (stmt->objtype == OBJECT_MATVIEW &&
14717 relForm->relkind != RELKIND_MATVIEW))
14718 continue;
14720 /* Check if we are only moving objects owned by certain roles */
14721 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
14722 continue;
14725 * Handle permissions-checking here since we are locking the tables
14726 * and also to avoid doing a bunch of work only to fail part-way. Note
14727 * that permissions will also be checked by AlterTableInternal().
14729 * Caller must be considered an owner on the table to move it.
14731 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
14732 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
14733 NameStr(relForm->relname));
14735 if (stmt->nowait &&
14736 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
14737 ereport(ERROR,
14738 (errcode(ERRCODE_OBJECT_IN_USE),
14739 errmsg("aborting because lock on relation \"%s.%s\" is not available",
14740 get_namespace_name(relForm->relnamespace),
14741 NameStr(relForm->relname))));
14742 else
14743 LockRelationOid(relOid, AccessExclusiveLock);
14745 /* Add to our list of objects to move */
14746 relations = lappend_oid(relations, relOid);
14749 table_endscan(scan);
14750 table_close(rel, AccessShareLock);
14752 if (relations == NIL)
14753 ereport(NOTICE,
14754 (errcode(ERRCODE_NO_DATA_FOUND),
14755 errmsg("no matching relations in tablespace \"%s\" found",
14756 orig_tablespaceoid == InvalidOid ? "(database default)" :
14757 get_tablespace_name(orig_tablespaceoid))));
14759 /* Everything is locked, loop through and move all of the relations. */
14760 foreach(l, relations)
14762 List *cmds = NIL;
14763 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14765 cmd->subtype = AT_SetTableSpace;
14766 cmd->name = stmt->new_tablespacename;
14768 cmds = lappend(cmds, cmd);
14770 EventTriggerAlterTableStart((Node *) stmt);
14771 /* OID is set by AlterTableInternal */
14772 AlterTableInternal(lfirst_oid(l), cmds, false);
14773 EventTriggerAlterTableEnd();
14776 return new_tablespaceoid;
14779 static void
14780 index_copy_data(Relation rel, RelFileLocator newrlocator)
14782 SMgrRelation dstrel;
14784 dstrel = smgropen(newrlocator, rel->rd_backend);
14787 * Since we copy the file directly without looking at the shared buffers,
14788 * we'd better first flush out any pages of the source relation that are
14789 * in shared buffers. We assume no new changes will be made while we are
14790 * holding exclusive lock on the rel.
14792 FlushRelationBuffers(rel);
14795 * Create and copy all forks of the relation, and schedule unlinking of
14796 * old physical files.
14798 * NOTE: any conflict in relfilenumber value will be caught in
14799 * RelationCreateStorage().
14801 RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
14803 /* copy main fork */
14804 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
14805 rel->rd_rel->relpersistence);
14807 /* copy those extra forks that exist */
14808 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
14809 forkNum <= MAX_FORKNUM; forkNum++)
14811 if (smgrexists(RelationGetSmgr(rel), forkNum))
14813 smgrcreate(dstrel, forkNum, false);
14816 * WAL log creation if the relation is persistent, or this is the
14817 * init fork of an unlogged relation.
14819 if (RelationIsPermanent(rel) ||
14820 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
14821 forkNum == INIT_FORKNUM))
14822 log_smgrcreate(&newrlocator, forkNum);
14823 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
14824 rel->rd_rel->relpersistence);
14828 /* drop old relation, and close new one */
14829 RelationDropStorage(rel);
14830 smgrclose(dstrel);
14834 * ALTER TABLE ENABLE/DISABLE TRIGGER
14836 * We just pass this off to trigger.c.
14838 static void
14839 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
14840 char fires_when, bool skip_system, bool recurse,
14841 LOCKMODE lockmode)
14843 EnableDisableTrigger(rel, trigname, InvalidOid,
14844 fires_when, skip_system, recurse,
14845 lockmode);
14847 InvokeObjectPostAlterHook(RelationRelationId,
14848 RelationGetRelid(rel), 0);
14852 * ALTER TABLE ENABLE/DISABLE RULE
14854 * We just pass this off to rewriteDefine.c.
14856 static void
14857 ATExecEnableDisableRule(Relation rel, const char *rulename,
14858 char fires_when, LOCKMODE lockmode)
14860 EnableDisableRule(rel, rulename, fires_when);
14862 InvokeObjectPostAlterHook(RelationRelationId,
14863 RelationGetRelid(rel), 0);
14867 * ALTER TABLE INHERIT
14869 * Add a parent to the child's parents. This verifies that all the columns and
14870 * check constraints of the parent appear in the child and that they have the
14871 * same data types and expressions.
14873 static void
14874 ATPrepAddInherit(Relation child_rel)
14876 if (child_rel->rd_rel->reloftype)
14877 ereport(ERROR,
14878 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14879 errmsg("cannot change inheritance of typed table")));
14881 if (child_rel->rd_rel->relispartition)
14882 ereport(ERROR,
14883 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14884 errmsg("cannot change inheritance of a partition")));
14886 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14887 ereport(ERROR,
14888 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14889 errmsg("cannot change inheritance of partitioned table")));
14893 * Return the address of the new parent relation.
14895 static ObjectAddress
14896 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
14898 Relation parent_rel;
14899 List *children;
14900 ObjectAddress address;
14901 const char *trigger_name;
14904 * A self-exclusive lock is needed here. See the similar case in
14905 * MergeAttributes() for a full explanation.
14907 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
14910 * Must be owner of both parent and child -- child was checked by
14911 * ATSimplePermissions call in ATPrepCmd
14913 ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
14915 /* Permanent rels cannot inherit from temporary ones */
14916 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14917 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
14918 ereport(ERROR,
14919 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14920 errmsg("cannot inherit from temporary relation \"%s\"",
14921 RelationGetRelationName(parent_rel))));
14923 /* If parent rel is temp, it must belong to this session */
14924 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14925 !parent_rel->rd_islocaltemp)
14926 ereport(ERROR,
14927 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14928 errmsg("cannot inherit from temporary relation of another session")));
14930 /* Ditto for the child */
14931 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
14932 !child_rel->rd_islocaltemp)
14933 ereport(ERROR,
14934 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14935 errmsg("cannot inherit to temporary relation of another session")));
14937 /* Prevent partitioned tables from becoming inheritance parents */
14938 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14939 ereport(ERROR,
14940 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14941 errmsg("cannot inherit from partitioned table \"%s\"",
14942 parent->relname)));
14944 /* Likewise for partitions */
14945 if (parent_rel->rd_rel->relispartition)
14946 ereport(ERROR,
14947 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14948 errmsg("cannot inherit from a partition")));
14951 * Prevent circularity by seeing if proposed parent inherits from child.
14952 * (In particular, this disallows making a rel inherit from itself.)
14954 * This is not completely bulletproof because of race conditions: in
14955 * multi-level inheritance trees, someone else could concurrently be
14956 * making another inheritance link that closes the loop but does not join
14957 * either of the rels we have locked. Preventing that seems to require
14958 * exclusive locks on the entire inheritance tree, which is a cure worse
14959 * than the disease. find_all_inheritors() will cope with circularity
14960 * anyway, so don't sweat it too much.
14962 * We use weakest lock we can on child's children, namely AccessShareLock.
14964 children = find_all_inheritors(RelationGetRelid(child_rel),
14965 AccessShareLock, NULL);
14967 if (list_member_oid(children, RelationGetRelid(parent_rel)))
14968 ereport(ERROR,
14969 (errcode(ERRCODE_DUPLICATE_TABLE),
14970 errmsg("circular inheritance not allowed"),
14971 errdetail("\"%s\" is already a child of \"%s\".",
14972 parent->relname,
14973 RelationGetRelationName(child_rel))));
14976 * If child_rel has row-level triggers with transition tables, we
14977 * currently don't allow it to become an inheritance child. See also
14978 * prohibitions in ATExecAttachPartition() and CreateTrigger().
14980 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
14981 if (trigger_name != NULL)
14982 ereport(ERROR,
14983 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14984 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
14985 trigger_name, RelationGetRelationName(child_rel)),
14986 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
14988 /* OK to create inheritance */
14989 CreateInheritance(child_rel, parent_rel);
14991 ObjectAddressSet(address, RelationRelationId,
14992 RelationGetRelid(parent_rel));
14994 /* keep our lock on the parent relation until commit */
14995 table_close(parent_rel, NoLock);
14997 return address;
15001 * CreateInheritance
15002 * Catalog manipulation portion of creating inheritance between a child
15003 * table and a parent table.
15005 * Common to ATExecAddInherit() and ATExecAttachPartition().
15007 static void
15008 CreateInheritance(Relation child_rel, Relation parent_rel)
15010 Relation catalogRelation;
15011 SysScanDesc scan;
15012 ScanKeyData key;
15013 HeapTuple inheritsTuple;
15014 int32 inhseqno;
15016 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15017 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15020 * Check for duplicates in the list of parents, and determine the highest
15021 * inhseqno already present; we'll use the next one for the new parent.
15022 * Also, if proposed child is a partition, it cannot already be
15023 * inheriting.
15025 * Note: we do not reject the case where the child already inherits from
15026 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15028 ScanKeyInit(&key,
15029 Anum_pg_inherits_inhrelid,
15030 BTEqualStrategyNumber, F_OIDEQ,
15031 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15032 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15033 true, NULL, 1, &key);
15035 /* inhseqno sequences start at 1 */
15036 inhseqno = 0;
15037 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15039 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15041 if (inh->inhparent == RelationGetRelid(parent_rel))
15042 ereport(ERROR,
15043 (errcode(ERRCODE_DUPLICATE_TABLE),
15044 errmsg("relation \"%s\" would be inherited from more than once",
15045 RelationGetRelationName(parent_rel))));
15047 if (inh->inhseqno > inhseqno)
15048 inhseqno = inh->inhseqno;
15050 systable_endscan(scan);
15052 /* Match up the columns and bump attinhcount as needed */
15053 MergeAttributesIntoExisting(child_rel, parent_rel);
15055 /* Match up the constraints and bump coninhcount as needed */
15056 MergeConstraintsIntoExisting(child_rel, parent_rel);
15059 * OK, it looks valid. Make the catalog entries that show inheritance.
15061 StoreCatalogInheritance1(RelationGetRelid(child_rel),
15062 RelationGetRelid(parent_rel),
15063 inhseqno + 1,
15064 catalogRelation,
15065 parent_rel->rd_rel->relkind ==
15066 RELKIND_PARTITIONED_TABLE);
15068 /* Now we're done with pg_inherits */
15069 table_close(catalogRelation, RowExclusiveLock);
15073 * Obtain the source-text form of the constraint expression for a check
15074 * constraint, given its pg_constraint tuple
15076 static char *
15077 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
15079 Form_pg_constraint con;
15080 bool isnull;
15081 Datum attr;
15082 Datum expr;
15084 con = (Form_pg_constraint) GETSTRUCT(contup);
15085 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15086 if (isnull)
15087 elog(ERROR, "null conbin for constraint %u", con->oid);
15089 expr = DirectFunctionCall2(pg_get_expr, attr,
15090 ObjectIdGetDatum(con->conrelid));
15091 return TextDatumGetCString(expr);
15095 * Determine whether two check constraints are functionally equivalent
15097 * The test we apply is to see whether they reverse-compile to the same
15098 * source string. This insulates us from issues like whether attributes
15099 * have the same physical column numbers in parent and child relations.
15101 static bool
15102 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
15104 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
15105 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
15107 if (acon->condeferrable != bcon->condeferrable ||
15108 acon->condeferred != bcon->condeferred ||
15109 strcmp(decompile_conbin(a, tupleDesc),
15110 decompile_conbin(b, tupleDesc)) != 0)
15111 return false;
15112 else
15113 return true;
15117 * Check columns in child table match up with columns in parent, and increment
15118 * their attinhcount.
15120 * Called by CreateInheritance
15122 * Currently all parent columns must be found in child. Missing columns are an
15123 * error. One day we might consider creating new columns like CREATE TABLE
15124 * does. However, that is widely unpopular --- in the common use case of
15125 * partitioned tables it's a foot-gun.
15127 * The data type must match exactly. If the parent column is NOT NULL then
15128 * the child must be as well. Defaults are not compared, however.
15130 static void
15131 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
15133 Relation attrrel;
15134 AttrNumber parent_attno;
15135 int parent_natts;
15136 TupleDesc tupleDesc;
15137 HeapTuple tuple;
15138 bool child_is_partition = false;
15140 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
15142 tupleDesc = RelationGetDescr(parent_rel);
15143 parent_natts = tupleDesc->natts;
15145 /* If parent_rel is a partitioned table, child_rel must be a partition */
15146 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15147 child_is_partition = true;
15149 for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
15151 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
15152 parent_attno - 1);
15153 char *attributeName = NameStr(attribute->attname);
15155 /* Ignore dropped columns in the parent. */
15156 if (attribute->attisdropped)
15157 continue;
15159 /* Find same column in child (matching on column name). */
15160 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel),
15161 attributeName);
15162 if (HeapTupleIsValid(tuple))
15164 /* Check they are same type, typmod, and collation */
15165 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
15167 if (attribute->atttypid != childatt->atttypid ||
15168 attribute->atttypmod != childatt->atttypmod)
15169 ereport(ERROR,
15170 (errcode(ERRCODE_DATATYPE_MISMATCH),
15171 errmsg("child table \"%s\" has different type for column \"%s\"",
15172 RelationGetRelationName(child_rel),
15173 attributeName)));
15175 if (attribute->attcollation != childatt->attcollation)
15176 ereport(ERROR,
15177 (errcode(ERRCODE_COLLATION_MISMATCH),
15178 errmsg("child table \"%s\" has different collation for column \"%s\"",
15179 RelationGetRelationName(child_rel),
15180 attributeName)));
15183 * Check child doesn't discard NOT NULL property. (Other
15184 * constraints are checked elsewhere.)
15186 if (attribute->attnotnull && !childatt->attnotnull)
15187 ereport(ERROR,
15188 (errcode(ERRCODE_DATATYPE_MISMATCH),
15189 errmsg("column \"%s\" in child table must be marked NOT NULL",
15190 attributeName)));
15193 * Child column must be generated if and only if parent column is.
15195 if (attribute->attgenerated && !childatt->attgenerated)
15196 ereport(ERROR,
15197 (errcode(ERRCODE_DATATYPE_MISMATCH),
15198 errmsg("column \"%s\" in child table must be a generated column",
15199 attributeName)));
15200 if (childatt->attgenerated && !attribute->attgenerated)
15201 ereport(ERROR,
15202 (errcode(ERRCODE_DATATYPE_MISMATCH),
15203 errmsg("column \"%s\" in child table must not be a generated column",
15204 attributeName)));
15207 * OK, bump the child column's inheritance count. (If we fail
15208 * later on, this change will just roll back.)
15210 childatt->attinhcount++;
15211 if (childatt->attinhcount < 0)
15212 ereport(ERROR,
15213 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15214 errmsg("too many inheritance parents"));
15217 * In case of partitions, we must enforce that value of attislocal
15218 * is same in all partitions. (Note: there are only inherited
15219 * attributes in partitions)
15221 if (child_is_partition)
15223 Assert(childatt->attinhcount == 1);
15224 childatt->attislocal = false;
15227 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
15228 heap_freetuple(tuple);
15230 else
15232 ereport(ERROR,
15233 (errcode(ERRCODE_DATATYPE_MISMATCH),
15234 errmsg("child table is missing column \"%s\"",
15235 attributeName)));
15239 table_close(attrrel, RowExclusiveLock);
15243 * Check constraints in child table match up with constraints in parent,
15244 * and increment their coninhcount.
15246 * Constraints that are marked ONLY in the parent are ignored.
15248 * Called by CreateInheritance
15250 * Currently all constraints in parent must be present in the child. One day we
15251 * may consider adding new constraints like CREATE TABLE does.
15253 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15254 * constraints. As long as tables have more like 10 constraints it shouldn't be
15255 * a problem though. Even 100 constraints ought not be the end of the world.
15257 * XXX See MergeWithExistingConstraint too if you change this code.
15259 static void
15260 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
15262 Relation catalog_relation;
15263 TupleDesc tuple_desc;
15264 SysScanDesc parent_scan;
15265 ScanKeyData parent_key;
15266 HeapTuple parent_tuple;
15267 bool child_is_partition = false;
15269 catalog_relation = table_open(ConstraintRelationId, RowExclusiveLock);
15270 tuple_desc = RelationGetDescr(catalog_relation);
15272 /* If parent_rel is a partitioned table, child_rel must be a partition */
15273 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15274 child_is_partition = true;
15276 /* Outer loop scans through the parent's constraint definitions */
15277 ScanKeyInit(&parent_key,
15278 Anum_pg_constraint_conrelid,
15279 BTEqualStrategyNumber, F_OIDEQ,
15280 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
15281 parent_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
15282 true, NULL, 1, &parent_key);
15284 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15286 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15287 SysScanDesc child_scan;
15288 ScanKeyData child_key;
15289 HeapTuple child_tuple;
15290 bool found = false;
15292 if (parent_con->contype != CONSTRAINT_CHECK)
15293 continue;
15295 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15296 if (parent_con->connoinherit)
15297 continue;
15299 /* Search for a child constraint matching this one */
15300 ScanKeyInit(&child_key,
15301 Anum_pg_constraint_conrelid,
15302 BTEqualStrategyNumber, F_OIDEQ,
15303 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15304 child_scan = systable_beginscan(catalog_relation, ConstraintRelidTypidNameIndexId,
15305 true, NULL, 1, &child_key);
15307 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15309 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
15310 HeapTuple child_copy;
15312 if (child_con->contype != CONSTRAINT_CHECK)
15313 continue;
15315 if (strcmp(NameStr(parent_con->conname),
15316 NameStr(child_con->conname)) != 0)
15317 continue;
15319 if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
15320 ereport(ERROR,
15321 (errcode(ERRCODE_DATATYPE_MISMATCH),
15322 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15323 RelationGetRelationName(child_rel),
15324 NameStr(parent_con->conname))));
15326 /* If the child constraint is "no inherit" then cannot merge */
15327 if (child_con->connoinherit)
15328 ereport(ERROR,
15329 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15330 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15331 NameStr(child_con->conname),
15332 RelationGetRelationName(child_rel))));
15335 * If the child constraint is "not valid" then cannot merge with a
15336 * valid parent constraint
15338 if (parent_con->convalidated && !child_con->convalidated)
15339 ereport(ERROR,
15340 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15341 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15342 NameStr(child_con->conname),
15343 RelationGetRelationName(child_rel))));
15346 * OK, bump the child constraint's inheritance count. (If we fail
15347 * later on, this change will just roll back.)
15349 child_copy = heap_copytuple(child_tuple);
15350 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
15351 child_con->coninhcount++;
15352 if (child_con->coninhcount < 0)
15353 ereport(ERROR,
15354 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15355 errmsg("too many inheritance parents"));
15358 * In case of partitions, an inherited constraint must be
15359 * inherited only once since it cannot have multiple parents and
15360 * it is never considered local.
15362 if (child_is_partition)
15364 Assert(child_con->coninhcount == 1);
15365 child_con->conislocal = false;
15368 CatalogTupleUpdate(catalog_relation, &child_copy->t_self, child_copy);
15369 heap_freetuple(child_copy);
15371 found = true;
15372 break;
15375 systable_endscan(child_scan);
15377 if (!found)
15378 ereport(ERROR,
15379 (errcode(ERRCODE_DATATYPE_MISMATCH),
15380 errmsg("child table is missing constraint \"%s\"",
15381 NameStr(parent_con->conname))));
15384 systable_endscan(parent_scan);
15385 table_close(catalog_relation, RowExclusiveLock);
15389 * ALTER TABLE NO INHERIT
15391 * Return value is the address of the relation that is no longer parent.
15393 static ObjectAddress
15394 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
15396 ObjectAddress address;
15397 Relation parent_rel;
15399 if (rel->rd_rel->relispartition)
15400 ereport(ERROR,
15401 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15402 errmsg("cannot change inheritance of a partition")));
15405 * AccessShareLock on the parent is probably enough, seeing that DROP
15406 * TABLE doesn't lock parent tables at all. We need some lock since we'll
15407 * be inspecting the parent's schema.
15409 parent_rel = table_openrv(parent, AccessShareLock);
15412 * We don't bother to check ownership of the parent table --- ownership of
15413 * the child is presumed enough rights.
15416 /* Off to RemoveInheritance() where most of the work happens */
15417 RemoveInheritance(rel, parent_rel, false);
15419 ObjectAddressSet(address, RelationRelationId,
15420 RelationGetRelid(parent_rel));
15422 /* keep our lock on the parent relation until commit */
15423 table_close(parent_rel, NoLock);
15425 return address;
15429 * MarkInheritDetached
15431 * Set inhdetachpending for a partition, for ATExecDetachPartition
15432 * in concurrent mode. While at it, verify that no other partition is
15433 * already pending detach.
15435 static void
15436 MarkInheritDetached(Relation child_rel, Relation parent_rel)
15438 Relation catalogRelation;
15439 SysScanDesc scan;
15440 ScanKeyData key;
15441 HeapTuple inheritsTuple;
15442 bool found = false;
15444 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
15447 * Find pg_inherits entries by inhparent. (We need to scan them all in
15448 * order to verify that no other partition is pending detach.)
15450 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15451 ScanKeyInit(&key,
15452 Anum_pg_inherits_inhparent,
15453 BTEqualStrategyNumber, F_OIDEQ,
15454 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
15455 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
15456 true, NULL, 1, &key);
15458 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15460 Form_pg_inherits inhForm;
15462 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15463 if (inhForm->inhdetachpending)
15464 ereport(ERROR,
15465 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
15466 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
15467 get_rel_name(inhForm->inhrelid),
15468 get_namespace_name(parent_rel->rd_rel->relnamespace),
15469 RelationGetRelationName(parent_rel)),
15470 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
15472 if (inhForm->inhrelid == RelationGetRelid(child_rel))
15474 HeapTuple newtup;
15476 newtup = heap_copytuple(inheritsTuple);
15477 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
15479 CatalogTupleUpdate(catalogRelation,
15480 &inheritsTuple->t_self,
15481 newtup);
15482 found = true;
15483 heap_freetuple(newtup);
15484 /* keep looking, to ensure we catch others pending detach */
15488 /* Done */
15489 systable_endscan(scan);
15490 table_close(catalogRelation, RowExclusiveLock);
15492 if (!found)
15493 ereport(ERROR,
15494 (errcode(ERRCODE_UNDEFINED_TABLE),
15495 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
15496 RelationGetRelationName(child_rel),
15497 RelationGetRelationName(parent_rel))));
15501 * RemoveInheritance
15503 * Drop a parent from the child's parents. This just adjusts the attinhcount
15504 * and attislocal of the columns and removes the pg_inherit and pg_depend
15505 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
15507 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
15508 * up attislocal stays true, which means if a child is ever removed from a
15509 * parent then its columns will never be automatically dropped which may
15510 * surprise. But at least we'll never surprise by dropping columns someone
15511 * isn't expecting to be dropped which would actually mean data loss.
15513 * coninhcount and conislocal for inherited constraints are adjusted in
15514 * exactly the same way.
15516 * Common to ATExecDropInherit() and ATExecDetachPartition().
15518 static void
15519 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
15521 Relation catalogRelation;
15522 SysScanDesc scan;
15523 ScanKeyData key[3];
15524 HeapTuple attributeTuple,
15525 constraintTuple;
15526 List *connames;
15527 bool found;
15528 bool child_is_partition = false;
15530 /* If parent_rel is a partitioned table, child_rel must be a partition */
15531 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15532 child_is_partition = true;
15534 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
15535 RelationGetRelid(parent_rel),
15536 expect_detached,
15537 RelationGetRelationName(child_rel));
15538 if (!found)
15540 if (child_is_partition)
15541 ereport(ERROR,
15542 (errcode(ERRCODE_UNDEFINED_TABLE),
15543 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
15544 RelationGetRelationName(child_rel),
15545 RelationGetRelationName(parent_rel))));
15546 else
15547 ereport(ERROR,
15548 (errcode(ERRCODE_UNDEFINED_TABLE),
15549 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
15550 RelationGetRelationName(parent_rel),
15551 RelationGetRelationName(child_rel))));
15555 * Search through child columns looking for ones matching parent rel
15557 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
15558 ScanKeyInit(&key[0],
15559 Anum_pg_attribute_attrelid,
15560 BTEqualStrategyNumber, F_OIDEQ,
15561 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15562 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
15563 true, NULL, 1, key);
15564 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
15566 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
15568 /* Ignore if dropped or not inherited */
15569 if (att->attisdropped)
15570 continue;
15571 if (att->attinhcount <= 0)
15572 continue;
15574 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
15575 NameStr(att->attname)))
15577 /* Decrement inhcount and possibly set islocal to true */
15578 HeapTuple copyTuple = heap_copytuple(attributeTuple);
15579 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
15581 copy_att->attinhcount--;
15582 if (copy_att->attinhcount == 0)
15583 copy_att->attislocal = true;
15585 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
15586 heap_freetuple(copyTuple);
15589 systable_endscan(scan);
15590 table_close(catalogRelation, RowExclusiveLock);
15593 * Likewise, find inherited check constraints and disinherit them. To do
15594 * this, we first need a list of the names of the parent's check
15595 * constraints. (We cheat a bit by only checking for name matches,
15596 * assuming that the expressions will match.)
15598 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
15599 ScanKeyInit(&key[0],
15600 Anum_pg_constraint_conrelid,
15601 BTEqualStrategyNumber, F_OIDEQ,
15602 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
15603 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
15604 true, NULL, 1, key);
15606 connames = NIL;
15608 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
15610 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
15612 if (con->contype == CONSTRAINT_CHECK)
15613 connames = lappend(connames, pstrdup(NameStr(con->conname)));
15616 systable_endscan(scan);
15618 /* Now scan the child's constraints */
15619 ScanKeyInit(&key[0],
15620 Anum_pg_constraint_conrelid,
15621 BTEqualStrategyNumber, F_OIDEQ,
15622 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15623 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
15624 true, NULL, 1, key);
15626 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
15628 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
15629 bool match;
15630 ListCell *lc;
15632 if (con->contype != CONSTRAINT_CHECK)
15633 continue;
15635 match = false;
15636 foreach(lc, connames)
15638 if (strcmp(NameStr(con->conname), (char *) lfirst(lc)) == 0)
15640 match = true;
15641 break;
15645 if (match)
15647 /* Decrement inhcount and possibly set islocal to true */
15648 HeapTuple copyTuple = heap_copytuple(constraintTuple);
15649 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
15651 if (copy_con->coninhcount <= 0) /* shouldn't happen */
15652 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
15653 RelationGetRelid(child_rel), NameStr(copy_con->conname));
15655 copy_con->coninhcount--;
15656 if (copy_con->coninhcount == 0)
15657 copy_con->conislocal = true;
15659 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
15660 heap_freetuple(copyTuple);
15664 systable_endscan(scan);
15665 table_close(catalogRelation, RowExclusiveLock);
15667 drop_parent_dependency(RelationGetRelid(child_rel),
15668 RelationRelationId,
15669 RelationGetRelid(parent_rel),
15670 child_dependency_type(child_is_partition));
15673 * Post alter hook of this inherits. Since object_access_hook doesn't take
15674 * multiple object identifiers, we relay oid of parent relation using
15675 * auxiliary_id argument.
15677 InvokeObjectPostAlterHookArg(InheritsRelationId,
15678 RelationGetRelid(child_rel), 0,
15679 RelationGetRelid(parent_rel), false);
15683 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
15684 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
15685 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
15686 * be TypeRelationId). There's no convenient way to do this, so go trawling
15687 * through pg_depend.
15689 static void
15690 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
15691 DependencyType deptype)
15693 Relation catalogRelation;
15694 SysScanDesc scan;
15695 ScanKeyData key[3];
15696 HeapTuple depTuple;
15698 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
15700 ScanKeyInit(&key[0],
15701 Anum_pg_depend_classid,
15702 BTEqualStrategyNumber, F_OIDEQ,
15703 ObjectIdGetDatum(RelationRelationId));
15704 ScanKeyInit(&key[1],
15705 Anum_pg_depend_objid,
15706 BTEqualStrategyNumber, F_OIDEQ,
15707 ObjectIdGetDatum(relid));
15708 ScanKeyInit(&key[2],
15709 Anum_pg_depend_objsubid,
15710 BTEqualStrategyNumber, F_INT4EQ,
15711 Int32GetDatum(0));
15713 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
15714 NULL, 3, key);
15716 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
15718 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
15720 if (dep->refclassid == refclassid &&
15721 dep->refobjid == refobjid &&
15722 dep->refobjsubid == 0 &&
15723 dep->deptype == deptype)
15724 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
15727 systable_endscan(scan);
15728 table_close(catalogRelation, RowExclusiveLock);
15732 * ALTER TABLE OF
15734 * Attach a table to a composite type, as though it had been created with CREATE
15735 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
15736 * subject table must not have inheritance parents. These restrictions ensure
15737 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
15739 * The address of the type is returned.
15741 static ObjectAddress
15742 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
15744 Oid relid = RelationGetRelid(rel);
15745 Type typetuple;
15746 Form_pg_type typeform;
15747 Oid typeid;
15748 Relation inheritsRelation,
15749 relationRelation;
15750 SysScanDesc scan;
15751 ScanKeyData key;
15752 AttrNumber table_attno,
15753 type_attno;
15754 TupleDesc typeTupleDesc,
15755 tableTupleDesc;
15756 ObjectAddress tableobj,
15757 typeobj;
15758 HeapTuple classtuple;
15760 /* Validate the type. */
15761 typetuple = typenameType(NULL, ofTypename, NULL);
15762 check_of_type(typetuple);
15763 typeform = (Form_pg_type) GETSTRUCT(typetuple);
15764 typeid = typeform->oid;
15766 /* Fail if the table has any inheritance parents. */
15767 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
15768 ScanKeyInit(&key,
15769 Anum_pg_inherits_inhrelid,
15770 BTEqualStrategyNumber, F_OIDEQ,
15771 ObjectIdGetDatum(relid));
15772 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
15773 true, NULL, 1, &key);
15774 if (HeapTupleIsValid(systable_getnext(scan)))
15775 ereport(ERROR,
15776 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15777 errmsg("typed tables cannot inherit")));
15778 systable_endscan(scan);
15779 table_close(inheritsRelation, AccessShareLock);
15782 * Check the tuple descriptors for compatibility. Unlike inheritance, we
15783 * require that the order also match. However, attnotnull need not match.
15785 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
15786 tableTupleDesc = RelationGetDescr(rel);
15787 table_attno = 1;
15788 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
15790 Form_pg_attribute type_attr,
15791 table_attr;
15792 const char *type_attname,
15793 *table_attname;
15795 /* Get the next non-dropped type attribute. */
15796 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
15797 if (type_attr->attisdropped)
15798 continue;
15799 type_attname = NameStr(type_attr->attname);
15801 /* Get the next non-dropped table attribute. */
15804 if (table_attno > tableTupleDesc->natts)
15805 ereport(ERROR,
15806 (errcode(ERRCODE_DATATYPE_MISMATCH),
15807 errmsg("table is missing column \"%s\"",
15808 type_attname)));
15809 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
15810 table_attno++;
15811 } while (table_attr->attisdropped);
15812 table_attname = NameStr(table_attr->attname);
15814 /* Compare name. */
15815 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
15816 ereport(ERROR,
15817 (errcode(ERRCODE_DATATYPE_MISMATCH),
15818 errmsg("table has column \"%s\" where type requires \"%s\"",
15819 table_attname, type_attname)));
15821 /* Compare type. */
15822 if (table_attr->atttypid != type_attr->atttypid ||
15823 table_attr->atttypmod != type_attr->atttypmod ||
15824 table_attr->attcollation != type_attr->attcollation)
15825 ereport(ERROR,
15826 (errcode(ERRCODE_DATATYPE_MISMATCH),
15827 errmsg("table \"%s\" has different type for column \"%s\"",
15828 RelationGetRelationName(rel), type_attname)));
15830 ReleaseTupleDesc(typeTupleDesc);
15832 /* Any remaining columns at the end of the table had better be dropped. */
15833 for (; table_attno <= tableTupleDesc->natts; table_attno++)
15835 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
15836 table_attno - 1);
15838 if (!table_attr->attisdropped)
15839 ereport(ERROR,
15840 (errcode(ERRCODE_DATATYPE_MISMATCH),
15841 errmsg("table has extra column \"%s\"",
15842 NameStr(table_attr->attname))));
15845 /* If the table was already typed, drop the existing dependency. */
15846 if (rel->rd_rel->reloftype)
15847 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
15848 DEPENDENCY_NORMAL);
15850 /* Record a dependency on the new type. */
15851 tableobj.classId = RelationRelationId;
15852 tableobj.objectId = relid;
15853 tableobj.objectSubId = 0;
15854 typeobj.classId = TypeRelationId;
15855 typeobj.objectId = typeid;
15856 typeobj.objectSubId = 0;
15857 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
15859 /* Update pg_class.reloftype */
15860 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
15861 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15862 if (!HeapTupleIsValid(classtuple))
15863 elog(ERROR, "cache lookup failed for relation %u", relid);
15864 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
15865 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
15867 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
15869 heap_freetuple(classtuple);
15870 table_close(relationRelation, RowExclusiveLock);
15872 ReleaseSysCache(typetuple);
15874 return typeobj;
15878 * ALTER TABLE NOT OF
15880 * Detach a typed table from its originating type. Just clear reloftype and
15881 * remove the dependency.
15883 static void
15884 ATExecDropOf(Relation rel, LOCKMODE lockmode)
15886 Oid relid = RelationGetRelid(rel);
15887 Relation relationRelation;
15888 HeapTuple tuple;
15890 if (!OidIsValid(rel->rd_rel->reloftype))
15891 ereport(ERROR,
15892 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15893 errmsg("\"%s\" is not a typed table",
15894 RelationGetRelationName(rel))));
15897 * We don't bother to check ownership of the type --- ownership of the
15898 * table is presumed enough rights. No lock required on the type, either.
15901 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
15902 DEPENDENCY_NORMAL);
15904 /* Clear pg_class.reloftype */
15905 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
15906 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
15907 if (!HeapTupleIsValid(tuple))
15908 elog(ERROR, "cache lookup failed for relation %u", relid);
15909 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
15910 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
15912 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
15914 heap_freetuple(tuple);
15915 table_close(relationRelation, RowExclusiveLock);
15919 * relation_mark_replica_identity: Update a table's replica identity
15921 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
15922 * index. Otherwise, it must be InvalidOid.
15924 * Caller had better hold an exclusive lock on the relation, as the results
15925 * of running two of these concurrently wouldn't be pretty.
15927 static void
15928 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
15929 bool is_internal)
15931 Relation pg_index;
15932 Relation pg_class;
15933 HeapTuple pg_class_tuple;
15934 HeapTuple pg_index_tuple;
15935 Form_pg_class pg_class_form;
15936 Form_pg_index pg_index_form;
15937 ListCell *index;
15940 * Check whether relreplident has changed, and update it if so.
15942 pg_class = table_open(RelationRelationId, RowExclusiveLock);
15943 pg_class_tuple = SearchSysCacheCopy1(RELOID,
15944 ObjectIdGetDatum(RelationGetRelid(rel)));
15945 if (!HeapTupleIsValid(pg_class_tuple))
15946 elog(ERROR, "cache lookup failed for relation \"%s\"",
15947 RelationGetRelationName(rel));
15948 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
15949 if (pg_class_form->relreplident != ri_type)
15951 pg_class_form->relreplident = ri_type;
15952 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
15954 table_close(pg_class, RowExclusiveLock);
15955 heap_freetuple(pg_class_tuple);
15958 * Update the per-index indisreplident flags correctly.
15960 pg_index = table_open(IndexRelationId, RowExclusiveLock);
15961 foreach(index, RelationGetIndexList(rel))
15963 Oid thisIndexOid = lfirst_oid(index);
15964 bool dirty = false;
15966 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
15967 ObjectIdGetDatum(thisIndexOid));
15968 if (!HeapTupleIsValid(pg_index_tuple))
15969 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
15970 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
15972 if (thisIndexOid == indexOid)
15974 /* Set the bit if not already set. */
15975 if (!pg_index_form->indisreplident)
15977 dirty = true;
15978 pg_index_form->indisreplident = true;
15981 else
15983 /* Unset the bit if set. */
15984 if (pg_index_form->indisreplident)
15986 dirty = true;
15987 pg_index_form->indisreplident = false;
15991 if (dirty)
15993 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
15994 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
15995 InvalidOid, is_internal);
15998 * Invalidate the relcache for the table, so that after we commit
15999 * all sessions will refresh the table's replica identity index
16000 * before attempting any UPDATE or DELETE on the table. (If we
16001 * changed the table's pg_class row above, then a relcache inval
16002 * is already queued due to that; but we might not have.)
16004 CacheInvalidateRelcache(rel);
16006 heap_freetuple(pg_index_tuple);
16009 table_close(pg_index, RowExclusiveLock);
16013 * ALTER TABLE <name> REPLICA IDENTITY ...
16015 static void
16016 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
16018 Oid indexOid;
16019 Relation indexRel;
16020 int key;
16022 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16024 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16025 return;
16027 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16029 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16030 return;
16032 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16034 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16035 return;
16037 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
16039 /* fallthrough */ ;
16041 else
16042 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16044 /* Check that the index exists */
16045 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16046 if (!OidIsValid(indexOid))
16047 ereport(ERROR,
16048 (errcode(ERRCODE_UNDEFINED_OBJECT),
16049 errmsg("index \"%s\" for table \"%s\" does not exist",
16050 stmt->name, RelationGetRelationName(rel))));
16052 indexRel = index_open(indexOid, ShareLock);
16054 /* Check that the index is on the relation we're altering. */
16055 if (indexRel->rd_index == NULL ||
16056 indexRel->rd_index->indrelid != RelationGetRelid(rel))
16057 ereport(ERROR,
16058 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16059 errmsg("\"%s\" is not an index for table \"%s\"",
16060 RelationGetRelationName(indexRel),
16061 RelationGetRelationName(rel))));
16062 /* The AM must support uniqueness, and the index must in fact be unique. */
16063 if (!indexRel->rd_indam->amcanunique ||
16064 !indexRel->rd_index->indisunique)
16065 ereport(ERROR,
16066 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16067 errmsg("cannot use non-unique index \"%s\" as replica identity",
16068 RelationGetRelationName(indexRel))));
16069 /* Deferred indexes are not guaranteed to be always unique. */
16070 if (!indexRel->rd_index->indimmediate)
16071 ereport(ERROR,
16072 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16073 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16074 RelationGetRelationName(indexRel))));
16075 /* Expression indexes aren't supported. */
16076 if (RelationGetIndexExpressions(indexRel) != NIL)
16077 ereport(ERROR,
16078 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16079 errmsg("cannot use expression index \"%s\" as replica identity",
16080 RelationGetRelationName(indexRel))));
16081 /* Predicate indexes aren't supported. */
16082 if (RelationGetIndexPredicate(indexRel) != NIL)
16083 ereport(ERROR,
16084 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16085 errmsg("cannot use partial index \"%s\" as replica identity",
16086 RelationGetRelationName(indexRel))));
16088 /* Check index for nullable columns. */
16089 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16091 int16 attno = indexRel->rd_index->indkey.values[key];
16092 Form_pg_attribute attr;
16095 * Reject any other system columns. (Going forward, we'll disallow
16096 * indexes containing such columns in the first place, but they might
16097 * exist in older branches.)
16099 if (attno <= 0)
16100 ereport(ERROR,
16101 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16102 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16103 RelationGetRelationName(indexRel), attno)));
16105 attr = TupleDescAttr(rel->rd_att, attno - 1);
16106 if (!attr->attnotnull)
16107 ereport(ERROR,
16108 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16109 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16110 RelationGetRelationName(indexRel),
16111 NameStr(attr->attname))));
16114 /* This index is suitable for use as a replica identity. Mark it. */
16115 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16117 index_close(indexRel, NoLock);
16121 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16123 static void
16124 ATExecSetRowSecurity(Relation rel, bool rls)
16126 Relation pg_class;
16127 Oid relid;
16128 HeapTuple tuple;
16130 relid = RelationGetRelid(rel);
16132 /* Pull the record for this relation and update it */
16133 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16135 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16137 if (!HeapTupleIsValid(tuple))
16138 elog(ERROR, "cache lookup failed for relation %u", relid);
16140 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
16141 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16143 InvokeObjectPostAlterHook(RelationRelationId,
16144 RelationGetRelid(rel), 0);
16146 table_close(pg_class, RowExclusiveLock);
16147 heap_freetuple(tuple);
16151 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16153 static void
16154 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
16156 Relation pg_class;
16157 Oid relid;
16158 HeapTuple tuple;
16160 relid = RelationGetRelid(rel);
16162 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16164 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16166 if (!HeapTupleIsValid(tuple))
16167 elog(ERROR, "cache lookup failed for relation %u", relid);
16169 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
16170 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16172 InvokeObjectPostAlterHook(RelationRelationId,
16173 RelationGetRelid(rel), 0);
16175 table_close(pg_class, RowExclusiveLock);
16176 heap_freetuple(tuple);
16180 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16182 static void
16183 ATExecGenericOptions(Relation rel, List *options)
16185 Relation ftrel;
16186 ForeignServer *server;
16187 ForeignDataWrapper *fdw;
16188 HeapTuple tuple;
16189 bool isnull;
16190 Datum repl_val[Natts_pg_foreign_table];
16191 bool repl_null[Natts_pg_foreign_table];
16192 bool repl_repl[Natts_pg_foreign_table];
16193 Datum datum;
16194 Form_pg_foreign_table tableform;
16196 if (options == NIL)
16197 return;
16199 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
16201 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
16202 ObjectIdGetDatum(rel->rd_id));
16203 if (!HeapTupleIsValid(tuple))
16204 ereport(ERROR,
16205 (errcode(ERRCODE_UNDEFINED_OBJECT),
16206 errmsg("foreign table \"%s\" does not exist",
16207 RelationGetRelationName(rel))));
16208 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16209 server = GetForeignServer(tableform->ftserver);
16210 fdw = GetForeignDataWrapper(server->fdwid);
16212 memset(repl_val, 0, sizeof(repl_val));
16213 memset(repl_null, false, sizeof(repl_null));
16214 memset(repl_repl, false, sizeof(repl_repl));
16216 /* Extract the current options */
16217 datum = SysCacheGetAttr(FOREIGNTABLEREL,
16218 tuple,
16219 Anum_pg_foreign_table_ftoptions,
16220 &isnull);
16221 if (isnull)
16222 datum = PointerGetDatum(NULL);
16224 /* Transform the options */
16225 datum = transformGenericOptions(ForeignTableRelationId,
16226 datum,
16227 options,
16228 fdw->fdwvalidator);
16230 if (PointerIsValid(DatumGetPointer(datum)))
16231 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
16232 else
16233 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16235 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16237 /* Everything looks good - update the tuple */
16239 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16240 repl_val, repl_null, repl_repl);
16242 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
16245 * Invalidate relcache so that all sessions will refresh any cached plans
16246 * that might depend on the old options.
16248 CacheInvalidateRelcache(rel);
16250 InvokeObjectPostAlterHook(ForeignTableRelationId,
16251 RelationGetRelid(rel), 0);
16253 table_close(ftrel, RowExclusiveLock);
16255 heap_freetuple(tuple);
16259 * ALTER TABLE ALTER COLUMN SET COMPRESSION
16261 * Return value is the address of the modified column
16263 static ObjectAddress
16264 ATExecSetCompression(Relation rel,
16265 const char *column,
16266 Node *newValue,
16267 LOCKMODE lockmode)
16269 Relation attrel;
16270 HeapTuple tuple;
16271 Form_pg_attribute atttableform;
16272 AttrNumber attnum;
16273 char *compression;
16274 char cmethod;
16275 ObjectAddress address;
16277 compression = strVal(newValue);
16279 attrel = table_open(AttributeRelationId, RowExclusiveLock);
16281 /* copy the cache entry so we can scribble on it below */
16282 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
16283 if (!HeapTupleIsValid(tuple))
16284 ereport(ERROR,
16285 (errcode(ERRCODE_UNDEFINED_COLUMN),
16286 errmsg("column \"%s\" of relation \"%s\" does not exist",
16287 column, RelationGetRelationName(rel))));
16289 /* prevent them from altering a system attribute */
16290 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16291 attnum = atttableform->attnum;
16292 if (attnum <= 0)
16293 ereport(ERROR,
16294 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16295 errmsg("cannot alter system column \"%s\"", column)));
16298 * Check that column type is compressible, then get the attribute
16299 * compression method code
16301 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
16303 /* update pg_attribute entry */
16304 atttableform->attcompression = cmethod;
16305 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
16307 InvokeObjectPostAlterHook(RelationRelationId,
16308 RelationGetRelid(rel),
16309 attnum);
16312 * Apply the change to indexes as well (only for simple index columns,
16313 * matching behavior of index.c ConstructTupleDescriptor()).
16315 SetIndexStorageProperties(rel, attrel, attnum,
16316 false, 0,
16317 true, cmethod,
16318 lockmode);
16320 heap_freetuple(tuple);
16322 table_close(attrel, RowExclusiveLock);
16324 /* make changes visible */
16325 CommandCounterIncrement();
16327 ObjectAddressSubSet(address, RelationRelationId,
16328 RelationGetRelid(rel), attnum);
16329 return address;
16334 * Preparation phase for SET LOGGED/UNLOGGED
16336 * This verifies that we're not trying to change a temp table. Also,
16337 * existing foreign key constraints are checked to avoid ending up with
16338 * permanent tables referencing unlogged tables.
16340 * Return value is false if the operation is a no-op (in which case the
16341 * checks are skipped), otherwise true.
16343 static bool
16344 ATPrepChangePersistence(Relation rel, bool toLogged)
16346 Relation pg_constraint;
16347 HeapTuple tuple;
16348 SysScanDesc scan;
16349 ScanKeyData skey[1];
16352 * Disallow changing status for a temp table. Also verify whether we can
16353 * get away with doing nothing; in such cases we don't need to run the
16354 * checks below, either.
16356 switch (rel->rd_rel->relpersistence)
16358 case RELPERSISTENCE_TEMP:
16359 ereport(ERROR,
16360 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16361 errmsg("cannot change logged status of table \"%s\" because it is temporary",
16362 RelationGetRelationName(rel)),
16363 errtable(rel)));
16364 break;
16365 case RELPERSISTENCE_PERMANENT:
16366 if (toLogged)
16367 /* nothing to do */
16368 return false;
16369 break;
16370 case RELPERSISTENCE_UNLOGGED:
16371 if (!toLogged)
16372 /* nothing to do */
16373 return false;
16374 break;
16378 * Check that the table is not part of any publication when changing to
16379 * UNLOGGED, as UNLOGGED tables can't be published.
16381 if (!toLogged &&
16382 GetRelationPublications(RelationGetRelid(rel)) != NIL)
16383 ereport(ERROR,
16384 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16385 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16386 RelationGetRelationName(rel)),
16387 errdetail("Unlogged relations cannot be replicated.")));
16390 * Check existing foreign key constraints to preserve the invariant that
16391 * permanent tables cannot reference unlogged ones. Self-referencing
16392 * foreign keys can safely be ignored.
16394 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
16397 * Scan conrelid if changing to permanent, else confrelid. This also
16398 * determines whether a useful index exists.
16400 ScanKeyInit(&skey[0],
16401 toLogged ? Anum_pg_constraint_conrelid :
16402 Anum_pg_constraint_confrelid,
16403 BTEqualStrategyNumber, F_OIDEQ,
16404 ObjectIdGetDatum(RelationGetRelid(rel)));
16405 scan = systable_beginscan(pg_constraint,
16406 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
16407 true, NULL, 1, skey);
16409 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
16411 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
16413 if (con->contype == CONSTRAINT_FOREIGN)
16415 Oid foreignrelid;
16416 Relation foreignrel;
16418 /* the opposite end of what we used as scankey */
16419 foreignrelid = toLogged ? con->confrelid : con->conrelid;
16421 /* ignore if self-referencing */
16422 if (RelationGetRelid(rel) == foreignrelid)
16423 continue;
16425 foreignrel = relation_open(foreignrelid, AccessShareLock);
16427 if (toLogged)
16429 if (!RelationIsPermanent(foreignrel))
16430 ereport(ERROR,
16431 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16432 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
16433 RelationGetRelationName(rel),
16434 RelationGetRelationName(foreignrel)),
16435 errtableconstraint(rel, NameStr(con->conname))));
16437 else
16439 if (RelationIsPermanent(foreignrel))
16440 ereport(ERROR,
16441 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16442 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
16443 RelationGetRelationName(rel),
16444 RelationGetRelationName(foreignrel)),
16445 errtableconstraint(rel, NameStr(con->conname))));
16448 relation_close(foreignrel, AccessShareLock);
16452 systable_endscan(scan);
16454 table_close(pg_constraint, AccessShareLock);
16456 return true;
16460 * Execute ALTER TABLE SET SCHEMA
16462 ObjectAddress
16463 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
16465 Relation rel;
16466 Oid relid;
16467 Oid oldNspOid;
16468 Oid nspOid;
16469 RangeVar *newrv;
16470 ObjectAddresses *objsMoved;
16471 ObjectAddress myself;
16473 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
16474 stmt->missing_ok ? RVR_MISSING_OK : 0,
16475 RangeVarCallbackForAlterRelation,
16476 (void *) stmt);
16478 if (!OidIsValid(relid))
16480 ereport(NOTICE,
16481 (errmsg("relation \"%s\" does not exist, skipping",
16482 stmt->relation->relname)));
16483 return InvalidObjectAddress;
16486 rel = relation_open(relid, NoLock);
16488 oldNspOid = RelationGetNamespace(rel);
16490 /* If it's an owned sequence, disallow moving it by itself. */
16491 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
16493 Oid tableId;
16494 int32 colId;
16496 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
16497 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
16498 ereport(ERROR,
16499 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16500 errmsg("cannot move an owned sequence into another schema"),
16501 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16502 RelationGetRelationName(rel),
16503 get_rel_name(tableId))));
16506 /* Get and lock schema OID and check its permissions. */
16507 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
16508 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
16510 /* common checks on switching namespaces */
16511 CheckSetNamespace(oldNspOid, nspOid);
16513 objsMoved = new_object_addresses();
16514 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
16515 free_object_addresses(objsMoved);
16517 ObjectAddressSet(myself, RelationRelationId, relid);
16519 if (oldschema)
16520 *oldschema = oldNspOid;
16522 /* close rel, but keep lock until commit */
16523 relation_close(rel, NoLock);
16525 return myself;
16529 * The guts of relocating a table or materialized view to another namespace:
16530 * besides moving the relation itself, its dependent objects are relocated to
16531 * the new schema.
16533 void
16534 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
16535 ObjectAddresses *objsMoved)
16537 Relation classRel;
16539 Assert(objsMoved != NULL);
16541 /* OK, modify the pg_class row and pg_depend entry */
16542 classRel = table_open(RelationRelationId, RowExclusiveLock);
16544 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
16545 nspOid, true, objsMoved);
16547 /* Fix the table's row type too, if it has one */
16548 if (OidIsValid(rel->rd_rel->reltype))
16549 AlterTypeNamespaceInternal(rel->rd_rel->reltype,
16550 nspOid, false, false, objsMoved);
16552 /* Fix other dependent stuff */
16553 if (rel->rd_rel->relkind == RELKIND_RELATION ||
16554 rel->rd_rel->relkind == RELKIND_MATVIEW ||
16555 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16557 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
16558 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
16559 objsMoved, AccessExclusiveLock);
16560 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
16561 false, objsMoved);
16564 table_close(classRel, RowExclusiveLock);
16568 * The guts of relocating a relation to another namespace: fix the pg_class
16569 * entry, and the pg_depend entry if any. Caller must already have
16570 * opened and write-locked pg_class.
16572 void
16573 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
16574 Oid oldNspOid, Oid newNspOid,
16575 bool hasDependEntry,
16576 ObjectAddresses *objsMoved)
16578 HeapTuple classTup;
16579 Form_pg_class classForm;
16580 ObjectAddress thisobj;
16581 bool already_done = false;
16583 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
16584 if (!HeapTupleIsValid(classTup))
16585 elog(ERROR, "cache lookup failed for relation %u", relOid);
16586 classForm = (Form_pg_class) GETSTRUCT(classTup);
16588 Assert(classForm->relnamespace == oldNspOid);
16590 thisobj.classId = RelationRelationId;
16591 thisobj.objectId = relOid;
16592 thisobj.objectSubId = 0;
16595 * If the object has already been moved, don't move it again. If it's
16596 * already in the right place, don't move it, but still fire the object
16597 * access hook.
16599 already_done = object_address_present(&thisobj, objsMoved);
16600 if (!already_done && oldNspOid != newNspOid)
16602 /* check for duplicate name (more friendly than unique-index failure) */
16603 if (get_relname_relid(NameStr(classForm->relname),
16604 newNspOid) != InvalidOid)
16605 ereport(ERROR,
16606 (errcode(ERRCODE_DUPLICATE_TABLE),
16607 errmsg("relation \"%s\" already exists in schema \"%s\"",
16608 NameStr(classForm->relname),
16609 get_namespace_name(newNspOid))));
16611 /* classTup is a copy, so OK to scribble on */
16612 classForm->relnamespace = newNspOid;
16614 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
16616 /* Update dependency on schema if caller said so */
16617 if (hasDependEntry &&
16618 changeDependencyFor(RelationRelationId,
16619 relOid,
16620 NamespaceRelationId,
16621 oldNspOid,
16622 newNspOid) != 1)
16623 elog(ERROR, "could not change schema dependency for relation \"%s\"",
16624 NameStr(classForm->relname));
16626 if (!already_done)
16628 add_exact_object_address(&thisobj, objsMoved);
16630 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
16633 heap_freetuple(classTup);
16637 * Move all indexes for the specified relation to another namespace.
16639 * Note: we assume adequate permission checking was done by the caller,
16640 * and that the caller has a suitable lock on the owning relation.
16642 static void
16643 AlterIndexNamespaces(Relation classRel, Relation rel,
16644 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
16646 List *indexList;
16647 ListCell *l;
16649 indexList = RelationGetIndexList(rel);
16651 foreach(l, indexList)
16653 Oid indexOid = lfirst_oid(l);
16654 ObjectAddress thisobj;
16656 thisobj.classId = RelationRelationId;
16657 thisobj.objectId = indexOid;
16658 thisobj.objectSubId = 0;
16661 * Note: currently, the index will not have its own dependency on the
16662 * namespace, so we don't need to do changeDependencyFor(). There's no
16663 * row type in pg_type, either.
16665 * XXX this objsMoved test may be pointless -- surely we have a single
16666 * dependency link from a relation to each index?
16668 if (!object_address_present(&thisobj, objsMoved))
16670 AlterRelationNamespaceInternal(classRel, indexOid,
16671 oldNspOid, newNspOid,
16672 false, objsMoved);
16673 add_exact_object_address(&thisobj, objsMoved);
16677 list_free(indexList);
16681 * Move all identity and SERIAL-column sequences of the specified relation to another
16682 * namespace.
16684 * Note: we assume adequate permission checking was done by the caller,
16685 * and that the caller has a suitable lock on the owning relation.
16687 static void
16688 AlterSeqNamespaces(Relation classRel, Relation rel,
16689 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
16690 LOCKMODE lockmode)
16692 Relation depRel;
16693 SysScanDesc scan;
16694 ScanKeyData key[2];
16695 HeapTuple tup;
16698 * SERIAL sequences are those having an auto dependency on one of the
16699 * table's columns (we don't care *which* column, exactly).
16701 depRel = table_open(DependRelationId, AccessShareLock);
16703 ScanKeyInit(&key[0],
16704 Anum_pg_depend_refclassid,
16705 BTEqualStrategyNumber, F_OIDEQ,
16706 ObjectIdGetDatum(RelationRelationId));
16707 ScanKeyInit(&key[1],
16708 Anum_pg_depend_refobjid,
16709 BTEqualStrategyNumber, F_OIDEQ,
16710 ObjectIdGetDatum(RelationGetRelid(rel)));
16711 /* we leave refobjsubid unspecified */
16713 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
16714 NULL, 2, key);
16716 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16718 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
16719 Relation seqRel;
16721 /* skip dependencies other than auto dependencies on columns */
16722 if (depForm->refobjsubid == 0 ||
16723 depForm->classid != RelationRelationId ||
16724 depForm->objsubid != 0 ||
16725 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16726 continue;
16728 /* Use relation_open just in case it's an index */
16729 seqRel = relation_open(depForm->objid, lockmode);
16731 /* skip non-sequence relations */
16732 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16734 /* No need to keep the lock */
16735 relation_close(seqRel, lockmode);
16736 continue;
16739 /* Fix the pg_class and pg_depend entries */
16740 AlterRelationNamespaceInternal(classRel, depForm->objid,
16741 oldNspOid, newNspOid,
16742 true, objsMoved);
16745 * Sequences used to have entries in pg_type, but no longer do. If we
16746 * ever re-instate that, we'll need to move the pg_type entry to the
16747 * new namespace, too (using AlterTypeNamespaceInternal).
16749 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
16751 /* Now we can close it. Keep the lock till end of transaction. */
16752 relation_close(seqRel, NoLock);
16755 systable_endscan(scan);
16757 relation_close(depRel, AccessShareLock);
16762 * This code supports
16763 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
16765 * Because we only support this for TEMP tables, it's sufficient to remember
16766 * the state in a backend-local data structure.
16770 * Register a newly-created relation's ON COMMIT action.
16772 void
16773 register_on_commit_action(Oid relid, OnCommitAction action)
16775 OnCommitItem *oc;
16776 MemoryContext oldcxt;
16779 * We needn't bother registering the relation unless there is an ON COMMIT
16780 * action we need to take.
16782 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
16783 return;
16785 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
16787 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
16788 oc->relid = relid;
16789 oc->oncommit = action;
16790 oc->creating_subid = GetCurrentSubTransactionId();
16791 oc->deleting_subid = InvalidSubTransactionId;
16794 * We use lcons() here so that ON COMMIT actions are processed in reverse
16795 * order of registration. That might not be essential but it seems
16796 * reasonable.
16798 on_commits = lcons(oc, on_commits);
16800 MemoryContextSwitchTo(oldcxt);
16804 * Unregister any ON COMMIT action when a relation is deleted.
16806 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
16808 void
16809 remove_on_commit_action(Oid relid)
16811 ListCell *l;
16813 foreach(l, on_commits)
16815 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16817 if (oc->relid == relid)
16819 oc->deleting_subid = GetCurrentSubTransactionId();
16820 break;
16826 * Perform ON COMMIT actions.
16828 * This is invoked just before actually committing, since it's possible
16829 * to encounter errors.
16831 void
16832 PreCommit_on_commit_actions(void)
16834 ListCell *l;
16835 List *oids_to_truncate = NIL;
16836 List *oids_to_drop = NIL;
16838 foreach(l, on_commits)
16840 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16842 /* Ignore entry if already dropped in this xact */
16843 if (oc->deleting_subid != InvalidSubTransactionId)
16844 continue;
16846 switch (oc->oncommit)
16848 case ONCOMMIT_NOOP:
16849 case ONCOMMIT_PRESERVE_ROWS:
16850 /* Do nothing (there shouldn't be such entries, actually) */
16851 break;
16852 case ONCOMMIT_DELETE_ROWS:
16855 * If this transaction hasn't accessed any temporary
16856 * relations, we can skip truncating ON COMMIT DELETE ROWS
16857 * tables, as they must still be empty.
16859 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
16860 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
16861 break;
16862 case ONCOMMIT_DROP:
16863 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
16864 break;
16869 * Truncate relations before dropping so that all dependencies between
16870 * relations are removed after they are worked on. Doing it like this
16871 * might be a waste as it is possible that a relation being truncated will
16872 * be dropped anyway due to its parent being dropped, but this makes the
16873 * code more robust because of not having to re-check that the relation
16874 * exists at truncation time.
16876 if (oids_to_truncate != NIL)
16877 heap_truncate(oids_to_truncate);
16879 if (oids_to_drop != NIL)
16881 ObjectAddresses *targetObjects = new_object_addresses();
16883 foreach(l, oids_to_drop)
16885 ObjectAddress object;
16887 object.classId = RelationRelationId;
16888 object.objectId = lfirst_oid(l);
16889 object.objectSubId = 0;
16891 Assert(!object_address_present(&object, targetObjects));
16893 add_exact_object_address(&object, targetObjects);
16897 * Since this is an automatic drop, rather than one directly initiated
16898 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
16900 performMultipleDeletions(targetObjects, DROP_CASCADE,
16901 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
16903 #ifdef USE_ASSERT_CHECKING
16906 * Note that table deletion will call remove_on_commit_action, so the
16907 * entry should get marked as deleted.
16909 foreach(l, on_commits)
16911 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
16913 if (oc->oncommit != ONCOMMIT_DROP)
16914 continue;
16916 Assert(oc->deleting_subid != InvalidSubTransactionId);
16918 #endif
16923 * Post-commit or post-abort cleanup for ON COMMIT management.
16925 * All we do here is remove no-longer-needed OnCommitItem entries.
16927 * During commit, remove entries that were deleted during this transaction;
16928 * during abort, remove those created during this transaction.
16930 void
16931 AtEOXact_on_commit_actions(bool isCommit)
16933 ListCell *cur_item;
16935 foreach(cur_item, on_commits)
16937 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
16939 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
16940 oc->creating_subid != InvalidSubTransactionId)
16942 /* cur_item must be removed */
16943 on_commits = foreach_delete_current(on_commits, cur_item);
16944 pfree(oc);
16946 else
16948 /* cur_item must be preserved */
16949 oc->creating_subid = InvalidSubTransactionId;
16950 oc->deleting_subid = InvalidSubTransactionId;
16956 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
16958 * During subabort, we can immediately remove entries created during this
16959 * subtransaction. During subcommit, just relabel entries marked during
16960 * this subtransaction as being the parent's responsibility.
16962 void
16963 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
16964 SubTransactionId parentSubid)
16966 ListCell *cur_item;
16968 foreach(cur_item, on_commits)
16970 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
16972 if (!isCommit && oc->creating_subid == mySubid)
16974 /* cur_item must be removed */
16975 on_commits = foreach_delete_current(on_commits, cur_item);
16976 pfree(oc);
16978 else
16980 /* cur_item must be preserved */
16981 if (oc->creating_subid == mySubid)
16982 oc->creating_subid = parentSubid;
16983 if (oc->deleting_subid == mySubid)
16984 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
16990 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
16991 * the relation to be locked only if (1) it's a plain or partitioned table,
16992 * materialized view, or TOAST table and (2) the current user is the owner (or
16993 * the superuser). This meets the permission-checking needs of CLUSTER,
16994 * REINDEX TABLE, and REFRESH MATERIALIZED VIEW; we expose it here so that it
16995 * can be used by all.
16997 void
16998 RangeVarCallbackOwnsTable(const RangeVar *relation,
16999 Oid relId, Oid oldRelId, void *arg)
17001 char relkind;
17003 /* Nothing to do if the relation was not found. */
17004 if (!OidIsValid(relId))
17005 return;
17008 * If the relation does exist, check whether it's an index. But note that
17009 * the relation might have been dropped between the time we did the name
17010 * lookup and now. In that case, there's nothing to do.
17012 relkind = get_rel_relkind(relId);
17013 if (!relkind)
17014 return;
17015 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
17016 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
17017 ereport(ERROR,
17018 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17019 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
17021 /* Check permissions */
17022 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17023 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)), relation->relname);
17027 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17029 static void
17030 RangeVarCallbackForTruncate(const RangeVar *relation,
17031 Oid relId, Oid oldRelId, void *arg)
17033 HeapTuple tuple;
17035 /* Nothing to do if the relation was not found. */
17036 if (!OidIsValid(relId))
17037 return;
17039 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17040 if (!HeapTupleIsValid(tuple)) /* should not happen */
17041 elog(ERROR, "cache lookup failed for relation %u", relId);
17043 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
17044 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17046 ReleaseSysCache(tuple);
17050 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17051 * the owner of the relation, or superuser.
17053 void
17054 RangeVarCallbackOwnsRelation(const RangeVar *relation,
17055 Oid relId, Oid oldRelId, void *arg)
17057 HeapTuple tuple;
17059 /* Nothing to do if the relation was not found. */
17060 if (!OidIsValid(relId))
17061 return;
17063 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17064 if (!HeapTupleIsValid(tuple)) /* should not happen */
17065 elog(ERROR, "cache lookup failed for relation %u", relId);
17067 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17068 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
17069 relation->relname);
17071 if (!allowSystemTableMods &&
17072 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
17073 ereport(ERROR,
17074 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17075 errmsg("permission denied: \"%s\" is a system catalog",
17076 relation->relname)));
17078 ReleaseSysCache(tuple);
17082 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17083 * processing.
17085 static void
17086 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
17087 void *arg)
17089 Node *stmt = (Node *) arg;
17090 ObjectType reltype;
17091 HeapTuple tuple;
17092 Form_pg_class classform;
17093 AclResult aclresult;
17094 char relkind;
17096 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
17097 if (!HeapTupleIsValid(tuple))
17098 return; /* concurrently dropped */
17099 classform = (Form_pg_class) GETSTRUCT(tuple);
17100 relkind = classform->relkind;
17102 /* Must own relation. */
17103 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
17104 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
17106 /* No system table modifications unless explicitly allowed. */
17107 if (!allowSystemTableMods && IsSystemClass(relid, classform))
17108 ereport(ERROR,
17109 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17110 errmsg("permission denied: \"%s\" is a system catalog",
17111 rv->relname)));
17114 * Extract the specified relation type from the statement parse tree.
17116 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17117 * have CREATE rights on the containing namespace.
17119 if (IsA(stmt, RenameStmt))
17121 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17122 GetUserId(), ACL_CREATE);
17123 if (aclresult != ACLCHECK_OK)
17124 aclcheck_error(aclresult, OBJECT_SCHEMA,
17125 get_namespace_name(classform->relnamespace));
17126 reltype = ((RenameStmt *) stmt)->renameType;
17128 else if (IsA(stmt, AlterObjectSchemaStmt))
17129 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17131 else if (IsA(stmt, AlterTableStmt))
17132 reltype = ((AlterTableStmt *) stmt)->objtype;
17133 else
17135 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17136 reltype = OBJECT_TABLE; /* placate compiler */
17140 * For compatibility with prior releases, we allow ALTER TABLE to be used
17141 * with most other types of relations (but not composite types). We allow
17142 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17143 * otherwise. Otherwise, the user must select the correct form of the
17144 * command for the relation at issue.
17146 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
17147 ereport(ERROR,
17148 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17149 errmsg("\"%s\" is not a sequence", rv->relname)));
17151 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
17152 ereport(ERROR,
17153 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17154 errmsg("\"%s\" is not a view", rv->relname)));
17156 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
17157 ereport(ERROR,
17158 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17159 errmsg("\"%s\" is not a materialized view", rv->relname)));
17161 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
17162 ereport(ERROR,
17163 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17164 errmsg("\"%s\" is not a foreign table", rv->relname)));
17166 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
17167 ereport(ERROR,
17168 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17169 errmsg("\"%s\" is not a composite type", rv->relname)));
17171 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17172 relkind != RELKIND_PARTITIONED_INDEX
17173 && !IsA(stmt, RenameStmt))
17174 ereport(ERROR,
17175 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17176 errmsg("\"%s\" is not an index", rv->relname)));
17179 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17180 * TYPE for that.
17182 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
17183 ereport(ERROR,
17184 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17185 errmsg("\"%s\" is a composite type", rv->relname),
17186 errhint("Use ALTER TYPE instead.")));
17189 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17190 * to a different schema, such as indexes and TOAST tables.
17192 if (IsA(stmt, AlterObjectSchemaStmt))
17194 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
17195 ereport(ERROR,
17196 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17197 errmsg("cannot change schema of index \"%s\"",
17198 rv->relname),
17199 errhint("Change the schema of the table instead.")));
17200 else if (relkind == RELKIND_COMPOSITE_TYPE)
17201 ereport(ERROR,
17202 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17203 errmsg("cannot change schema of composite type \"%s\"",
17204 rv->relname),
17205 errhint("Use ALTER TYPE instead.")));
17206 else if (relkind == RELKIND_TOASTVALUE)
17207 ereport(ERROR,
17208 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17209 errmsg("cannot change schema of TOAST table \"%s\"",
17210 rv->relname),
17211 errhint("Change the schema of the table instead.")));
17214 ReleaseSysCache(tuple);
17218 * Transform any expressions present in the partition key
17220 * Returns a transformed PartitionSpec.
17222 static PartitionSpec *
17223 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
17225 PartitionSpec *newspec;
17226 ParseState *pstate;
17227 ParseNamespaceItem *nsitem;
17228 ListCell *l;
17230 newspec = makeNode(PartitionSpec);
17232 newspec->strategy = partspec->strategy;
17233 newspec->partParams = NIL;
17234 newspec->location = partspec->location;
17236 /* Check valid number of columns for strategy */
17237 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
17238 list_length(partspec->partParams) != 1)
17239 ereport(ERROR,
17240 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17241 errmsg("cannot use \"list\" partition strategy with more than one column")));
17244 * Create a dummy ParseState and insert the target relation as its sole
17245 * rangetable entry. We need a ParseState for transformExpr.
17247 pstate = make_parsestate(NULL);
17248 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
17249 NULL, false, true);
17250 addNSItemToQuery(pstate, nsitem, true, true, true);
17252 /* take care of any partition expressions */
17253 foreach(l, partspec->partParams)
17255 PartitionElem *pelem = lfirst_node(PartitionElem, l);
17257 if (pelem->expr)
17259 /* Copy, to avoid scribbling on the input */
17260 pelem = copyObject(pelem);
17262 /* Now do parse transformation of the expression */
17263 pelem->expr = transformExpr(pstate, pelem->expr,
17264 EXPR_KIND_PARTITION_EXPRESSION);
17266 /* we have to fix its collations too */
17267 assign_expr_collations(pstate, pelem->expr);
17270 newspec->partParams = lappend(newspec->partParams, pelem);
17273 return newspec;
17277 * Compute per-partition-column information from a list of PartitionElems.
17278 * Expressions in the PartitionElems must be parse-analyzed already.
17280 static void
17281 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
17282 List **partexprs, Oid *partopclass, Oid *partcollation,
17283 PartitionStrategy strategy)
17285 int attn;
17286 ListCell *lc;
17287 Oid am_oid;
17289 attn = 0;
17290 foreach(lc, partParams)
17292 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
17293 Oid atttype;
17294 Oid attcollation;
17296 if (pelem->name != NULL)
17298 /* Simple attribute reference */
17299 HeapTuple atttuple;
17300 Form_pg_attribute attform;
17302 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
17303 pelem->name);
17304 if (!HeapTupleIsValid(atttuple))
17305 ereport(ERROR,
17306 (errcode(ERRCODE_UNDEFINED_COLUMN),
17307 errmsg("column \"%s\" named in partition key does not exist",
17308 pelem->name),
17309 parser_errposition(pstate, pelem->location)));
17310 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
17312 if (attform->attnum <= 0)
17313 ereport(ERROR,
17314 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17315 errmsg("cannot use system column \"%s\" in partition key",
17316 pelem->name),
17317 parser_errposition(pstate, pelem->location)));
17320 * Generated columns cannot work: They are computed after BEFORE
17321 * triggers, but partition routing is done before all triggers.
17323 if (attform->attgenerated)
17324 ereport(ERROR,
17325 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17326 errmsg("cannot use generated column in partition key"),
17327 errdetail("Column \"%s\" is a generated column.",
17328 pelem->name),
17329 parser_errposition(pstate, pelem->location)));
17331 partattrs[attn] = attform->attnum;
17332 atttype = attform->atttypid;
17333 attcollation = attform->attcollation;
17334 ReleaseSysCache(atttuple);
17336 else
17338 /* Expression */
17339 Node *expr = pelem->expr;
17340 char partattname[16];
17342 Assert(expr != NULL);
17343 atttype = exprType(expr);
17344 attcollation = exprCollation(expr);
17347 * The expression must be of a storable type (e.g., not RECORD).
17348 * The test is the same as for whether a table column is of a safe
17349 * type (which is why we needn't check for the non-expression
17350 * case).
17352 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
17353 CheckAttributeType(partattname,
17354 atttype, attcollation,
17355 NIL, CHKATYPE_IS_PARTKEY);
17358 * Strip any top-level COLLATE clause. This ensures that we treat
17359 * "x COLLATE y" and "(x COLLATE y)" alike.
17361 while (IsA(expr, CollateExpr))
17362 expr = (Node *) ((CollateExpr *) expr)->arg;
17364 if (IsA(expr, Var) &&
17365 ((Var *) expr)->varattno > 0)
17368 * User wrote "(column)" or "(column COLLATE something)".
17369 * Treat it like simple attribute anyway.
17371 partattrs[attn] = ((Var *) expr)->varattno;
17373 else
17375 Bitmapset *expr_attrs = NULL;
17376 int i;
17378 partattrs[attn] = 0; /* marks the column as expression */
17379 *partexprs = lappend(*partexprs, expr);
17382 * Try to simplify the expression before checking for
17383 * mutability. The main practical value of doing it in this
17384 * order is that an inline-able SQL-language function will be
17385 * accepted if its expansion is immutable, whether or not the
17386 * function itself is marked immutable.
17388 * Note that expression_planner does not change the passed in
17389 * expression destructively and we have already saved the
17390 * expression to be stored into the catalog above.
17392 expr = (Node *) expression_planner((Expr *) expr);
17395 * Partition expression cannot contain mutable functions,
17396 * because a given row must always map to the same partition
17397 * as long as there is no change in the partition boundary
17398 * structure.
17400 if (contain_mutable_functions(expr))
17401 ereport(ERROR,
17402 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17403 errmsg("functions in partition key expression must be marked IMMUTABLE")));
17406 * transformPartitionSpec() should have already rejected
17407 * subqueries, aggregates, window functions, and SRFs, based
17408 * on the EXPR_KIND_ for partition expressions.
17412 * Cannot allow system column references, since that would
17413 * make partition routing impossible: their values won't be
17414 * known yet when we need to do that.
17416 pull_varattnos(expr, 1, &expr_attrs);
17417 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
17419 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
17420 expr_attrs))
17421 ereport(ERROR,
17422 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17423 errmsg("partition key expressions cannot contain system column references")));
17427 * Generated columns cannot work: They are computed after
17428 * BEFORE triggers, but partition routing is done before all
17429 * triggers.
17431 i = -1;
17432 while ((i = bms_next_member(expr_attrs, i)) >= 0)
17434 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
17436 if (attno > 0 &&
17437 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
17438 ereport(ERROR,
17439 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17440 errmsg("cannot use generated column in partition key"),
17441 errdetail("Column \"%s\" is a generated column.",
17442 get_attname(RelationGetRelid(rel), attno, false)),
17443 parser_errposition(pstate, pelem->location)));
17447 * While it is not exactly *wrong* for a partition expression
17448 * to be a constant, it seems better to reject such keys.
17450 if (IsA(expr, Const))
17451 ereport(ERROR,
17452 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17453 errmsg("cannot use constant expression as partition key")));
17458 * Apply collation override if any
17460 if (pelem->collation)
17461 attcollation = get_collation_oid(pelem->collation, false);
17464 * Check we have a collation iff it's a collatable type. The only
17465 * expected failures here are (1) COLLATE applied to a noncollatable
17466 * type, or (2) partition expression had an unresolved collation. But
17467 * we might as well code this to be a complete consistency check.
17469 if (type_is_collatable(atttype))
17471 if (!OidIsValid(attcollation))
17472 ereport(ERROR,
17473 (errcode(ERRCODE_INDETERMINATE_COLLATION),
17474 errmsg("could not determine which collation to use for partition expression"),
17475 errhint("Use the COLLATE clause to set the collation explicitly.")));
17477 else
17479 if (OidIsValid(attcollation))
17480 ereport(ERROR,
17481 (errcode(ERRCODE_DATATYPE_MISMATCH),
17482 errmsg("collations are not supported by type %s",
17483 format_type_be(atttype))));
17486 partcollation[attn] = attcollation;
17489 * Identify the appropriate operator class. For list and range
17490 * partitioning, we use a btree operator class; hash partitioning uses
17491 * a hash operator class.
17493 if (strategy == PARTITION_STRATEGY_HASH)
17494 am_oid = HASH_AM_OID;
17495 else
17496 am_oid = BTREE_AM_OID;
17498 if (!pelem->opclass)
17500 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
17502 if (!OidIsValid(partopclass[attn]))
17504 if (strategy == PARTITION_STRATEGY_HASH)
17505 ereport(ERROR,
17506 (errcode(ERRCODE_UNDEFINED_OBJECT),
17507 errmsg("data type %s has no default operator class for access method \"%s\"",
17508 format_type_be(atttype), "hash"),
17509 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
17510 else
17511 ereport(ERROR,
17512 (errcode(ERRCODE_UNDEFINED_OBJECT),
17513 errmsg("data type %s has no default operator class for access method \"%s\"",
17514 format_type_be(atttype), "btree"),
17515 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
17518 else
17519 partopclass[attn] = ResolveOpClass(pelem->opclass,
17520 atttype,
17521 am_oid == HASH_AM_OID ? "hash" : "btree",
17522 am_oid);
17524 attn++;
17529 * PartConstraintImpliedByRelConstraint
17530 * Do scanrel's existing constraints imply the partition constraint?
17532 * "Existing constraints" include its check constraints and column-level
17533 * NOT NULL constraints. partConstraint describes the partition constraint,
17534 * in implicit-AND form.
17536 bool
17537 PartConstraintImpliedByRelConstraint(Relation scanrel,
17538 List *partConstraint)
17540 List *existConstraint = NIL;
17541 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
17542 int i;
17544 if (constr && constr->has_not_null)
17546 int natts = scanrel->rd_att->natts;
17548 for (i = 1; i <= natts; i++)
17550 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
17552 if (att->attnotnull && !att->attisdropped)
17554 NullTest *ntest = makeNode(NullTest);
17556 ntest->arg = (Expr *) makeVar(1,
17558 att->atttypid,
17559 att->atttypmod,
17560 att->attcollation,
17562 ntest->nulltesttype = IS_NOT_NULL;
17565 * argisrow=false is correct even for a composite column,
17566 * because attnotnull does not represent a SQL-spec IS NOT
17567 * NULL test in such a case, just IS DISTINCT FROM NULL.
17569 ntest->argisrow = false;
17570 ntest->location = -1;
17571 existConstraint = lappend(existConstraint, ntest);
17576 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
17580 * ConstraintImpliedByRelConstraint
17581 * Do scanrel's existing constraints imply the given constraint?
17583 * testConstraint is the constraint to validate. provenConstraint is a
17584 * caller-provided list of conditions which this function may assume
17585 * to be true. Both provenConstraint and testConstraint must be in
17586 * implicit-AND form, must only contain immutable clauses, and must
17587 * contain only Vars with varno = 1.
17589 bool
17590 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
17592 List *existConstraint = list_copy(provenConstraint);
17593 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
17594 int num_check,
17597 num_check = (constr != NULL) ? constr->num_check : 0;
17598 for (i = 0; i < num_check; i++)
17600 Node *cexpr;
17603 * If this constraint hasn't been fully validated yet, we must ignore
17604 * it here.
17606 if (!constr->check[i].ccvalid)
17607 continue;
17609 cexpr = stringToNode(constr->check[i].ccbin);
17612 * Run each expression through const-simplification and
17613 * canonicalization. It is necessary, because we will be comparing it
17614 * to similarly-processed partition constraint expressions, and may
17615 * fail to detect valid matches without this.
17617 cexpr = eval_const_expressions(NULL, cexpr);
17618 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
17620 existConstraint = list_concat(existConstraint,
17621 make_ands_implicit((Expr *) cexpr));
17625 * Try to make the proof. Since we are comparing CHECK constraints, we
17626 * need to use weak implication, i.e., we assume existConstraint is
17627 * not-false and try to prove the same for testConstraint.
17629 * Note that predicate_implied_by assumes its first argument is known
17630 * immutable. That should always be true for both NOT NULL and partition
17631 * constraints, so we don't test it here.
17633 return predicate_implied_by(testConstraint, existConstraint, true);
17637 * QueuePartitionConstraintValidation
17639 * Add an entry to wqueue to have the given partition constraint validated by
17640 * Phase 3, for the given relation, and all its children.
17642 * We first verify whether the given constraint is implied by pre-existing
17643 * relation constraints; if it is, there's no need to scan the table to
17644 * validate, so don't queue in that case.
17646 static void
17647 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
17648 List *partConstraint,
17649 bool validate_default)
17652 * Based on the table's existing constraints, determine whether or not we
17653 * may skip scanning the table.
17655 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
17657 if (!validate_default)
17658 ereport(DEBUG1,
17659 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
17660 RelationGetRelationName(scanrel))));
17661 else
17662 ereport(DEBUG1,
17663 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
17664 RelationGetRelationName(scanrel))));
17665 return;
17669 * Constraints proved insufficient. For plain relations, queue a
17670 * validation item now; for partitioned tables, recurse to process each
17671 * partition.
17673 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
17675 AlteredTableInfo *tab;
17677 /* Grab a work queue entry. */
17678 tab = ATGetQueueEntry(wqueue, scanrel);
17679 Assert(tab->partition_constraint == NULL);
17680 tab->partition_constraint = (Expr *) linitial(partConstraint);
17681 tab->validate_default = validate_default;
17683 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17685 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
17686 int i;
17688 for (i = 0; i < partdesc->nparts; i++)
17690 Relation part_rel;
17691 List *thisPartConstraint;
17694 * This is the minimum lock we need to prevent deadlocks.
17696 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
17699 * Adjust the constraint for scanrel so that it matches this
17700 * partition's attribute numbers.
17702 thisPartConstraint =
17703 map_partition_varattnos(partConstraint, 1,
17704 part_rel, scanrel);
17706 QueuePartitionConstraintValidation(wqueue, part_rel,
17707 thisPartConstraint,
17708 validate_default);
17709 table_close(part_rel, NoLock); /* keep lock till commit */
17715 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
17717 * Return the address of the newly attached partition.
17719 static ObjectAddress
17720 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
17721 AlterTableUtilityContext *context)
17723 Relation attachrel,
17724 catalog;
17725 List *attachrel_children;
17726 List *partConstraint;
17727 SysScanDesc scan;
17728 ScanKeyData skey;
17729 AttrNumber attno;
17730 int natts;
17731 TupleDesc tupleDesc;
17732 ObjectAddress address;
17733 const char *trigger_name;
17734 Oid defaultPartOid;
17735 List *partBoundConstraint;
17736 ParseState *pstate = make_parsestate(NULL);
17738 pstate->p_sourcetext = context->queryString;
17741 * We must lock the default partition if one exists, because attaching a
17742 * new partition will change its partition constraint.
17744 defaultPartOid =
17745 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
17746 if (OidIsValid(defaultPartOid))
17747 LockRelationOid(defaultPartOid, AccessExclusiveLock);
17749 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
17752 * XXX I think it'd be a good idea to grab locks on all tables referenced
17753 * by FKs at this point also.
17757 * Must be owner of both parent and source table -- parent was checked by
17758 * ATSimplePermissions call in ATPrepCmd
17760 ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
17762 /* A partition can only have one parent */
17763 if (attachrel->rd_rel->relispartition)
17764 ereport(ERROR,
17765 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17766 errmsg("\"%s\" is already a partition",
17767 RelationGetRelationName(attachrel))));
17769 if (OidIsValid(attachrel->rd_rel->reloftype))
17770 ereport(ERROR,
17771 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17772 errmsg("cannot attach a typed table as partition")));
17775 * Table being attached should not already be part of inheritance; either
17776 * as a child table...
17778 catalog = table_open(InheritsRelationId, AccessShareLock);
17779 ScanKeyInit(&skey,
17780 Anum_pg_inherits_inhrelid,
17781 BTEqualStrategyNumber, F_OIDEQ,
17782 ObjectIdGetDatum(RelationGetRelid(attachrel)));
17783 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
17784 NULL, 1, &skey);
17785 if (HeapTupleIsValid(systable_getnext(scan)))
17786 ereport(ERROR,
17787 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17788 errmsg("cannot attach inheritance child as partition")));
17789 systable_endscan(scan);
17791 /* ...or as a parent table (except the case when it is partitioned) */
17792 ScanKeyInit(&skey,
17793 Anum_pg_inherits_inhparent,
17794 BTEqualStrategyNumber, F_OIDEQ,
17795 ObjectIdGetDatum(RelationGetRelid(attachrel)));
17796 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
17797 1, &skey);
17798 if (HeapTupleIsValid(systable_getnext(scan)) &&
17799 attachrel->rd_rel->relkind == RELKIND_RELATION)
17800 ereport(ERROR,
17801 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17802 errmsg("cannot attach inheritance parent as partition")));
17803 systable_endscan(scan);
17804 table_close(catalog, AccessShareLock);
17807 * Prevent circularity by seeing if rel is a partition of attachrel. (In
17808 * particular, this disallows making a rel a partition of itself.)
17810 * We do that by checking if rel is a member of the list of attachrel's
17811 * partitions provided the latter is partitioned at all. We want to avoid
17812 * having to construct this list again, so we request the strongest lock
17813 * on all partitions. We need the strongest lock, because we may decide
17814 * to scan them if we find out that the table being attached (or its leaf
17815 * partitions) may contain rows that violate the partition constraint. If
17816 * the table has a constraint that would prevent such rows, which by
17817 * definition is present in all the partitions, we need not scan the
17818 * table, nor its partitions. But we cannot risk a deadlock by taking a
17819 * weaker lock now and the stronger one only when needed.
17821 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
17822 AccessExclusiveLock, NULL);
17823 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
17824 ereport(ERROR,
17825 (errcode(ERRCODE_DUPLICATE_TABLE),
17826 errmsg("circular inheritance not allowed"),
17827 errdetail("\"%s\" is already a child of \"%s\".",
17828 RelationGetRelationName(rel),
17829 RelationGetRelationName(attachrel))));
17831 /* If the parent is permanent, so must be all of its partitions. */
17832 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
17833 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
17834 ereport(ERROR,
17835 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17836 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
17837 RelationGetRelationName(rel))));
17839 /* Temp parent cannot have a partition that is itself not a temp */
17840 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17841 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17842 ereport(ERROR,
17843 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17844 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
17845 RelationGetRelationName(rel))));
17847 /* If the parent is temp, it must belong to this session */
17848 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17849 !rel->rd_islocaltemp)
17850 ereport(ERROR,
17851 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17852 errmsg("cannot attach as partition of temporary relation of another session")));
17854 /* Ditto for the partition */
17855 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17856 !attachrel->rd_islocaltemp)
17857 ereport(ERROR,
17858 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17859 errmsg("cannot attach temporary relation of another session as partition")));
17861 /* Check if there are any columns in attachrel that aren't in the parent */
17862 tupleDesc = RelationGetDescr(attachrel);
17863 natts = tupleDesc->natts;
17864 for (attno = 1; attno <= natts; attno++)
17866 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
17867 char *attributeName = NameStr(attribute->attname);
17869 /* Ignore dropped */
17870 if (attribute->attisdropped)
17871 continue;
17873 /* Try to find the column in parent (matching on column name) */
17874 if (!SearchSysCacheExists2(ATTNAME,
17875 ObjectIdGetDatum(RelationGetRelid(rel)),
17876 CStringGetDatum(attributeName)))
17877 ereport(ERROR,
17878 (errcode(ERRCODE_DATATYPE_MISMATCH),
17879 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
17880 RelationGetRelationName(attachrel), attributeName,
17881 RelationGetRelationName(rel)),
17882 errdetail("The new partition may contain only the columns present in parent.")));
17886 * If child_rel has row-level triggers with transition tables, we
17887 * currently don't allow it to become a partition. See also prohibitions
17888 * in ATExecAddInherit() and CreateTrigger().
17890 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
17891 if (trigger_name != NULL)
17892 ereport(ERROR,
17893 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17894 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
17895 trigger_name, RelationGetRelationName(attachrel)),
17896 errdetail("ROW triggers with transition tables are not supported on partitions.")));
17899 * Check that the new partition's bound is valid and does not overlap any
17900 * of existing partitions of the parent - note that it does not return on
17901 * error.
17903 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
17904 cmd->bound, pstate);
17906 /* OK to create inheritance. Rest of the checks performed there */
17907 CreateInheritance(attachrel, rel);
17909 /* Update the pg_class entry. */
17910 StorePartitionBound(attachrel, rel, cmd->bound);
17912 /* Ensure there exists a correct set of indexes in the partition. */
17913 AttachPartitionEnsureIndexes(rel, attachrel);
17915 /* and triggers */
17916 CloneRowTriggersToPartition(rel, attachrel);
17919 * Clone foreign key constraints. Callee is responsible for setting up
17920 * for phase 3 constraint verification.
17922 CloneForeignKeyConstraints(wqueue, rel, attachrel);
17925 * Generate partition constraint from the partition bound specification.
17926 * If the parent itself is a partition, make sure to include its
17927 * constraint as well.
17929 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
17930 partConstraint = list_concat(partBoundConstraint,
17931 RelationGetPartitionQual(rel));
17933 /* Skip validation if there are no constraints to validate. */
17934 if (partConstraint)
17937 * Run the partition quals through const-simplification similar to
17938 * check constraints. We skip canonicalize_qual, though, because
17939 * partition quals should be in canonical form already.
17941 partConstraint =
17942 (List *) eval_const_expressions(NULL,
17943 (Node *) partConstraint);
17945 /* XXX this sure looks wrong */
17946 partConstraint = list_make1(make_ands_explicit(partConstraint));
17949 * Adjust the generated constraint to match this partition's attribute
17950 * numbers.
17952 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
17953 rel);
17955 /* Validate partition constraints against the table being attached. */
17956 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
17957 false);
17961 * If we're attaching a partition other than the default partition and a
17962 * default one exists, then that partition's partition constraint changes,
17963 * so add an entry to the work queue to validate it, too. (We must not do
17964 * this when the partition being attached is the default one; we already
17965 * did it above!)
17967 if (OidIsValid(defaultPartOid))
17969 Relation defaultrel;
17970 List *defPartConstraint;
17972 Assert(!cmd->bound->is_default);
17974 /* we already hold a lock on the default partition */
17975 defaultrel = table_open(defaultPartOid, NoLock);
17976 defPartConstraint =
17977 get_proposed_default_constraint(partBoundConstraint);
17980 * Map the Vars in the constraint expression from rel's attnos to
17981 * defaultrel's.
17983 defPartConstraint =
17984 map_partition_varattnos(defPartConstraint,
17985 1, defaultrel, rel);
17986 QueuePartitionConstraintValidation(wqueue, defaultrel,
17987 defPartConstraint, true);
17989 /* keep our lock until commit. */
17990 table_close(defaultrel, NoLock);
17993 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
17996 * If the partition we just attached is partitioned itself, invalidate
17997 * relcache for all descendent partitions too to ensure that their
17998 * rd_partcheck expression trees are rebuilt; partitions already locked at
17999 * the beginning of this function.
18001 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18003 ListCell *l;
18005 foreach(l, attachrel_children)
18007 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
18011 /* keep our lock until commit */
18012 table_close(attachrel, NoLock);
18014 return address;
18018 * AttachPartitionEnsureIndexes
18019 * subroutine for ATExecAttachPartition to create/match indexes
18021 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18022 * PARTITION: every partition must have an index attached to each index on the
18023 * partitioned table.
18025 static void
18026 AttachPartitionEnsureIndexes(Relation rel, Relation attachrel)
18028 List *idxes;
18029 List *attachRelIdxs;
18030 Relation *attachrelIdxRels;
18031 IndexInfo **attachInfos;
18032 int i;
18033 ListCell *cell;
18034 MemoryContext cxt;
18035 MemoryContext oldcxt;
18037 cxt = AllocSetContextCreate(CurrentMemoryContext,
18038 "AttachPartitionEnsureIndexes",
18039 ALLOCSET_DEFAULT_SIZES);
18040 oldcxt = MemoryContextSwitchTo(cxt);
18042 idxes = RelationGetIndexList(rel);
18043 attachRelIdxs = RelationGetIndexList(attachrel);
18044 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18045 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18047 /* Build arrays of all existing indexes and their IndexInfos */
18048 i = 0;
18049 foreach(cell, attachRelIdxs)
18051 Oid cldIdxId = lfirst_oid(cell);
18053 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18054 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18055 i++;
18059 * If we're attaching a foreign table, we must fail if any of the indexes
18060 * is a constraint index; otherwise, there's nothing to do here. Do this
18061 * before starting work, to avoid wasting the effort of building a few
18062 * non-unique indexes before coming across a unique one.
18064 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18066 foreach(cell, idxes)
18068 Oid idx = lfirst_oid(cell);
18069 Relation idxRel = index_open(idx, AccessShareLock);
18071 if (idxRel->rd_index->indisunique ||
18072 idxRel->rd_index->indisprimary)
18073 ereport(ERROR,
18074 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18075 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18076 RelationGetRelationName(attachrel),
18077 RelationGetRelationName(rel)),
18078 errdetail("Partitioned table \"%s\" contains unique indexes.",
18079 RelationGetRelationName(rel))));
18080 index_close(idxRel, AccessShareLock);
18083 goto out;
18087 * For each index on the partitioned table, find a matching one in the
18088 * partition-to-be; if one is not found, create one.
18090 foreach(cell, idxes)
18092 Oid idx = lfirst_oid(cell);
18093 Relation idxRel = index_open(idx, AccessShareLock);
18094 IndexInfo *info;
18095 AttrMap *attmap;
18096 bool found = false;
18097 Oid constraintOid;
18100 * Ignore indexes in the partitioned table other than partitioned
18101 * indexes.
18103 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18105 index_close(idxRel, AccessShareLock);
18106 continue;
18109 /* construct an indexinfo to compare existing indexes against */
18110 info = BuildIndexInfo(idxRel);
18111 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18112 RelationGetDescr(rel),
18113 false);
18114 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18117 * Scan the list of existing indexes in the partition-to-be, and mark
18118 * the first matching, valid, unattached one we find, if any, as
18119 * partition of the parent index. If we find one, we're done.
18121 for (i = 0; i < list_length(attachRelIdxs); i++)
18123 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18124 Oid cldConstrOid = InvalidOid;
18126 /* does this index have a parent? if so, can't use it */
18127 if (attachrelIdxRels[i]->rd_rel->relispartition)
18128 continue;
18130 /* If this index is invalid, can't use it */
18131 if (!attachrelIdxRels[i]->rd_index->indisvalid)
18132 continue;
18134 if (CompareIndexInfo(attachInfos[i], info,
18135 attachrelIdxRels[i]->rd_indcollation,
18136 idxRel->rd_indcollation,
18137 attachrelIdxRels[i]->rd_opfamily,
18138 idxRel->rd_opfamily,
18139 attmap))
18142 * If this index is being created in the parent because of a
18143 * constraint, then the child needs to have a constraint also,
18144 * so look for one. If there is no such constraint, this
18145 * index is no good, so keep looking.
18147 if (OidIsValid(constraintOid))
18149 cldConstrOid =
18150 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
18151 cldIdxId);
18152 /* no dice */
18153 if (!OidIsValid(cldConstrOid))
18154 continue;
18157 /* bingo. */
18158 IndexSetParentIndex(attachrelIdxRels[i], idx);
18159 if (OidIsValid(constraintOid))
18160 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18161 RelationGetRelid(attachrel));
18162 found = true;
18164 CommandCounterIncrement();
18165 break;
18170 * If no suitable index was found in the partition-to-be, create one
18171 * now.
18173 if (!found)
18175 IndexStmt *stmt;
18176 Oid conOid;
18178 stmt = generateClonedIndexStmt(NULL,
18179 idxRel, attmap,
18180 &conOid);
18181 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
18182 RelationGetRelid(idxRel),
18183 conOid,
18185 true, false, false, false, false);
18188 index_close(idxRel, AccessShareLock);
18191 out:
18192 /* Clean up. */
18193 for (i = 0; i < list_length(attachRelIdxs); i++)
18194 index_close(attachrelIdxRels[i], AccessShareLock);
18195 MemoryContextSwitchTo(oldcxt);
18196 MemoryContextDelete(cxt);
18200 * CloneRowTriggersToPartition
18201 * subroutine for ATExecAttachPartition/DefineRelation to create row
18202 * triggers on partitions
18204 static void
18205 CloneRowTriggersToPartition(Relation parent, Relation partition)
18207 Relation pg_trigger;
18208 ScanKeyData key;
18209 SysScanDesc scan;
18210 HeapTuple tuple;
18211 MemoryContext perTupCxt;
18213 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18214 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
18215 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
18216 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
18217 true, NULL, 1, &key);
18219 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
18220 "clone trig", ALLOCSET_SMALL_SIZES);
18222 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18224 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
18225 CreateTrigStmt *trigStmt;
18226 Node *qual = NULL;
18227 Datum value;
18228 bool isnull;
18229 List *cols = NIL;
18230 List *trigargs = NIL;
18231 MemoryContext oldcxt;
18234 * Ignore statement-level triggers; those are not cloned.
18236 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
18237 continue;
18240 * Don't clone internal triggers, because the constraint cloning code
18241 * will.
18243 if (trigForm->tgisinternal)
18244 continue;
18247 * Complain if we find an unexpected trigger type.
18249 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
18250 !TRIGGER_FOR_AFTER(trigForm->tgtype))
18251 elog(ERROR, "unexpected trigger \"%s\" found",
18252 NameStr(trigForm->tgname));
18254 /* Use short-lived context for CREATE TRIGGER */
18255 oldcxt = MemoryContextSwitchTo(perTupCxt);
18258 * If there is a WHEN clause, generate a 'cooked' version of it that's
18259 * appropriate for the partition.
18261 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
18262 RelationGetDescr(pg_trigger), &isnull);
18263 if (!isnull)
18265 qual = stringToNode(TextDatumGetCString(value));
18266 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
18267 partition, parent);
18268 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
18269 partition, parent);
18273 * If there is a column list, transform it to a list of column names.
18274 * Note we don't need to map this list in any way ...
18276 if (trigForm->tgattr.dim1 > 0)
18278 int i;
18280 for (i = 0; i < trigForm->tgattr.dim1; i++)
18282 Form_pg_attribute col;
18284 col = TupleDescAttr(parent->rd_att,
18285 trigForm->tgattr.values[i] - 1);
18286 cols = lappend(cols,
18287 makeString(pstrdup(NameStr(col->attname))));
18291 /* Reconstruct trigger arguments list. */
18292 if (trigForm->tgnargs > 0)
18294 char *p;
18296 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
18297 RelationGetDescr(pg_trigger), &isnull);
18298 if (isnull)
18299 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18300 NameStr(trigForm->tgname), RelationGetRelationName(partition));
18302 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
18304 for (int i = 0; i < trigForm->tgnargs; i++)
18306 trigargs = lappend(trigargs, makeString(pstrdup(p)));
18307 p += strlen(p) + 1;
18311 trigStmt = makeNode(CreateTrigStmt);
18312 trigStmt->replace = false;
18313 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
18314 trigStmt->trigname = NameStr(trigForm->tgname);
18315 trigStmt->relation = NULL;
18316 trigStmt->funcname = NULL; /* passed separately */
18317 trigStmt->args = trigargs;
18318 trigStmt->row = true;
18319 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
18320 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
18321 trigStmt->columns = cols;
18322 trigStmt->whenClause = NULL; /* passed separately */
18323 trigStmt->transitionRels = NIL; /* not supported at present */
18324 trigStmt->deferrable = trigForm->tgdeferrable;
18325 trigStmt->initdeferred = trigForm->tginitdeferred;
18326 trigStmt->constrrel = NULL; /* passed separately */
18328 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
18329 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
18330 trigForm->tgfoid, trigForm->oid, qual,
18331 false, true, trigForm->tgenabled);
18333 MemoryContextSwitchTo(oldcxt);
18334 MemoryContextReset(perTupCxt);
18337 MemoryContextDelete(perTupCxt);
18339 systable_endscan(scan);
18340 table_close(pg_trigger, RowExclusiveLock);
18344 * ALTER TABLE DETACH PARTITION
18346 * Return the address of the relation that is no longer a partition of rel.
18348 * If concurrent mode is requested, we run in two transactions. A side-
18349 * effect is that this command cannot run in a multi-part ALTER TABLE.
18350 * Currently, that's enforced by the grammar.
18352 * The strategy for concurrency is to first modify the partition's
18353 * pg_inherit catalog row to make it visible to everyone that the
18354 * partition is detached, lock the partition against writes, and commit
18355 * the transaction; anyone who requests the partition descriptor from
18356 * that point onwards has to ignore such a partition. In a second
18357 * transaction, we wait until all transactions that could have seen the
18358 * partition as attached are gone, then we remove the rest of partition
18359 * metadata (pg_inherits and pg_class.relpartbounds).
18361 static ObjectAddress
18362 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
18363 RangeVar *name, bool concurrent)
18365 Relation partRel;
18366 ObjectAddress address;
18367 Oid defaultPartOid;
18370 * We must lock the default partition, because detaching this partition
18371 * will change its partition constraint.
18373 defaultPartOid =
18374 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
18375 if (OidIsValid(defaultPartOid))
18378 * Concurrent detaching when a default partition exists is not
18379 * supported. The main problem is that the default partition
18380 * constraint would change. And there's a definitional problem: what
18381 * should happen to the tuples that are being inserted that belong to
18382 * the partition being detached? Putting them on the partition being
18383 * detached would be wrong, since they'd become "lost" after the
18384 * detaching completes but we cannot put them in the default partition
18385 * either until we alter its partition constraint.
18387 * I think we could solve this problem if we effected the constraint
18388 * change before committing the first transaction. But the lock would
18389 * have to remain AEL and it would cause concurrent query planning to
18390 * be blocked, so changing it that way would be even worse.
18392 if (concurrent)
18393 ereport(ERROR,
18394 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18395 errmsg("cannot detach partitions concurrently when a default partition exists")));
18396 LockRelationOid(defaultPartOid, AccessExclusiveLock);
18400 * In concurrent mode, the partition is locked with share-update-exclusive
18401 * in the first transaction. This allows concurrent transactions to be
18402 * doing DML to the partition.
18404 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
18405 AccessExclusiveLock);
18408 * Check inheritance conditions and either delete the pg_inherits row (in
18409 * non-concurrent mode) or just set the inhdetachpending flag.
18411 if (!concurrent)
18412 RemoveInheritance(partRel, rel, false);
18413 else
18414 MarkInheritDetached(partRel, rel);
18417 * Ensure that foreign keys still hold after this detach. This keeps
18418 * locks on the referencing tables, which prevents concurrent transactions
18419 * from adding rows that we wouldn't see. For this to work in concurrent
18420 * mode, it is critical that the partition appears as no longer attached
18421 * for the RI queries as soon as the first transaction commits.
18423 ATDetachCheckNoForeignKeyRefs(partRel);
18426 * Concurrent mode has to work harder; first we add a new constraint to
18427 * the partition that matches the partition constraint. Then we close our
18428 * existing transaction, and in a new one wait for all processes to catch
18429 * up on the catalog updates we've done so far; at that point we can
18430 * complete the operation.
18432 if (concurrent)
18434 Oid partrelid,
18435 parentrelid;
18436 LOCKTAG tag;
18437 char *parentrelname;
18438 char *partrelname;
18441 * Add a new constraint to the partition being detached, which
18442 * supplants the partition constraint (unless there is one already).
18444 DetachAddConstraintIfNeeded(wqueue, partRel);
18447 * We're almost done now; the only traces that remain are the
18448 * pg_inherits tuple and the partition's relpartbounds. Before we can
18449 * remove those, we need to wait until all transactions that know that
18450 * this is a partition are gone.
18454 * Remember relation OIDs to re-acquire them later; and relation names
18455 * too, for error messages if something is dropped in between.
18457 partrelid = RelationGetRelid(partRel);
18458 parentrelid = RelationGetRelid(rel);
18459 parentrelname = MemoryContextStrdup(PortalContext,
18460 RelationGetRelationName(rel));
18461 partrelname = MemoryContextStrdup(PortalContext,
18462 RelationGetRelationName(partRel));
18464 /* Invalidate relcache entries for the parent -- must be before close */
18465 CacheInvalidateRelcache(rel);
18467 table_close(partRel, NoLock);
18468 table_close(rel, NoLock);
18469 tab->rel = NULL;
18471 /* Make updated catalog entry visible */
18472 PopActiveSnapshot();
18473 CommitTransactionCommand();
18475 StartTransactionCommand();
18478 * Now wait. This ensures that all queries that were planned
18479 * including the partition are finished before we remove the rest of
18480 * catalog entries. We don't need or indeed want to acquire this
18481 * lock, though -- that would block later queries.
18483 * We don't need to concern ourselves with waiting for a lock on the
18484 * partition itself, since we will acquire AccessExclusiveLock below.
18486 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
18487 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
18490 * Now acquire locks in both relations again. Note they may have been
18491 * removed in the meantime, so care is required.
18493 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
18494 partRel = try_relation_open(partrelid, AccessExclusiveLock);
18496 /* If the relations aren't there, something bad happened; bail out */
18497 if (rel == NULL)
18499 if (partRel != NULL) /* shouldn't happen */
18500 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
18501 partrelname);
18502 ereport(ERROR,
18503 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18504 errmsg("partitioned table \"%s\" was removed concurrently",
18505 parentrelname)));
18507 if (partRel == NULL)
18508 ereport(ERROR,
18509 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18510 errmsg("partition \"%s\" was removed concurrently", partrelname)));
18512 tab->rel = rel;
18515 /* Do the final part of detaching */
18516 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
18518 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
18520 /* keep our lock until commit */
18521 table_close(partRel, NoLock);
18523 return address;
18527 * Second part of ALTER TABLE .. DETACH.
18529 * This is separate so that it can be run independently when the second
18530 * transaction of the concurrent algorithm fails (crash or abort).
18532 static void
18533 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
18534 Oid defaultPartOid)
18536 Relation classRel;
18537 List *fks;
18538 ListCell *cell;
18539 List *indexes;
18540 Datum new_val[Natts_pg_class];
18541 bool new_null[Natts_pg_class],
18542 new_repl[Natts_pg_class];
18543 HeapTuple tuple,
18544 newtuple;
18545 Relation trigrel = NULL;
18547 if (concurrent)
18550 * We can remove the pg_inherits row now. (In the non-concurrent case,
18551 * this was already done).
18553 RemoveInheritance(partRel, rel, true);
18556 /* Drop any triggers that were cloned on creation/attach. */
18557 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
18560 * Detach any foreign keys that are inherited. This includes creating
18561 * additional action triggers.
18563 fks = copyObject(RelationGetFKeyList(partRel));
18564 if (fks != NIL)
18565 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
18566 foreach(cell, fks)
18568 ForeignKeyCacheInfo *fk = lfirst(cell);
18569 HeapTuple contup;
18570 Form_pg_constraint conform;
18571 Constraint *fkconstraint;
18572 Oid insertTriggerOid,
18573 updateTriggerOid;
18575 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
18576 if (!HeapTupleIsValid(contup))
18577 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
18578 conform = (Form_pg_constraint) GETSTRUCT(contup);
18580 /* consider only the inherited foreign keys */
18581 if (conform->contype != CONSTRAINT_FOREIGN ||
18582 !OidIsValid(conform->conparentid))
18584 ReleaseSysCache(contup);
18585 continue;
18588 /* unset conparentid and adjust conislocal, coninhcount, etc. */
18589 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
18592 * Also, look up the partition's "check" triggers corresponding to the
18593 * constraint being detached and detach them from the parent triggers.
18595 GetForeignKeyCheckTriggers(trigrel,
18596 fk->conoid, fk->confrelid, fk->conrelid,
18597 &insertTriggerOid, &updateTriggerOid);
18598 Assert(OidIsValid(insertTriggerOid));
18599 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
18600 RelationGetRelid(partRel));
18601 Assert(OidIsValid(updateTriggerOid));
18602 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
18603 RelationGetRelid(partRel));
18606 * Make the action triggers on the referenced relation. When this was
18607 * a partition the action triggers pointed to the parent rel (they
18608 * still do), but now we need separate ones of our own.
18610 fkconstraint = makeNode(Constraint);
18611 fkconstraint->contype = CONSTRAINT_FOREIGN;
18612 fkconstraint->conname = pstrdup(NameStr(conform->conname));
18613 fkconstraint->deferrable = conform->condeferrable;
18614 fkconstraint->initdeferred = conform->condeferred;
18615 fkconstraint->location = -1;
18616 fkconstraint->pktable = NULL;
18617 fkconstraint->fk_attrs = NIL;
18618 fkconstraint->pk_attrs = NIL;
18619 fkconstraint->fk_matchtype = conform->confmatchtype;
18620 fkconstraint->fk_upd_action = conform->confupdtype;
18621 fkconstraint->fk_del_action = conform->confdeltype;
18622 fkconstraint->fk_del_set_cols = NIL;
18623 fkconstraint->old_conpfeqop = NIL;
18624 fkconstraint->old_pktable_oid = InvalidOid;
18625 fkconstraint->skip_validation = false;
18626 fkconstraint->initially_valid = true;
18628 createForeignKeyActionTriggers(partRel, conform->confrelid,
18629 fkconstraint, fk->conoid,
18630 conform->conindid,
18631 InvalidOid, InvalidOid,
18632 NULL, NULL);
18634 ReleaseSysCache(contup);
18636 list_free_deep(fks);
18637 if (trigrel)
18638 table_close(trigrel, RowExclusiveLock);
18641 * Any sub-constraints that are in the referenced-side of a larger
18642 * constraint have to be removed. This partition is no longer part of the
18643 * key space of the constraint.
18645 foreach(cell, GetParentedForeignKeyRefs(partRel))
18647 Oid constrOid = lfirst_oid(cell);
18648 ObjectAddress constraint;
18650 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
18651 deleteDependencyRecordsForClass(ConstraintRelationId,
18652 constrOid,
18653 ConstraintRelationId,
18654 DEPENDENCY_INTERNAL);
18655 CommandCounterIncrement();
18657 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
18658 performDeletion(&constraint, DROP_RESTRICT, 0);
18661 /* Now we can detach indexes */
18662 indexes = RelationGetIndexList(partRel);
18663 foreach(cell, indexes)
18665 Oid idxid = lfirst_oid(cell);
18666 Relation idx;
18667 Oid constrOid;
18669 if (!has_superclass(idxid))
18670 continue;
18672 Assert((IndexGetRelation(get_partition_parent(idxid, false), false) ==
18673 RelationGetRelid(rel)));
18675 idx = index_open(idxid, AccessExclusiveLock);
18676 IndexSetParentIndex(idx, InvalidOid);
18678 /* If there's a constraint associated with the index, detach it too */
18679 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
18680 idxid);
18681 if (OidIsValid(constrOid))
18682 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
18684 index_close(idx, NoLock);
18687 /* Update pg_class tuple */
18688 classRel = table_open(RelationRelationId, RowExclusiveLock);
18689 tuple = SearchSysCacheCopy1(RELOID,
18690 ObjectIdGetDatum(RelationGetRelid(partRel)));
18691 if (!HeapTupleIsValid(tuple))
18692 elog(ERROR, "cache lookup failed for relation %u",
18693 RelationGetRelid(partRel));
18694 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
18696 /* Clear relpartbound and reset relispartition */
18697 memset(new_val, 0, sizeof(new_val));
18698 memset(new_null, false, sizeof(new_null));
18699 memset(new_repl, false, sizeof(new_repl));
18700 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
18701 new_null[Anum_pg_class_relpartbound - 1] = true;
18702 new_repl[Anum_pg_class_relpartbound - 1] = true;
18703 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
18704 new_val, new_null, new_repl);
18706 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
18707 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
18708 heap_freetuple(newtuple);
18709 table_close(classRel, RowExclusiveLock);
18711 if (OidIsValid(defaultPartOid))
18714 * If the relation being detached is the default partition itself,
18715 * remove it from the parent's pg_partitioned_table entry.
18717 * If not, we must invalidate default partition's relcache entry, as
18718 * in StorePartitionBound: its partition constraint depends on every
18719 * other partition's partition constraint.
18721 if (RelationGetRelid(partRel) == defaultPartOid)
18722 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
18723 else
18724 CacheInvalidateRelcacheByRelid(defaultPartOid);
18728 * Invalidate the parent's relcache so that the partition is no longer
18729 * included in its partition descriptor.
18731 CacheInvalidateRelcache(rel);
18734 * If the partition we just detached is partitioned itself, invalidate
18735 * relcache for all descendent partitions too to ensure that their
18736 * rd_partcheck expression trees are rebuilt; must lock partitions before
18737 * doing so, using the same lockmode as what partRel has been locked with
18738 * by the caller.
18740 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18742 List *children;
18744 children = find_all_inheritors(RelationGetRelid(partRel),
18745 AccessExclusiveLock, NULL);
18746 foreach(cell, children)
18748 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
18754 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
18756 * To use when a DETACH PARTITION command previously did not run to
18757 * completion; this completes the detaching process.
18759 static ObjectAddress
18760 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
18762 Relation partRel;
18763 ObjectAddress address;
18764 Snapshot snap = GetActiveSnapshot();
18766 partRel = table_openrv(name, AccessExclusiveLock);
18769 * Wait until existing snapshots are gone. This is important if the
18770 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
18771 * user could immediately run DETACH FINALIZE without actually waiting for
18772 * existing transactions. We must not complete the detach action until
18773 * all such queries are complete (otherwise we would present them with an
18774 * inconsistent view of catalogs).
18776 WaitForOlderSnapshots(snap->xmin, false);
18778 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
18780 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
18782 table_close(partRel, NoLock);
18784 return address;
18788 * DetachAddConstraintIfNeeded
18789 * Subroutine for ATExecDetachPartition. Create a constraint that
18790 * takes the place of the partition constraint, but avoid creating
18791 * a dupe if an constraint already exists which implies the needed
18792 * constraint.
18794 static void
18795 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
18797 List *constraintExpr;
18799 constraintExpr = RelationGetPartitionQual(partRel);
18800 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
18803 * Avoid adding a new constraint if the needed constraint is implied by an
18804 * existing constraint
18806 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
18808 AlteredTableInfo *tab;
18809 Constraint *n;
18811 tab = ATGetQueueEntry(wqueue, partRel);
18813 /* Add constraint on partition, equivalent to the partition constraint */
18814 n = makeNode(Constraint);
18815 n->contype = CONSTR_CHECK;
18816 n->conname = NULL;
18817 n->location = -1;
18818 n->is_no_inherit = false;
18819 n->raw_expr = NULL;
18820 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
18821 n->initially_valid = true;
18822 n->skip_validation = true;
18823 /* It's a re-add, since it nominally already exists */
18824 ATAddCheckConstraint(wqueue, tab, partRel, n,
18825 true, false, true, ShareUpdateExclusiveLock);
18830 * DropClonedTriggersFromPartition
18831 * subroutine for ATExecDetachPartition to remove any triggers that were
18832 * cloned to the partition when it was created-as-partition or attached.
18833 * This undoes what CloneRowTriggersToPartition did.
18835 static void
18836 DropClonedTriggersFromPartition(Oid partitionId)
18838 ScanKeyData skey;
18839 SysScanDesc scan;
18840 HeapTuple trigtup;
18841 Relation tgrel;
18842 ObjectAddresses *objects;
18844 objects = new_object_addresses();
18847 * Scan pg_trigger to search for all triggers on this rel.
18849 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18850 F_OIDEQ, ObjectIdGetDatum(partitionId));
18851 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
18852 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
18853 true, NULL, 1, &skey);
18854 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
18856 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
18857 ObjectAddress trig;
18859 /* Ignore triggers that weren't cloned */
18860 if (!OidIsValid(pg_trigger->tgparentid))
18861 continue;
18864 * Ignore internal triggers that are implementation objects of foreign
18865 * keys, because these will be detached when the foreign keys
18866 * themselves are.
18868 if (OidIsValid(pg_trigger->tgconstrrelid))
18869 continue;
18872 * This is ugly, but necessary: remove the dependency markings on the
18873 * trigger so that it can be removed.
18875 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
18876 TriggerRelationId,
18877 DEPENDENCY_PARTITION_PRI);
18878 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
18879 RelationRelationId,
18880 DEPENDENCY_PARTITION_SEC);
18882 /* remember this trigger to remove it below */
18883 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
18884 add_exact_object_address(&trig, objects);
18887 /* make the dependency removal visible to the deletion below */
18888 CommandCounterIncrement();
18889 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
18891 /* done */
18892 free_object_addresses(objects);
18893 systable_endscan(scan);
18894 table_close(tgrel, RowExclusiveLock);
18898 * Before acquiring lock on an index, acquire the same lock on the owning
18899 * table.
18901 struct AttachIndexCallbackState
18903 Oid partitionOid;
18904 Oid parentTblOid;
18905 bool lockedParentTbl;
18908 static void
18909 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
18910 void *arg)
18912 struct AttachIndexCallbackState *state;
18913 Form_pg_class classform;
18914 HeapTuple tuple;
18916 state = (struct AttachIndexCallbackState *) arg;
18918 if (!state->lockedParentTbl)
18920 LockRelationOid(state->parentTblOid, AccessShareLock);
18921 state->lockedParentTbl = true;
18925 * If we previously locked some other heap, and the name we're looking up
18926 * no longer refers to an index on that relation, release the now-useless
18927 * lock. XXX maybe we should do *after* we verify whether the index does
18928 * not actually belong to the same relation ...
18930 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
18932 UnlockRelationOid(state->partitionOid, AccessShareLock);
18933 state->partitionOid = InvalidOid;
18936 /* Didn't find a relation, so no need for locking or permission checks. */
18937 if (!OidIsValid(relOid))
18938 return;
18940 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
18941 if (!HeapTupleIsValid(tuple))
18942 return; /* concurrently dropped, so nothing to do */
18943 classform = (Form_pg_class) GETSTRUCT(tuple);
18944 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
18945 classform->relkind != RELKIND_INDEX)
18946 ereport(ERROR,
18947 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18948 errmsg("\"%s\" is not an index", rv->relname)));
18949 ReleaseSysCache(tuple);
18952 * Since we need only examine the heap's tupledesc, an access share lock
18953 * on it (preventing any DDL) is sufficient.
18955 state->partitionOid = IndexGetRelation(relOid, false);
18956 LockRelationOid(state->partitionOid, AccessShareLock);
18960 * ALTER INDEX i1 ATTACH PARTITION i2
18962 static ObjectAddress
18963 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
18965 Relation partIdx;
18966 Relation partTbl;
18967 Relation parentTbl;
18968 ObjectAddress address;
18969 Oid partIdxId;
18970 Oid currParent;
18971 struct AttachIndexCallbackState state;
18974 * We need to obtain lock on the index 'name' to modify it, but we also
18975 * need to read its owning table's tuple descriptor -- so we need to lock
18976 * both. To avoid deadlocks, obtain lock on the table before doing so on
18977 * the index. Furthermore, we need to examine the parent table of the
18978 * partition, so lock that one too.
18980 state.partitionOid = InvalidOid;
18981 state.parentTblOid = parentIdx->rd_index->indrelid;
18982 state.lockedParentTbl = false;
18983 partIdxId =
18984 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
18985 RangeVarCallbackForAttachIndex,
18986 (void *) &state);
18987 /* Not there? */
18988 if (!OidIsValid(partIdxId))
18989 ereport(ERROR,
18990 (errcode(ERRCODE_UNDEFINED_OBJECT),
18991 errmsg("index \"%s\" does not exist", name->relname)));
18993 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
18994 partIdx = relation_open(partIdxId, AccessExclusiveLock);
18996 /* we already hold locks on both tables, so this is safe: */
18997 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
18998 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19000 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19002 /* Silently do nothing if already in the right state */
19003 currParent = partIdx->rd_rel->relispartition ?
19004 get_partition_parent(partIdxId, false) : InvalidOid;
19005 if (currParent != RelationGetRelid(parentIdx))
19007 IndexInfo *childInfo;
19008 IndexInfo *parentInfo;
19009 AttrMap *attmap;
19010 bool found;
19011 int i;
19012 PartitionDesc partDesc;
19013 Oid constraintOid,
19014 cldConstrId = InvalidOid;
19017 * If this partition already has an index attached, refuse the
19018 * operation.
19020 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19022 if (OidIsValid(currParent))
19023 ereport(ERROR,
19024 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19025 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19026 RelationGetRelationName(partIdx),
19027 RelationGetRelationName(parentIdx)),
19028 errdetail("Index \"%s\" is already attached to another index.",
19029 RelationGetRelationName(partIdx))));
19031 /* Make sure it indexes a partition of the other index's table */
19032 partDesc = RelationGetPartitionDesc(parentTbl, true);
19033 found = false;
19034 for (i = 0; i < partDesc->nparts; i++)
19036 if (partDesc->oids[i] == state.partitionOid)
19038 found = true;
19039 break;
19042 if (!found)
19043 ereport(ERROR,
19044 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19045 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19046 RelationGetRelationName(partIdx),
19047 RelationGetRelationName(parentIdx)),
19048 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19049 RelationGetRelationName(partIdx),
19050 RelationGetRelationName(parentTbl))));
19052 /* Ensure the indexes are compatible */
19053 childInfo = BuildIndexInfo(partIdx);
19054 parentInfo = BuildIndexInfo(parentIdx);
19055 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19056 RelationGetDescr(parentTbl),
19057 false);
19058 if (!CompareIndexInfo(childInfo, parentInfo,
19059 partIdx->rd_indcollation,
19060 parentIdx->rd_indcollation,
19061 partIdx->rd_opfamily,
19062 parentIdx->rd_opfamily,
19063 attmap))
19064 ereport(ERROR,
19065 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19066 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19067 RelationGetRelationName(partIdx),
19068 RelationGetRelationName(parentIdx)),
19069 errdetail("The index definitions do not match.")));
19072 * If there is a constraint in the parent, make sure there is one in
19073 * the child too.
19075 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19076 RelationGetRelid(parentIdx));
19078 if (OidIsValid(constraintOid))
19080 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19081 partIdxId);
19082 if (!OidIsValid(cldConstrId))
19083 ereport(ERROR,
19084 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19085 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19086 RelationGetRelationName(partIdx),
19087 RelationGetRelationName(parentIdx)),
19088 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19089 RelationGetRelationName(parentIdx),
19090 RelationGetRelationName(parentTbl),
19091 RelationGetRelationName(partIdx))));
19094 /* All good -- do it */
19095 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
19096 if (OidIsValid(constraintOid))
19097 ConstraintSetParentConstraint(cldConstrId, constraintOid,
19098 RelationGetRelid(partTbl));
19100 free_attrmap(attmap);
19102 validatePartitionedIndex(parentIdx, parentTbl);
19105 relation_close(parentTbl, AccessShareLock);
19106 /* keep these locks till commit */
19107 relation_close(partTbl, NoLock);
19108 relation_close(partIdx, NoLock);
19110 return address;
19114 * Verify whether the given partition already contains an index attached
19115 * to the given partitioned index. If so, raise an error.
19117 static void
19118 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
19120 Oid existingIdx;
19122 existingIdx = index_get_partition(partitionTbl,
19123 RelationGetRelid(parentIdx));
19124 if (OidIsValid(existingIdx))
19125 ereport(ERROR,
19126 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19127 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19128 RelationGetRelationName(partIdx),
19129 RelationGetRelationName(parentIdx)),
19130 errdetail("Another index is already attached for partition \"%s\".",
19131 RelationGetRelationName(partitionTbl))));
19135 * Verify whether the set of attached partition indexes to a parent index on
19136 * a partitioned table is complete. If it is, mark the parent index valid.
19138 * This should be called each time a partition index is attached.
19140 static void
19141 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
19143 Relation inheritsRel;
19144 SysScanDesc scan;
19145 ScanKeyData key;
19146 int tuples = 0;
19147 HeapTuple inhTup;
19148 bool updated = false;
19150 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19153 * Scan pg_inherits for this parent index. Count each valid index we find
19154 * (verifying the pg_index entry for each), and if we reach the total
19155 * amount we expect, we can mark this parent index as valid.
19157 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
19158 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
19159 BTEqualStrategyNumber, F_OIDEQ,
19160 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19161 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19162 NULL, 1, &key);
19163 while ((inhTup = systable_getnext(scan)) != NULL)
19165 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19166 HeapTuple indTup;
19167 Form_pg_index indexForm;
19169 indTup = SearchSysCache1(INDEXRELID,
19170 ObjectIdGetDatum(inhForm->inhrelid));
19171 if (!HeapTupleIsValid(indTup))
19172 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
19173 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19174 if (indexForm->indisvalid)
19175 tuples += 1;
19176 ReleaseSysCache(indTup);
19179 /* Done with pg_inherits */
19180 systable_endscan(scan);
19181 table_close(inheritsRel, AccessShareLock);
19184 * If we found as many inherited indexes as the partitioned table has
19185 * partitions, we're good; update pg_index to set indisvalid.
19187 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19189 Relation idxRel;
19190 HeapTuple indTup;
19191 Form_pg_index indexForm;
19193 idxRel = table_open(IndexRelationId, RowExclusiveLock);
19194 indTup = SearchSysCacheCopy1(INDEXRELID,
19195 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19196 if (!HeapTupleIsValid(indTup))
19197 elog(ERROR, "cache lookup failed for index %u",
19198 RelationGetRelid(partedIdx));
19199 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19201 indexForm->indisvalid = true;
19202 updated = true;
19204 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
19206 table_close(idxRel, RowExclusiveLock);
19207 heap_freetuple(indTup);
19211 * If this index is in turn a partition of a larger index, validating it
19212 * might cause the parent to become valid also. Try that.
19214 if (updated && partedIdx->rd_rel->relispartition)
19216 Oid parentIdxId,
19217 parentTblId;
19218 Relation parentIdx,
19219 parentTbl;
19221 /* make sure we see the validation we just did */
19222 CommandCounterIncrement();
19224 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
19225 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
19226 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
19227 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
19228 Assert(!parentIdx->rd_index->indisvalid);
19230 validatePartitionedIndex(parentIdx, parentTbl);
19232 relation_close(parentIdx, AccessExclusiveLock);
19233 relation_close(parentTbl, AccessExclusiveLock);
19238 * Return an OID list of constraints that reference the given relation
19239 * that are marked as having a parent constraints.
19241 static List *
19242 GetParentedForeignKeyRefs(Relation partition)
19244 Relation pg_constraint;
19245 HeapTuple tuple;
19246 SysScanDesc scan;
19247 ScanKeyData key[2];
19248 List *constraints = NIL;
19251 * If no indexes, or no columns are referenceable by FKs, we can avoid the
19252 * scan.
19254 if (RelationGetIndexList(partition) == NIL ||
19255 bms_is_empty(RelationGetIndexAttrBitmap(partition,
19256 INDEX_ATTR_BITMAP_KEY)))
19257 return NIL;
19259 /* Search for constraints referencing this table */
19260 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19261 ScanKeyInit(&key[0],
19262 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
19263 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
19264 ScanKeyInit(&key[1],
19265 Anum_pg_constraint_contype, BTEqualStrategyNumber,
19266 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
19268 /* XXX This is a seqscan, as we don't have a usable index */
19269 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
19270 while ((tuple = systable_getnext(scan)) != NULL)
19272 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19275 * We only need to process constraints that are part of larger ones.
19277 if (!OidIsValid(constrForm->conparentid))
19278 continue;
19280 constraints = lappend_oid(constraints, constrForm->oid);
19283 systable_endscan(scan);
19284 table_close(pg_constraint, AccessShareLock);
19286 return constraints;
19290 * During DETACH PARTITION, verify that any foreign keys pointing to the
19291 * partitioned table would not become invalid. An error is raised if any
19292 * referenced values exist.
19294 static void
19295 ATDetachCheckNoForeignKeyRefs(Relation partition)
19297 List *constraints;
19298 ListCell *cell;
19300 constraints = GetParentedForeignKeyRefs(partition);
19302 foreach(cell, constraints)
19304 Oid constrOid = lfirst_oid(cell);
19305 HeapTuple tuple;
19306 Form_pg_constraint constrForm;
19307 Relation rel;
19308 Trigger trig = {0};
19310 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
19311 if (!HeapTupleIsValid(tuple))
19312 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
19313 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19315 Assert(OidIsValid(constrForm->conparentid));
19316 Assert(constrForm->confrelid == RelationGetRelid(partition));
19318 /* prevent data changes into the referencing table until commit */
19319 rel = table_open(constrForm->conrelid, ShareLock);
19321 trig.tgoid = InvalidOid;
19322 trig.tgname = NameStr(constrForm->conname);
19323 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
19324 trig.tgisinternal = true;
19325 trig.tgconstrrelid = RelationGetRelid(partition);
19326 trig.tgconstrindid = constrForm->conindid;
19327 trig.tgconstraint = constrForm->oid;
19328 trig.tgdeferrable = false;
19329 trig.tginitdeferred = false;
19330 /* we needn't fill in remaining fields */
19332 RI_PartitionRemove_Check(&trig, rel, partition);
19334 ReleaseSysCache(tuple);
19336 table_close(rel, NoLock);
19341 * resolve column compression specification to compression method.
19343 static char
19344 GetAttributeCompression(Oid atttypid, char *compression)
19346 char cmethod;
19348 if (compression == NULL || strcmp(compression, "default") == 0)
19349 return InvalidCompressionMethod;
19352 * To specify a nondefault method, the column data type must be toastable.
19353 * Note this says nothing about whether the column's attstorage setting
19354 * permits compression; we intentionally allow attstorage and
19355 * attcompression to be independent. But with a non-toastable type,
19356 * attstorage could not be set to a value that would permit compression.
19358 * We don't actually need to enforce this, since nothing bad would happen
19359 * if attcompression were non-default; it would never be consulted. But
19360 * it seems more user-friendly to complain about a certainly-useless
19361 * attempt to set the property.
19363 if (!TypeIsToastable(atttypid))
19364 ereport(ERROR,
19365 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19366 errmsg("column data type %s does not support compression",
19367 format_type_be(atttypid))));
19369 cmethod = CompressionNameToMethod(compression);
19370 if (!CompressionMethodIsValid(cmethod))
19371 ereport(ERROR,
19372 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
19373 errmsg("invalid compression method \"%s\"", compression)));
19375 return cmethod;
19379 * resolve column storage specification
19381 static char
19382 GetAttributeStorage(Oid atttypid, const char *storagemode)
19384 char cstorage = 0;
19386 if (pg_strcasecmp(storagemode, "plain") == 0)
19387 cstorage = TYPSTORAGE_PLAIN;
19388 else if (pg_strcasecmp(storagemode, "external") == 0)
19389 cstorage = TYPSTORAGE_EXTERNAL;
19390 else if (pg_strcasecmp(storagemode, "extended") == 0)
19391 cstorage = TYPSTORAGE_EXTENDED;
19392 else if (pg_strcasecmp(storagemode, "main") == 0)
19393 cstorage = TYPSTORAGE_MAIN;
19394 else if (pg_strcasecmp(storagemode, "default") == 0)
19395 cstorage = get_typstorage(atttypid);
19396 else
19397 ereport(ERROR,
19398 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
19399 errmsg("invalid storage type \"%s\"",
19400 storagemode)));
19403 * safety check: do not allow toasted storage modes unless column datatype
19404 * is TOAST-aware.
19406 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
19407 ereport(ERROR,
19408 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
19409 errmsg("column data type %s can only have storage PLAIN",
19410 format_type_be(atttypid))));
19412 return cstorage;