When creating materialized views, use REFRESH to load data.
[pgsql.git] / src / backend / commands / tablecmds.c
blob721d24783b4e38b472fee53dbce5bcd5ef9518be
1 /*-------------------------------------------------------------------------
3 * tablecmds.c
4 * Commands for creating and altering table structures and settings
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * IDENTIFICATION
11 * src/backend/commands/tablecmds.c
13 *-------------------------------------------------------------------------
15 #include "postgres.h"
17 #include "access/attmap.h"
18 #include "access/genam.h"
19 #include "access/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_policy.h"
47 #include "catalog/pg_proc.h"
48 #include "catalog/pg_publication_rel.h"
49 #include "catalog/pg_rewrite.h"
50 #include "catalog/pg_statistic_ext.h"
51 #include "catalog/pg_tablespace.h"
52 #include "catalog/pg_trigger.h"
53 #include "catalog/pg_type.h"
54 #include "catalog/storage.h"
55 #include "catalog/storage_xlog.h"
56 #include "catalog/toasting.h"
57 #include "commands/cluster.h"
58 #include "commands/comment.h"
59 #include "commands/defrem.h"
60 #include "commands/event_trigger.h"
61 #include "commands/sequence.h"
62 #include "commands/tablecmds.h"
63 #include "commands/tablespace.h"
64 #include "commands/trigger.h"
65 #include "commands/typecmds.h"
66 #include "commands/user.h"
67 #include "commands/vacuum.h"
68 #include "executor/executor.h"
69 #include "foreign/fdwapi.h"
70 #include "foreign/foreign.h"
71 #include "miscadmin.h"
72 #include "nodes/makefuncs.h"
73 #include "nodes/nodeFuncs.h"
74 #include "nodes/parsenodes.h"
75 #include "optimizer/optimizer.h"
76 #include "parser/parse_coerce.h"
77 #include "parser/parse_collate.h"
78 #include "parser/parse_expr.h"
79 #include "parser/parse_relation.h"
80 #include "parser/parse_type.h"
81 #include "parser/parse_utilcmd.h"
82 #include "parser/parser.h"
83 #include "partitioning/partbounds.h"
84 #include "partitioning/partdesc.h"
85 #include "pgstat.h"
86 #include "rewrite/rewriteDefine.h"
87 #include "rewrite/rewriteHandler.h"
88 #include "rewrite/rewriteManip.h"
89 #include "storage/bufmgr.h"
90 #include "storage/lmgr.h"
91 #include "storage/lock.h"
92 #include "storage/predicate.h"
93 #include "storage/smgr.h"
94 #include "tcop/utility.h"
95 #include "utils/acl.h"
96 #include "utils/builtins.h"
97 #include "utils/fmgroids.h"
98 #include "utils/inval.h"
99 #include "utils/lsyscache.h"
100 #include "utils/memutils.h"
101 #include "utils/partcache.h"
102 #include "utils/relcache.h"
103 #include "utils/ruleutils.h"
104 #include "utils/snapmgr.h"
105 #include "utils/syscache.h"
106 #include "utils/timestamp.h"
107 #include "utils/typcache.h"
108 #include "utils/usercontext.h"
111 * ON COMMIT action list
113 typedef struct OnCommitItem
115 Oid relid; /* relid of relation */
116 OnCommitAction oncommit; /* what to do at end of xact */
119 * If this entry was created during the current transaction,
120 * creating_subid is the ID of the creating subxact; if created in a prior
121 * transaction, creating_subid is zero. If deleted during the current
122 * transaction, deleting_subid is the ID of the deleting subxact; if no
123 * deletion request is pending, deleting_subid is zero.
125 SubTransactionId creating_subid;
126 SubTransactionId deleting_subid;
127 } OnCommitItem;
129 static List *on_commits = NIL;
133 * State information for ALTER TABLE
135 * The pending-work queue for an ALTER TABLE is a List of AlteredTableInfo
136 * structs, one for each table modified by the operation (the named table
137 * plus any child tables that are affected). We save lists of subcommands
138 * to apply to this table (possibly modified by parse transformation steps);
139 * these lists will be executed in Phase 2. If a Phase 3 step is needed,
140 * necessary information is stored in the constraints and newvals lists.
142 * Phase 2 is divided into multiple passes; subcommands are executed in
143 * a pass determined by subcommand type.
146 typedef enum AlterTablePass
148 AT_PASS_UNSET = -1, /* UNSET will cause ERROR */
149 AT_PASS_DROP, /* DROP (all flavors) */
150 AT_PASS_ALTER_TYPE, /* ALTER COLUMN TYPE */
151 AT_PASS_ADD_COL, /* ADD COLUMN */
152 AT_PASS_SET_EXPRESSION, /* ALTER SET EXPRESSION */
153 AT_PASS_OLD_INDEX, /* re-add existing indexes */
154 AT_PASS_OLD_CONSTR, /* re-add existing constraints */
155 /* We could support a RENAME COLUMN pass here, but not currently used */
156 AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
157 AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
158 AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
159 AT_PASS_ADD_INDEX, /* ADD indexes */
160 AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
161 AT_PASS_MISC, /* other stuff */
162 } AlterTablePass;
164 #define AT_NUM_PASSES (AT_PASS_MISC + 1)
166 typedef struct AlteredTableInfo
168 /* Information saved before any work commences: */
169 Oid relid; /* Relation to work on */
170 char relkind; /* Its relkind */
171 TupleDesc oldDesc; /* Pre-modification tuple descriptor */
174 * Transiently set during Phase 2, normally set to NULL.
176 * ATRewriteCatalogs sets this when it starts, and closes when ATExecCmd
177 * returns control. This can be exploited by ATExecCmd subroutines to
178 * close/reopen across transaction boundaries.
180 Relation rel;
182 /* Information saved by Phase 1 for Phase 2: */
183 List *subcmds[AT_NUM_PASSES]; /* Lists of AlterTableCmd */
184 /* Information saved by Phases 1/2 for Phase 3: */
185 List *constraints; /* List of NewConstraint */
186 List *newvals; /* List of NewColumnValue */
187 List *afterStmts; /* List of utility command parsetrees */
188 bool verify_new_notnull; /* T if we should recheck NOT NULL */
189 int rewrite; /* Reason for forced rewrite, if any */
190 bool chgAccessMethod; /* T if SET ACCESS METHOD is used */
191 Oid newAccessMethod; /* new access method; 0 means no change,
192 * if above is true */
193 Oid newTableSpace; /* new tablespace; 0 means no change */
194 bool chgPersistence; /* T if SET LOGGED/UNLOGGED is used */
195 char newrelpersistence; /* if above is true */
196 Expr *partition_constraint; /* for attach partition validation */
197 /* true, if validating default due to some other attach/detach */
198 bool validate_default;
199 /* Objects to rebuild after completing ALTER TYPE operations */
200 List *changedConstraintOids; /* OIDs of constraints to rebuild */
201 List *changedConstraintDefs; /* string definitions of same */
202 List *changedIndexOids; /* OIDs of indexes to rebuild */
203 List *changedIndexDefs; /* string definitions of same */
204 char *replicaIdentityIndex; /* index to reset as REPLICA IDENTITY */
205 char *clusterOnIndex; /* index to use for CLUSTER */
206 List *changedStatisticsOids; /* OIDs of statistics to rebuild */
207 List *changedStatisticsDefs; /* string definitions of same */
208 } AlteredTableInfo;
210 /* Struct describing one new constraint to check in Phase 3 scan */
211 /* Note: new not-null constraints are handled elsewhere */
212 typedef struct NewConstraint
214 char *name; /* Constraint name, or NULL if none */
215 ConstrType contype; /* CHECK or FOREIGN */
216 Oid refrelid; /* PK rel, if FOREIGN */
217 Oid refindid; /* OID of PK's index, if FOREIGN */
218 Oid conid; /* OID of pg_constraint entry, if FOREIGN */
219 Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
220 ExprState *qualstate; /* Execution state for CHECK expr */
221 } NewConstraint;
224 * Struct describing one new column value that needs to be computed during
225 * Phase 3 copy (this could be either a new column with a non-null default, or
226 * a column that we're changing the type of). Columns without such an entry
227 * are just copied from the old table during ATRewriteTable. Note that the
228 * expr is an expression over *old* table values, except when is_generated
229 * is true; then it is an expression over columns of the *new* tuple.
231 typedef struct NewColumnValue
233 AttrNumber attnum; /* which column */
234 Expr *expr; /* expression to compute */
235 ExprState *exprstate; /* execution state */
236 bool is_generated; /* is it a GENERATED expression? */
237 } NewColumnValue;
240 * Error-reporting support for RemoveRelations
242 struct dropmsgstrings
244 char kind;
245 int nonexistent_code;
246 const char *nonexistent_msg;
247 const char *skipping_msg;
248 const char *nota_msg;
249 const char *drophint_msg;
252 static const struct dropmsgstrings dropmsgstringarray[] = {
253 {RELKIND_RELATION,
254 ERRCODE_UNDEFINED_TABLE,
255 gettext_noop("table \"%s\" does not exist"),
256 gettext_noop("table \"%s\" does not exist, skipping"),
257 gettext_noop("\"%s\" is not a table"),
258 gettext_noop("Use DROP TABLE to remove a table.")},
259 {RELKIND_SEQUENCE,
260 ERRCODE_UNDEFINED_TABLE,
261 gettext_noop("sequence \"%s\" does not exist"),
262 gettext_noop("sequence \"%s\" does not exist, skipping"),
263 gettext_noop("\"%s\" is not a sequence"),
264 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
265 {RELKIND_VIEW,
266 ERRCODE_UNDEFINED_TABLE,
267 gettext_noop("view \"%s\" does not exist"),
268 gettext_noop("view \"%s\" does not exist, skipping"),
269 gettext_noop("\"%s\" is not a view"),
270 gettext_noop("Use DROP VIEW to remove a view.")},
271 {RELKIND_MATVIEW,
272 ERRCODE_UNDEFINED_TABLE,
273 gettext_noop("materialized view \"%s\" does not exist"),
274 gettext_noop("materialized view \"%s\" does not exist, skipping"),
275 gettext_noop("\"%s\" is not a materialized view"),
276 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
277 {RELKIND_INDEX,
278 ERRCODE_UNDEFINED_OBJECT,
279 gettext_noop("index \"%s\" does not exist"),
280 gettext_noop("index \"%s\" does not exist, skipping"),
281 gettext_noop("\"%s\" is not an index"),
282 gettext_noop("Use DROP INDEX to remove an index.")},
283 {RELKIND_COMPOSITE_TYPE,
284 ERRCODE_UNDEFINED_OBJECT,
285 gettext_noop("type \"%s\" does not exist"),
286 gettext_noop("type \"%s\" does not exist, skipping"),
287 gettext_noop("\"%s\" is not a type"),
288 gettext_noop("Use DROP TYPE to remove a type.")},
289 {RELKIND_FOREIGN_TABLE,
290 ERRCODE_UNDEFINED_OBJECT,
291 gettext_noop("foreign table \"%s\" does not exist"),
292 gettext_noop("foreign table \"%s\" does not exist, skipping"),
293 gettext_noop("\"%s\" is not a foreign table"),
294 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
295 {RELKIND_PARTITIONED_TABLE,
296 ERRCODE_UNDEFINED_TABLE,
297 gettext_noop("table \"%s\" does not exist"),
298 gettext_noop("table \"%s\" does not exist, skipping"),
299 gettext_noop("\"%s\" is not a table"),
300 gettext_noop("Use DROP TABLE to remove a table.")},
301 {RELKIND_PARTITIONED_INDEX,
302 ERRCODE_UNDEFINED_OBJECT,
303 gettext_noop("index \"%s\" does not exist"),
304 gettext_noop("index \"%s\" does not exist, skipping"),
305 gettext_noop("\"%s\" is not an index"),
306 gettext_noop("Use DROP INDEX to remove an index.")},
307 {'\0', 0, NULL, NULL, NULL, NULL}
310 /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
311 struct DropRelationCallbackState
313 /* These fields are set by RemoveRelations: */
314 char expected_relkind;
315 LOCKMODE heap_lockmode;
316 /* These fields are state to track which subsidiary locks are held: */
317 Oid heapOid;
318 Oid partParentOid;
319 /* These fields are passed back by RangeVarCallbackForDropRelation: */
320 char actual_relkind;
321 char actual_relpersistence;
324 /* Alter table target-type flags for ATSimplePermissions */
325 #define ATT_TABLE 0x0001
326 #define ATT_VIEW 0x0002
327 #define ATT_MATVIEW 0x0004
328 #define ATT_INDEX 0x0008
329 #define ATT_COMPOSITE_TYPE 0x0010
330 #define ATT_FOREIGN_TABLE 0x0020
331 #define ATT_PARTITIONED_INDEX 0x0040
332 #define ATT_SEQUENCE 0x0080
335 * ForeignTruncateInfo
337 * Information related to truncation of foreign tables. This is used for
338 * the elements in a hash table. It uses the server OID as lookup key,
339 * and includes a per-server list of all foreign tables involved in the
340 * truncation.
342 typedef struct ForeignTruncateInfo
344 Oid serverid;
345 List *rels;
346 } ForeignTruncateInfo;
349 * Partition tables are expected to be dropped when the parent partitioned
350 * table gets dropped. Hence for partitioning we use AUTO dependency.
351 * Otherwise, for regular inheritance use NORMAL dependency.
353 #define child_dependency_type(child_is_partition) \
354 ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
356 static void truncate_check_rel(Oid relid, Form_pg_class reltuple);
357 static void truncate_check_perms(Oid relid, Form_pg_class reltuple);
358 static void truncate_check_activity(Relation rel);
359 static void RangeVarCallbackForTruncate(const RangeVar *relation,
360 Oid relId, Oid oldRelId, void *arg);
361 static List *MergeAttributes(List *columns, const List *supers, char relpersistence,
362 bool is_partition, List **supconstr);
363 static List *MergeCheckConstraint(List *constraints, const char *name, Node *expr);
364 static void MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef);
365 static ColumnDef *MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef);
366 static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition);
367 static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
368 static void StoreCatalogInheritance(Oid relationId, List *supers,
369 bool child_is_partition);
370 static void StoreCatalogInheritance1(Oid relationId, Oid parentOid,
371 int32 seqNumber, Relation inhRelation,
372 bool child_is_partition);
373 static int findAttrByName(const char *attributeName, const List *columns);
374 static void AlterIndexNamespaces(Relation classRel, Relation rel,
375 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved);
376 static void AlterSeqNamespaces(Relation classRel, Relation rel,
377 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
378 LOCKMODE lockmode);
379 static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd,
380 bool recurse, bool recursing, LOCKMODE lockmode);
381 static bool ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
382 Relation rel, HeapTuple contuple, List **otherrelids,
383 LOCKMODE lockmode);
384 static ObjectAddress ATExecValidateConstraint(List **wqueue,
385 Relation rel, char *constrName,
386 bool recurse, bool recursing, LOCKMODE lockmode);
387 static int transformColumnNameList(Oid relId, List *colList,
388 int16 *attnums, Oid *atttypids);
389 static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
390 List **attnamelist,
391 int16 *attnums, Oid *atttypids,
392 Oid *opclasses);
393 static Oid transformFkeyCheckAttrs(Relation pkrel,
394 int numattrs, int16 *attnums,
395 Oid *opclasses);
396 static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts);
397 static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId,
398 Oid *funcid);
399 static void validateForeignKeyConstraint(char *conname,
400 Relation rel, Relation pkrel,
401 Oid pkindOid, Oid constraintOid);
402 static void CheckAlterTableIsSafe(Relation rel);
403 static void ATController(AlterTableStmt *parsetree,
404 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
405 AlterTableUtilityContext *context);
406 static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
407 bool recurse, bool recursing, LOCKMODE lockmode,
408 AlterTableUtilityContext *context);
409 static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
410 AlterTableUtilityContext *context);
411 static void ATExecCmd(List **wqueue, AlteredTableInfo *tab,
412 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
413 AlterTableUtilityContext *context);
414 static AlterTableCmd *ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab,
415 Relation rel, AlterTableCmd *cmd,
416 bool recurse, LOCKMODE lockmode,
417 AlterTablePass cur_pass,
418 AlterTableUtilityContext *context);
419 static void ATRewriteTables(AlterTableStmt *parsetree,
420 List **wqueue, LOCKMODE lockmode,
421 AlterTableUtilityContext *context);
422 static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
423 static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
424 static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets);
425 static void ATSimpleRecursion(List **wqueue, Relation rel,
426 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
427 AlterTableUtilityContext *context);
428 static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode);
429 static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
430 LOCKMODE lockmode,
431 AlterTableUtilityContext *context);
432 static List *find_typed_table_dependencies(Oid typeOid, const char *typeName,
433 DropBehavior behavior);
434 static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
435 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
436 AlterTableUtilityContext *context);
437 static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
438 Relation rel, AlterTableCmd **cmd,
439 bool recurse, bool recursing,
440 LOCKMODE lockmode, AlterTablePass cur_pass,
441 AlterTableUtilityContext *context);
442 static bool check_for_column_name_collision(Relation rel, const char *colname,
443 bool if_not_exists);
444 static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid);
445 static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid);
446 static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing);
447 static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode);
448 static void ATPrepSetNotNull(List **wqueue, Relation rel,
449 AlterTableCmd *cmd, bool recurse, bool recursing,
450 LOCKMODE lockmode,
451 AlterTableUtilityContext *context);
452 static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
453 const char *colName, LOCKMODE lockmode);
454 static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
455 const char *colName, LOCKMODE lockmode);
456 static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr);
457 static bool ConstraintImpliedByRelConstraint(Relation scanrel,
458 List *testConstraint, List *provenConstraint);
459 static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName,
460 Node *newDefault, LOCKMODE lockmode);
461 static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
462 Node *newDefault);
463 static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName,
464 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
465 static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName,
466 Node *def, LOCKMODE lockmode, bool recurse, bool recursing);
467 static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
468 bool recurse, bool recursing);
469 static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
470 Node *newExpr, LOCKMODE lockmode);
471 static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode);
472 static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode);
473 static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum,
474 Node *newValue, LOCKMODE lockmode);
475 static ObjectAddress ATExecSetOptions(Relation rel, const char *colName,
476 Node *options, bool isReset, LOCKMODE lockmode);
477 static ObjectAddress ATExecSetStorage(Relation rel, const char *colName,
478 Node *newValue, LOCKMODE lockmode);
479 static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
480 AlterTableCmd *cmd, LOCKMODE lockmode,
481 AlterTableUtilityContext *context);
482 static ObjectAddress ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
483 DropBehavior behavior,
484 bool recurse, bool recursing,
485 bool missing_ok, LOCKMODE lockmode,
486 ObjectAddresses *addrs);
487 static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
488 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
489 static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
490 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode);
491 static ObjectAddress ATExecAddConstraint(List **wqueue,
492 AlteredTableInfo *tab, Relation rel,
493 Constraint *newConstraint, bool recurse, bool is_readd,
494 LOCKMODE lockmode);
495 static char *ChooseForeignKeyConstraintNameAddition(List *colnames);
496 static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
497 IndexStmt *stmt, LOCKMODE lockmode);
498 static ObjectAddress ATAddCheckConstraint(List **wqueue,
499 AlteredTableInfo *tab, Relation rel,
500 Constraint *constr,
501 bool recurse, bool recursing, bool is_readd,
502 LOCKMODE lockmode);
503 static ObjectAddress ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab,
504 Relation rel, Constraint *fkconstraint,
505 bool recurse, bool recursing,
506 LOCKMODE lockmode);
507 static ObjectAddress addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint,
508 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
509 int numfks, int16 *pkattnum, int16 *fkattnum,
510 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
511 int numfkdelsetcols, int16 *fkdelsetcols,
512 bool old_check_ok,
513 Oid parentDelTrigger, Oid parentUpdTrigger);
514 static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
515 int numfksetcols, const int16 *fksetcolsattnums,
516 List *fksetcols);
517 static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint,
518 Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr,
519 int numfks, int16 *pkattnum, int16 *fkattnum,
520 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
521 int numfkdelsetcols, int16 *fkdelsetcols,
522 bool old_check_ok, LOCKMODE lockmode,
523 Oid parentInsTrigger, Oid parentUpdTrigger);
524 static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
525 Relation partitionRel);
526 static void CloneFkReferenced(Relation parentRel, Relation partitionRel);
527 static void CloneFkReferencing(List **wqueue, Relation parentRel,
528 Relation partRel);
529 static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
530 Constraint *fkconstraint, Oid constraintOid,
531 Oid indexOid,
532 Oid parentInsTrigger, Oid parentUpdTrigger,
533 Oid *insertTrigOid, Oid *updateTrigOid);
534 static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid,
535 Constraint *fkconstraint, Oid constraintOid,
536 Oid indexOid,
537 Oid parentDelTrigger, Oid parentUpdTrigger,
538 Oid *deleteTrigOid, Oid *updateTrigOid);
539 static bool tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
540 Oid partRelid,
541 Oid parentConstrOid, int numfks,
542 AttrNumber *mapped_conkey, AttrNumber *confkey,
543 Oid *conpfeqop,
544 Oid parentInsTrigger,
545 Oid parentUpdTrigger,
546 Relation trigrel);
547 static void GetForeignKeyActionTriggers(Relation trigrel,
548 Oid conoid, Oid confrelid, Oid conrelid,
549 Oid *deleteTriggerOid,
550 Oid *updateTriggerOid);
551 static void GetForeignKeyCheckTriggers(Relation trigrel,
552 Oid conoid, Oid confrelid, Oid conrelid,
553 Oid *insertTriggerOid,
554 Oid *updateTriggerOid);
555 static void ATExecDropConstraint(Relation rel, const char *constrName,
556 DropBehavior behavior,
557 bool recurse, bool recursing,
558 bool missing_ok, LOCKMODE lockmode);
559 static void ATPrepAlterColumnType(List **wqueue,
560 AlteredTableInfo *tab, Relation rel,
561 bool recurse, bool recursing,
562 AlterTableCmd *cmd, LOCKMODE lockmode,
563 AlterTableUtilityContext *context);
564 static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno);
565 static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
566 AlterTableCmd *cmd, LOCKMODE lockmode);
567 static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
568 Relation rel, AttrNumber attnum, const char *colName);
569 static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab);
570 static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab);
571 static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab);
572 static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
573 LOCKMODE lockmode);
574 static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
575 char *cmd, List **wqueue, LOCKMODE lockmode,
576 bool rewrite);
577 static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass,
578 Oid objid, Relation rel, List *domname,
579 const char *conname);
580 static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
581 static void TryReuseForeignKey(Oid oldId, Constraint *con);
582 static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName,
583 List *options, LOCKMODE lockmode);
584 static void change_owner_fix_column_acls(Oid relationOid,
585 Oid oldOwnerId, Oid newOwnerId);
586 static void change_owner_recurse_to_sequences(Oid relationOid,
587 Oid newOwnerId, LOCKMODE lockmode);
588 static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName,
589 LOCKMODE lockmode);
590 static void ATExecDropCluster(Relation rel, LOCKMODE lockmode);
591 static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname);
592 static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId);
593 static bool ATPrepChangePersistence(Relation rel, bool toLogged);
594 static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel,
595 const char *tablespacename, LOCKMODE lockmode);
596 static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode);
597 static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace);
598 static void ATExecSetRelOptions(Relation rel, List *defList,
599 AlterTableType operation,
600 LOCKMODE lockmode);
601 static void ATExecEnableDisableTrigger(Relation rel, const char *trigname,
602 char fires_when, bool skip_system, bool recurse,
603 LOCKMODE lockmode);
604 static void ATExecEnableDisableRule(Relation rel, const char *rulename,
605 char fires_when, LOCKMODE lockmode);
606 static void ATPrepAddInherit(Relation child_rel);
607 static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode);
608 static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode);
609 static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
610 DependencyType deptype);
611 static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode);
612 static void ATExecDropOf(Relation rel, LOCKMODE lockmode);
613 static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode);
614 static void ATExecGenericOptions(Relation rel, List *options);
615 static void ATExecSetRowSecurity(Relation rel, bool rls);
616 static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls);
617 static ObjectAddress ATExecSetCompression(Relation rel,
618 const char *column, Node *newValue, LOCKMODE lockmode);
620 static void index_copy_data(Relation rel, RelFileLocator newrlocator);
621 static const char *storage_name(char c);
623 static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
624 Oid oldRelOid, void *arg);
625 static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
626 Oid oldrelid, void *arg);
627 static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec);
628 static void ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
629 List **partexprs, Oid *partopclass, Oid *partcollation,
630 PartitionStrategy strategy);
631 static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition);
632 static void RemoveInheritance(Relation child_rel, Relation parent_rel,
633 bool expect_detached);
634 static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
635 PartitionCmd *cmd,
636 AlterTableUtilityContext *context);
637 static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel);
638 static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
639 List *partConstraint,
640 bool validate_default);
641 static void CloneRowTriggersToPartition(Relation parent, Relation partition);
642 static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel);
643 static void DropClonedTriggersFromPartition(Oid partitionId);
644 static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab,
645 Relation rel, RangeVar *name,
646 bool concurrent);
647 static void DetachPartitionFinalize(Relation rel, Relation partRel,
648 bool concurrent, Oid defaultPartOid);
649 static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name);
650 static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx,
651 RangeVar *name);
652 static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl);
653 static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx,
654 Relation partitionTbl);
655 static List *GetParentedForeignKeyRefs(Relation partition);
656 static void ATDetachCheckNoForeignKeyRefs(Relation partition);
657 static char GetAttributeCompression(Oid atttypid, const char *compression);
658 static char GetAttributeStorage(Oid atttypid, const char *storagemode);
660 static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab,
661 Relation rel, PartitionCmd *cmd,
662 AlterTableUtilityContext *context);
663 static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
664 PartitionCmd *cmd, AlterTableUtilityContext *context);
666 /* ----------------------------------------------------------------
667 * DefineRelation
668 * Creates a new relation.
670 * stmt carries parsetree information from an ordinary CREATE TABLE statement.
671 * The other arguments are used to extend the behavior for other cases:
672 * relkind: relkind to assign to the new relation
673 * ownerId: if not InvalidOid, use this as the new relation's owner.
674 * typaddress: if not null, it's set to the pg_type entry's address.
675 * queryString: for error reporting
677 * Note that permissions checks are done against current user regardless of
678 * ownerId. A nonzero ownerId is used when someone is creating a relation
679 * "on behalf of" someone else, so we still want to see that the current user
680 * has permissions to do it.
682 * If successful, returns the address of the new relation.
683 * ----------------------------------------------------------------
685 ObjectAddress
686 DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
687 ObjectAddress *typaddress, const char *queryString)
689 char relname[NAMEDATALEN];
690 Oid namespaceId;
691 Oid relationId;
692 Oid tablespaceId;
693 Relation rel;
694 TupleDesc descriptor;
695 List *inheritOids;
696 List *old_constraints;
697 List *rawDefaults;
698 List *cookedDefaults;
699 Datum reloptions;
700 ListCell *listptr;
701 AttrNumber attnum;
702 bool partitioned;
703 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
704 Oid ofTypeId;
705 ObjectAddress address;
706 LOCKMODE parentLockmode;
707 Oid accessMethodId = InvalidOid;
710 * Truncate relname to appropriate length (probably a waste of time, as
711 * parser should have done this already).
713 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
716 * Check consistency of arguments
718 if (stmt->oncommit != ONCOMMIT_NOOP
719 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
720 ereport(ERROR,
721 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
722 errmsg("ON COMMIT can only be used on temporary tables")));
724 if (stmt->partspec != NULL)
726 if (relkind != RELKIND_RELATION)
727 elog(ERROR, "unexpected relkind: %d", (int) relkind);
729 relkind = RELKIND_PARTITIONED_TABLE;
730 partitioned = true;
732 else
733 partitioned = false;
736 * Look up the namespace in which we are supposed to create the relation,
737 * check we have permission to create there, lock it against concurrent
738 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
739 * namespace is selected.
741 namespaceId =
742 RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL);
745 * Security check: disallow creating temp tables from security-restricted
746 * code. This is needed because calling code might not expect untrusted
747 * tables to appear in pg_temp at the front of its search path.
749 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
750 && InSecurityRestrictedOperation())
751 ereport(ERROR,
752 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
753 errmsg("cannot create temporary table within security-restricted operation")));
756 * Determine the lockmode to use when scanning parents. A self-exclusive
757 * lock is needed here.
759 * For regular inheritance, if two backends attempt to add children to the
760 * same parent simultaneously, and that parent has no pre-existing
761 * children, then both will attempt to update the parent's relhassubclass
762 * field, leading to a "tuple concurrently updated" error. Also, this
763 * interlocks against a concurrent ANALYZE on the parent table, which
764 * might otherwise be attempting to clear the parent's relhassubclass
765 * field, if its previous children were recently dropped.
767 * If the child table is a partition, then we instead grab an exclusive
768 * lock on the parent because its partition descriptor will be changed by
769 * addition of the new partition.
771 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
772 ShareUpdateExclusiveLock);
774 /* Determine the list of OIDs of the parents. */
775 inheritOids = NIL;
776 foreach(listptr, stmt->inhRelations)
778 RangeVar *rv = (RangeVar *) lfirst(listptr);
779 Oid parentOid;
781 parentOid = RangeVarGetRelid(rv, parentLockmode, false);
784 * Reject duplications in the list of parents.
786 if (list_member_oid(inheritOids, parentOid))
787 ereport(ERROR,
788 (errcode(ERRCODE_DUPLICATE_TABLE),
789 errmsg("relation \"%s\" would be inherited from more than once",
790 get_rel_name(parentOid))));
792 inheritOids = lappend_oid(inheritOids, parentOid);
796 * Select tablespace to use: an explicitly indicated one, or (in the case
797 * of a partitioned table) the parent's, if it has one.
799 if (stmt->tablespacename)
801 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
803 if (partitioned && tablespaceId == MyDatabaseTableSpace)
804 ereport(ERROR,
805 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
806 errmsg("cannot specify default tablespace for partitioned relations")));
808 else if (stmt->partbound)
810 Assert(list_length(inheritOids) == 1);
811 tablespaceId = get_rel_tablespace(linitial_oid(inheritOids));
813 else
814 tablespaceId = InvalidOid;
816 /* still nothing? use the default */
817 if (!OidIsValid(tablespaceId))
818 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
819 partitioned);
821 /* Check permissions except when using database's default */
822 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
824 AclResult aclresult;
826 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(),
827 ACL_CREATE);
828 if (aclresult != ACLCHECK_OK)
829 aclcheck_error(aclresult, OBJECT_TABLESPACE,
830 get_tablespace_name(tablespaceId));
833 /* In all cases disallow placing user relations in pg_global */
834 if (tablespaceId == GLOBALTABLESPACE_OID)
835 ereport(ERROR,
836 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
837 errmsg("only shared relations can be placed in pg_global tablespace")));
839 /* Identify user ID that will own the table */
840 if (!OidIsValid(ownerId))
841 ownerId = GetUserId();
844 * Parse and validate reloptions, if any.
846 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
847 true, false);
849 switch (relkind)
851 case RELKIND_VIEW:
852 (void) view_reloptions(reloptions, true);
853 break;
854 case RELKIND_PARTITIONED_TABLE:
855 (void) partitioned_table_reloptions(reloptions, true);
856 break;
857 default:
858 (void) heap_reloptions(relkind, reloptions, true);
861 if (stmt->ofTypename)
863 AclResult aclresult;
865 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
867 aclresult = object_aclcheck(TypeRelationId, ofTypeId, GetUserId(), ACL_USAGE);
868 if (aclresult != ACLCHECK_OK)
869 aclcheck_error_type(aclresult, ofTypeId);
871 else
872 ofTypeId = InvalidOid;
875 * Look up inheritance ancestors and generate relation schema, including
876 * inherited attributes. (Note that stmt->tableElts is destructively
877 * modified by MergeAttributes.)
879 stmt->tableElts =
880 MergeAttributes(stmt->tableElts, inheritOids,
881 stmt->relation->relpersistence,
882 stmt->partbound != NULL,
883 &old_constraints);
886 * Create a tuple descriptor from the relation schema. Note that this
887 * deals with column names, types, and not-null constraints, but not
888 * default values or CHECK constraints; we handle those below.
890 descriptor = BuildDescForRelation(stmt->tableElts);
893 * Find columns with default values and prepare for insertion of the
894 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
895 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
896 * while raw defaults go into a list of RawColumnDefault structs that will
897 * be processed by AddRelationNewConstraints. (We can't deal with raw
898 * expressions until we can do transformExpr.)
900 * We can set the atthasdef flags now in the tuple descriptor; this just
901 * saves StoreAttrDefault from having to do an immediate update of the
902 * pg_attribute rows.
904 rawDefaults = NIL;
905 cookedDefaults = NIL;
906 attnum = 0;
908 foreach(listptr, stmt->tableElts)
910 ColumnDef *colDef = lfirst(listptr);
911 Form_pg_attribute attr;
913 attnum++;
914 attr = TupleDescAttr(descriptor, attnum - 1);
916 if (colDef->raw_default != NULL)
918 RawColumnDefault *rawEnt;
920 Assert(colDef->cooked_default == NULL);
922 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
923 rawEnt->attnum = attnum;
924 rawEnt->raw_default = colDef->raw_default;
925 rawEnt->missingMode = false;
926 rawEnt->generated = colDef->generated;
927 rawDefaults = lappend(rawDefaults, rawEnt);
928 attr->atthasdef = true;
930 else if (colDef->cooked_default != NULL)
932 CookedConstraint *cooked;
934 cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint));
935 cooked->contype = CONSTR_DEFAULT;
936 cooked->conoid = InvalidOid; /* until created */
937 cooked->name = NULL;
938 cooked->attnum = attnum;
939 cooked->expr = colDef->cooked_default;
940 cooked->skip_validation = false;
941 cooked->is_local = true; /* not used for defaults */
942 cooked->inhcount = 0; /* ditto */
943 cooked->is_no_inherit = false;
944 cookedDefaults = lappend(cookedDefaults, cooked);
945 attr->atthasdef = true;
950 * For relations with table AM and partitioned tables, select access
951 * method to use: an explicitly indicated one, or (in the case of a
952 * partitioned table) the parent's, if it has one.
954 if (stmt->accessMethod != NULL)
956 Assert(RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE);
957 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
959 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
961 if (stmt->partbound)
963 Assert(list_length(inheritOids) == 1);
964 accessMethodId = get_rel_relam(linitial_oid(inheritOids));
967 if (RELKIND_HAS_TABLE_AM(relkind) && !OidIsValid(accessMethodId))
968 accessMethodId = get_table_am_oid(default_table_access_method, false);
972 * Create the relation. Inherited defaults and constraints are passed in
973 * for immediate handling --- since they don't need parsing, they can be
974 * stored immediately.
976 relationId = heap_create_with_catalog(relname,
977 namespaceId,
978 tablespaceId,
979 InvalidOid,
980 InvalidOid,
981 ofTypeId,
982 ownerId,
983 accessMethodId,
984 descriptor,
985 list_concat(cookedDefaults,
986 old_constraints),
987 relkind,
988 stmt->relation->relpersistence,
989 false,
990 false,
991 stmt->oncommit,
992 reloptions,
993 true,
994 allowSystemTableMods,
995 false,
996 InvalidOid,
997 typaddress);
1000 * We must bump the command counter to make the newly-created relation
1001 * tuple visible for opening.
1003 CommandCounterIncrement();
1006 * Open the new relation and acquire exclusive lock on it. This isn't
1007 * really necessary for locking out other backends (since they can't see
1008 * the new rel anyway until we commit), but it keeps the lock manager from
1009 * complaining about deadlock risks.
1011 rel = relation_open(relationId, AccessExclusiveLock);
1014 * Now add any newly specified column default and generation expressions
1015 * to the new relation. These are passed to us in the form of raw
1016 * parsetrees; we need to transform them to executable expression trees
1017 * before they can be added. The most convenient way to do that is to
1018 * apply the parser's transformExpr routine, but transformExpr doesn't
1019 * work unless we have a pre-existing relation. So, the transformation has
1020 * to be postponed to this final step of CREATE TABLE.
1022 * This needs to be before processing the partitioning clauses because
1023 * those could refer to generated columns.
1025 if (rawDefaults)
1026 AddRelationNewConstraints(rel, rawDefaults, NIL,
1027 true, true, false, queryString);
1030 * Make column generation expressions visible for use by partitioning.
1032 CommandCounterIncrement();
1034 /* Process and store partition bound, if any. */
1035 if (stmt->partbound)
1037 PartitionBoundSpec *bound;
1038 ParseState *pstate;
1039 Oid parentId = linitial_oid(inheritOids),
1040 defaultPartOid;
1041 Relation parent,
1042 defaultRel = NULL;
1043 ParseNamespaceItem *nsitem;
1045 /* Already have strong enough lock on the parent */
1046 parent = table_open(parentId, NoLock);
1049 * We are going to try to validate the partition bound specification
1050 * against the partition key of parentRel, so it better have one.
1052 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1053 ereport(ERROR,
1054 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1055 errmsg("\"%s\" is not partitioned",
1056 RelationGetRelationName(parent))));
1059 * The partition constraint of the default partition depends on the
1060 * partition bounds of every other partition. It is possible that
1061 * another backend might be about to execute a query on the default
1062 * partition table, and that the query relies on previously cached
1063 * default partition constraints. We must therefore take a table lock
1064 * strong enough to prevent all queries on the default partition from
1065 * proceeding until we commit and send out a shared-cache-inval notice
1066 * that will make them update their index lists.
1068 * Order of locking: The relation being added won't be visible to
1069 * other backends until it is committed, hence here in
1070 * DefineRelation() the order of locking the default partition and the
1071 * relation being added does not matter. But at all other places we
1072 * need to lock the default relation before we lock the relation being
1073 * added or removed i.e. we should take the lock in same order at all
1074 * the places such that lock parent, lock default partition and then
1075 * lock the partition so as to avoid a deadlock.
1077 defaultPartOid =
1078 get_default_oid_from_partdesc(RelationGetPartitionDesc(parent,
1079 true));
1080 if (OidIsValid(defaultPartOid))
1081 defaultRel = table_open(defaultPartOid, AccessExclusiveLock);
1083 /* Transform the bound values */
1084 pstate = make_parsestate(NULL);
1085 pstate->p_sourcetext = queryString;
1088 * Add an nsitem containing this relation, so that transformExpr
1089 * called on partition bound expressions is able to report errors
1090 * using a proper context.
1092 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
1093 NULL, false, false);
1094 addNSItemToQuery(pstate, nsitem, false, true, true);
1096 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1099 * Check first that the new partition's bound is valid and does not
1100 * overlap with any of existing partitions of the parent.
1102 check_new_partition_bound(relname, parent, bound, pstate);
1105 * If the default partition exists, its partition constraints will
1106 * change after the addition of this new partition such that it won't
1107 * allow any row that qualifies for this new partition. So, check that
1108 * the existing data in the default partition satisfies the constraint
1109 * as it will exist after adding this partition.
1111 if (OidIsValid(defaultPartOid))
1113 check_default_partition_contents(parent, defaultRel, bound);
1114 /* Keep the lock until commit. */
1115 table_close(defaultRel, NoLock);
1118 /* Update the pg_class entry. */
1119 StorePartitionBound(rel, parent, bound);
1121 table_close(parent, NoLock);
1124 /* Store inheritance information for new rel. */
1125 StoreCatalogInheritance(relationId, inheritOids, stmt->partbound != NULL);
1128 * Process the partitioning specification (if any) and store the partition
1129 * key information into the catalog.
1131 if (partitioned)
1133 ParseState *pstate;
1134 int partnatts;
1135 AttrNumber partattrs[PARTITION_MAX_KEYS];
1136 Oid partopclass[PARTITION_MAX_KEYS];
1137 Oid partcollation[PARTITION_MAX_KEYS];
1138 List *partexprs = NIL;
1140 pstate = make_parsestate(NULL);
1141 pstate->p_sourcetext = queryString;
1143 partnatts = list_length(stmt->partspec->partParams);
1145 /* Protect fixed-size arrays here and in executor */
1146 if (partnatts > PARTITION_MAX_KEYS)
1147 ereport(ERROR,
1148 (errcode(ERRCODE_TOO_MANY_COLUMNS),
1149 errmsg("cannot partition using more than %d columns",
1150 PARTITION_MAX_KEYS)));
1153 * We need to transform the raw parsetrees corresponding to partition
1154 * expressions into executable expression trees. Like column defaults
1155 * and CHECK constraints, we could not have done the transformation
1156 * earlier.
1158 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1160 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1161 partattrs, &partexprs, partopclass,
1162 partcollation, stmt->partspec->strategy);
1164 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1165 partexprs,
1166 partopclass, partcollation);
1168 /* make it all visible */
1169 CommandCounterIncrement();
1173 * If we're creating a partition, create now all the indexes, triggers,
1174 * FKs defined in the parent.
1176 * We can't do it earlier, because DefineIndex wants to know the partition
1177 * key which we just stored.
1179 if (stmt->partbound)
1181 Oid parentId = linitial_oid(inheritOids);
1182 Relation parent;
1183 List *idxlist;
1184 ListCell *cell;
1186 /* Already have strong enough lock on the parent */
1187 parent = table_open(parentId, NoLock);
1188 idxlist = RelationGetIndexList(parent);
1191 * For each index in the parent table, create one in the partition
1193 foreach(cell, idxlist)
1195 Relation idxRel = index_open(lfirst_oid(cell), AccessShareLock);
1196 AttrMap *attmap;
1197 IndexStmt *idxstmt;
1198 Oid constraintOid;
1200 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1202 if (idxRel->rd_index->indisunique)
1203 ereport(ERROR,
1204 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1205 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1206 RelationGetRelationName(parent)),
1207 errdetail("Table \"%s\" contains indexes that are unique.",
1208 RelationGetRelationName(parent))));
1209 else
1211 index_close(idxRel, AccessShareLock);
1212 continue;
1216 attmap = build_attrmap_by_name(RelationGetDescr(rel),
1217 RelationGetDescr(parent),
1218 false);
1219 idxstmt =
1220 generateClonedIndexStmt(NULL, idxRel,
1221 attmap, &constraintOid);
1222 DefineIndex(RelationGetRelid(rel),
1223 idxstmt,
1224 InvalidOid,
1225 RelationGetRelid(idxRel),
1226 constraintOid,
1228 false, false, false, false, false);
1230 index_close(idxRel, AccessShareLock);
1233 list_free(idxlist);
1236 * If there are any row-level triggers, clone them to the new
1237 * partition.
1239 if (parent->trigdesc != NULL)
1240 CloneRowTriggersToPartition(parent, rel);
1243 * And foreign keys too. Note that because we're freshly creating the
1244 * table, there is no need to verify these new constraints.
1246 CloneForeignKeyConstraints(NULL, parent, rel);
1248 table_close(parent, NoLock);
1252 * Now add any newly specified CHECK constraints to the new relation. Same
1253 * as for defaults above, but these need to come after partitioning is set
1254 * up.
1256 if (stmt->constraints)
1257 AddRelationNewConstraints(rel, NIL, stmt->constraints,
1258 true, true, false, queryString);
1260 ObjectAddressSet(address, RelationRelationId, relationId);
1263 * Clean up. We keep lock on new relation (although it shouldn't be
1264 * visible to anyone else anyway, until commit).
1266 relation_close(rel, NoLock);
1268 return address;
1272 * BuildDescForRelation
1274 * Given a list of ColumnDef nodes, build a TupleDesc.
1276 * Note: This is only for the limited purpose of table and view creation. Not
1277 * everything is filled in. A real tuple descriptor should be obtained from
1278 * the relcache.
1280 TupleDesc
1281 BuildDescForRelation(const List *columns)
1283 int natts;
1284 AttrNumber attnum;
1285 ListCell *l;
1286 TupleDesc desc;
1287 char *attname;
1288 Oid atttypid;
1289 int32 atttypmod;
1290 Oid attcollation;
1291 int attdim;
1294 * allocate a new tuple descriptor
1296 natts = list_length(columns);
1297 desc = CreateTemplateTupleDesc(natts);
1299 attnum = 0;
1301 foreach(l, columns)
1303 ColumnDef *entry = lfirst(l);
1304 AclResult aclresult;
1305 Form_pg_attribute att;
1308 * for each entry in the list, get the name and type information from
1309 * the list and have TupleDescInitEntry fill in the attribute
1310 * information we need.
1312 attnum++;
1314 attname = entry->colname;
1315 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1317 aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1318 if (aclresult != ACLCHECK_OK)
1319 aclcheck_error_type(aclresult, atttypid);
1321 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1322 attdim = list_length(entry->typeName->arrayBounds);
1323 if (attdim > PG_INT16_MAX)
1324 ereport(ERROR,
1325 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1326 errmsg("too many array dimensions"));
1328 if (entry->typeName->setof)
1329 ereport(ERROR,
1330 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1331 errmsg("column \"%s\" cannot be declared SETOF",
1332 attname)));
1334 TupleDescInitEntry(desc, attnum, attname,
1335 atttypid, atttypmod, attdim);
1336 att = TupleDescAttr(desc, attnum - 1);
1338 /* Override TupleDescInitEntry's settings as requested */
1339 TupleDescInitEntryCollation(desc, attnum, attcollation);
1341 /* Fill in additional stuff not handled by TupleDescInitEntry */
1342 att->attnotnull = entry->is_not_null;
1343 att->attislocal = entry->is_local;
1344 att->attinhcount = entry->inhcount;
1345 att->attidentity = entry->identity;
1346 att->attgenerated = entry->generated;
1347 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1348 if (entry->storage)
1349 att->attstorage = entry->storage;
1350 else if (entry->storage_name)
1351 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1354 return desc;
1358 * Emit the right error or warning message for a "DROP" command issued on a
1359 * non-existent relation
1361 static void
1362 DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
1364 const struct dropmsgstrings *rentry;
1366 if (rel->schemaname != NULL &&
1367 !OidIsValid(LookupNamespaceNoError(rel->schemaname)))
1369 if (!missing_ok)
1371 ereport(ERROR,
1372 (errcode(ERRCODE_UNDEFINED_SCHEMA),
1373 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1375 else
1377 ereport(NOTICE,
1378 (errmsg("schema \"%s\" does not exist, skipping",
1379 rel->schemaname)));
1381 return;
1384 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1386 if (rentry->kind == rightkind)
1388 if (!missing_ok)
1390 ereport(ERROR,
1391 (errcode(rentry->nonexistent_code),
1392 errmsg(rentry->nonexistent_msg, rel->relname)));
1394 else
1396 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1397 break;
1402 Assert(rentry->kind != '\0'); /* Should be impossible */
1406 * Emit the right error message for a "DROP" command issued on a
1407 * relation of the wrong type
1409 static void
1410 DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
1412 const struct dropmsgstrings *rentry;
1413 const struct dropmsgstrings *wentry;
1415 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1416 if (rentry->kind == rightkind)
1417 break;
1418 Assert(rentry->kind != '\0');
1420 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1421 if (wentry->kind == wrongkind)
1422 break;
1423 /* wrongkind could be something we don't have in our table... */
1425 ereport(ERROR,
1426 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1427 errmsg(rentry->nota_msg, relname),
1428 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1432 * RemoveRelations
1433 * Implements DROP TABLE, DROP INDEX, DROP SEQUENCE, DROP VIEW,
1434 * DROP MATERIALIZED VIEW, DROP FOREIGN TABLE
1436 void
1437 RemoveRelations(DropStmt *drop)
1439 ObjectAddresses *objects;
1440 char relkind;
1441 ListCell *cell;
1442 int flags = 0;
1443 LOCKMODE lockmode = AccessExclusiveLock;
1445 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1446 if (drop->concurrent)
1449 * Note that for temporary relations this lock may get upgraded later
1450 * on, but as no other session can access a temporary relation, this
1451 * is actually fine.
1453 lockmode = ShareUpdateExclusiveLock;
1454 Assert(drop->removeType == OBJECT_INDEX);
1455 if (list_length(drop->objects) != 1)
1456 ereport(ERROR,
1457 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1458 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1459 if (drop->behavior == DROP_CASCADE)
1460 ereport(ERROR,
1461 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1462 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1466 * First we identify all the relations, then we delete them in a single
1467 * performMultipleDeletions() call. This is to avoid unwanted DROP
1468 * RESTRICT errors if one of the relations depends on another.
1471 /* Determine required relkind */
1472 switch (drop->removeType)
1474 case OBJECT_TABLE:
1475 relkind = RELKIND_RELATION;
1476 break;
1478 case OBJECT_INDEX:
1479 relkind = RELKIND_INDEX;
1480 break;
1482 case OBJECT_SEQUENCE:
1483 relkind = RELKIND_SEQUENCE;
1484 break;
1486 case OBJECT_VIEW:
1487 relkind = RELKIND_VIEW;
1488 break;
1490 case OBJECT_MATVIEW:
1491 relkind = RELKIND_MATVIEW;
1492 break;
1494 case OBJECT_FOREIGN_TABLE:
1495 relkind = RELKIND_FOREIGN_TABLE;
1496 break;
1498 default:
1499 elog(ERROR, "unrecognized drop object type: %d",
1500 (int) drop->removeType);
1501 relkind = 0; /* keep compiler quiet */
1502 break;
1505 /* Lock and validate each relation; build a list of object addresses */
1506 objects = new_object_addresses();
1508 foreach(cell, drop->objects)
1510 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1511 Oid relOid;
1512 ObjectAddress obj;
1513 struct DropRelationCallbackState state;
1516 * These next few steps are a great deal like relation_openrv, but we
1517 * don't bother building a relcache entry since we don't need it.
1519 * Check for shared-cache-inval messages before trying to access the
1520 * relation. This is needed to cover the case where the name
1521 * identifies a rel that has been dropped and recreated since the
1522 * start of our transaction: if we don't flush the old syscache entry,
1523 * then we'll latch onto that entry and suffer an error later.
1525 AcceptInvalidationMessages();
1527 /* Look up the appropriate relation using namespace search. */
1528 state.expected_relkind = relkind;
1529 state.heap_lockmode = drop->concurrent ?
1530 ShareUpdateExclusiveLock : AccessExclusiveLock;
1531 /* We must initialize these fields to show that no locks are held: */
1532 state.heapOid = InvalidOid;
1533 state.partParentOid = InvalidOid;
1535 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1536 RangeVarCallbackForDropRelation,
1537 (void *) &state);
1539 /* Not there? */
1540 if (!OidIsValid(relOid))
1542 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1543 continue;
1547 * Decide if concurrent mode needs to be used here or not. The
1548 * callback retrieved the rel's persistence for us.
1550 if (drop->concurrent &&
1551 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1553 Assert(list_length(drop->objects) == 1 &&
1554 drop->removeType == OBJECT_INDEX);
1555 flags |= PERFORM_DELETION_CONCURRENTLY;
1559 * Concurrent index drop cannot be used with partitioned indexes,
1560 * either.
1562 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1563 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1564 ereport(ERROR,
1565 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1566 errmsg("cannot drop partitioned index \"%s\" concurrently",
1567 rel->relname)));
1570 * If we're told to drop a partitioned index, we must acquire lock on
1571 * all the children of its parent partitioned table before proceeding.
1572 * Otherwise we'd try to lock the child index partitions before their
1573 * tables, leading to potential deadlock against other sessions that
1574 * will lock those objects in the other order.
1576 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1577 (void) find_all_inheritors(state.heapOid,
1578 state.heap_lockmode,
1579 NULL);
1581 /* OK, we're ready to delete this one */
1582 obj.classId = RelationRelationId;
1583 obj.objectId = relOid;
1584 obj.objectSubId = 0;
1586 add_exact_object_address(&obj, objects);
1589 performMultipleDeletions(objects, drop->behavior, flags);
1591 free_object_addresses(objects);
1595 * Before acquiring a table lock, check whether we have sufficient rights.
1596 * In the case of DROP INDEX, also try to lock the table before the index.
1597 * Also, if the table to be dropped is a partition, we try to lock the parent
1598 * first.
1600 static void
1601 RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1602 void *arg)
1604 HeapTuple tuple;
1605 struct DropRelationCallbackState *state;
1606 char expected_relkind;
1607 bool is_partition;
1608 Form_pg_class classform;
1609 LOCKMODE heap_lockmode;
1610 bool invalid_system_index = false;
1612 state = (struct DropRelationCallbackState *) arg;
1613 heap_lockmode = state->heap_lockmode;
1616 * If we previously locked some other index's heap, and the name we're
1617 * looking up no longer refers to that relation, release the now-useless
1618 * lock.
1620 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1622 UnlockRelationOid(state->heapOid, heap_lockmode);
1623 state->heapOid = InvalidOid;
1627 * Similarly, if we previously locked some other partition's heap, and the
1628 * name we're looking up no longer refers to that relation, release the
1629 * now-useless lock.
1631 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1633 UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1634 state->partParentOid = InvalidOid;
1637 /* Didn't find a relation, so no need for locking or permission checks. */
1638 if (!OidIsValid(relOid))
1639 return;
1641 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1642 if (!HeapTupleIsValid(tuple))
1643 return; /* concurrently dropped, so nothing to do */
1644 classform = (Form_pg_class) GETSTRUCT(tuple);
1645 is_partition = classform->relispartition;
1647 /* Pass back some data to save lookups in RemoveRelations */
1648 state->actual_relkind = classform->relkind;
1649 state->actual_relpersistence = classform->relpersistence;
1652 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1653 * but RemoveRelations() can only pass one relkind for a given relation.
1654 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1655 * That means we must be careful before giving the wrong type error when
1656 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1657 * exists with indexes.
1659 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1660 expected_relkind = RELKIND_RELATION;
1661 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1662 expected_relkind = RELKIND_INDEX;
1663 else
1664 expected_relkind = classform->relkind;
1666 if (state->expected_relkind != expected_relkind)
1667 DropErrorMsgWrongType(rel->relname, classform->relkind,
1668 state->expected_relkind);
1670 /* Allow DROP to either table owner or schema owner */
1671 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1672 !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1673 aclcheck_error(ACLCHECK_NOT_OWNER,
1674 get_relkind_objtype(classform->relkind),
1675 rel->relname);
1678 * Check the case of a system index that might have been invalidated by a
1679 * failed concurrent process and allow its drop. For the time being, this
1680 * only concerns indexes of toast relations that became invalid during a
1681 * REINDEX CONCURRENTLY process.
1683 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1685 HeapTuple locTuple;
1686 Form_pg_index indexform;
1687 bool indisvalid;
1689 locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1690 if (!HeapTupleIsValid(locTuple))
1692 ReleaseSysCache(tuple);
1693 return;
1696 indexform = (Form_pg_index) GETSTRUCT(locTuple);
1697 indisvalid = indexform->indisvalid;
1698 ReleaseSysCache(locTuple);
1700 /* Mark object as being an invalid index of system catalogs */
1701 if (!indisvalid)
1702 invalid_system_index = true;
1705 /* In the case of an invalid index, it is fine to bypass this check */
1706 if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1707 ereport(ERROR,
1708 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1709 errmsg("permission denied: \"%s\" is a system catalog",
1710 rel->relname)));
1712 ReleaseSysCache(tuple);
1715 * In DROP INDEX, attempt to acquire lock on the parent table before
1716 * locking the index. index_drop() will need this anyway, and since
1717 * regular queries lock tables before their indexes, we risk deadlock if
1718 * we do it the other way around. No error if we don't find a pg_index
1719 * entry, though --- the relation may have been dropped. Note that this
1720 * code will execute for either plain or partitioned indexes.
1722 if (expected_relkind == RELKIND_INDEX &&
1723 relOid != oldRelOid)
1725 state->heapOid = IndexGetRelation(relOid, true);
1726 if (OidIsValid(state->heapOid))
1727 LockRelationOid(state->heapOid, heap_lockmode);
1731 * Similarly, if the relation is a partition, we must acquire lock on its
1732 * parent before locking the partition. That's because queries lock the
1733 * parent before its partitions, so we risk deadlock if we do it the other
1734 * way around.
1736 if (is_partition && relOid != oldRelOid)
1738 state->partParentOid = get_partition_parent(relOid, true);
1739 if (OidIsValid(state->partParentOid))
1740 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1745 * ExecuteTruncate
1746 * Executes a TRUNCATE command.
1748 * This is a multi-relation truncate. We first open and grab exclusive
1749 * lock on all relations involved, checking permissions and otherwise
1750 * verifying that the relation is OK for truncation. Note that if relations
1751 * are foreign tables, at this stage, we have not yet checked that their
1752 * foreign data in external data sources are OK for truncation. These are
1753 * checked when foreign data are actually truncated later. In CASCADE mode,
1754 * relations having FK references to the targeted relations are automatically
1755 * added to the group; in RESTRICT mode, we check that all FK references are
1756 * internal to the group that's being truncated. Finally all the relations
1757 * are truncated and reindexed.
1759 void
1760 ExecuteTruncate(TruncateStmt *stmt)
1762 List *rels = NIL;
1763 List *relids = NIL;
1764 List *relids_logged = NIL;
1765 ListCell *cell;
1768 * Open, exclusive-lock, and check all the explicitly-specified relations
1770 foreach(cell, stmt->relations)
1772 RangeVar *rv = lfirst(cell);
1773 Relation rel;
1774 bool recurse = rv->inh;
1775 Oid myrelid;
1776 LOCKMODE lockmode = AccessExclusiveLock;
1778 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1779 0, RangeVarCallbackForTruncate,
1780 NULL);
1782 /* don't throw error for "TRUNCATE foo, foo" */
1783 if (list_member_oid(relids, myrelid))
1784 continue;
1786 /* open the relation, we already hold a lock on it */
1787 rel = table_open(myrelid, NoLock);
1790 * RangeVarGetRelidExtended() has done most checks with its callback,
1791 * but other checks with the now-opened Relation remain.
1793 truncate_check_activity(rel);
1795 rels = lappend(rels, rel);
1796 relids = lappend_oid(relids, myrelid);
1798 /* Log this relation only if needed for logical decoding */
1799 if (RelationIsLogicallyLogged(rel))
1800 relids_logged = lappend_oid(relids_logged, myrelid);
1802 if (recurse)
1804 ListCell *child;
1805 List *children;
1807 children = find_all_inheritors(myrelid, lockmode, NULL);
1809 foreach(child, children)
1811 Oid childrelid = lfirst_oid(child);
1813 if (list_member_oid(relids, childrelid))
1814 continue;
1816 /* find_all_inheritors already got lock */
1817 rel = table_open(childrelid, NoLock);
1820 * It is possible that the parent table has children that are
1821 * temp tables of other backends. We cannot safely access
1822 * such tables (because of buffering issues), and the best
1823 * thing to do is to silently ignore them. Note that this
1824 * check is the same as one of the checks done in
1825 * truncate_check_activity() called below, still it is kept
1826 * here for simplicity.
1828 if (RELATION_IS_OTHER_TEMP(rel))
1830 table_close(rel, lockmode);
1831 continue;
1835 * Inherited TRUNCATE commands perform access permission
1836 * checks on the parent table only. So we skip checking the
1837 * children's permissions and don't call
1838 * truncate_check_perms() here.
1840 truncate_check_rel(RelationGetRelid(rel), rel->rd_rel);
1841 truncate_check_activity(rel);
1843 rels = lappend(rels, rel);
1844 relids = lappend_oid(relids, childrelid);
1846 /* Log this relation only if needed for logical decoding */
1847 if (RelationIsLogicallyLogged(rel))
1848 relids_logged = lappend_oid(relids_logged, childrelid);
1851 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1852 ereport(ERROR,
1853 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1854 errmsg("cannot truncate only a partitioned table"),
1855 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1858 ExecuteTruncateGuts(rels, relids, relids_logged,
1859 stmt->behavior, stmt->restart_seqs, false);
1861 /* And close the rels */
1862 foreach(cell, rels)
1864 Relation rel = (Relation) lfirst(cell);
1866 table_close(rel, NoLock);
1871 * ExecuteTruncateGuts
1873 * Internal implementation of TRUNCATE. This is called by the actual TRUNCATE
1874 * command (see above) as well as replication subscribers that execute a
1875 * replicated TRUNCATE action.
1877 * explicit_rels is the list of Relations to truncate that the command
1878 * specified. relids is the list of Oids corresponding to explicit_rels.
1879 * relids_logged is the list of Oids (a subset of relids) that require
1880 * WAL-logging. This is all a bit redundant, but the existing callers have
1881 * this information handy in this form.
1883 void
1884 ExecuteTruncateGuts(List *explicit_rels,
1885 List *relids,
1886 List *relids_logged,
1887 DropBehavior behavior, bool restart_seqs,
1888 bool run_as_table_owner)
1890 List *rels;
1891 List *seq_relids = NIL;
1892 HTAB *ft_htab = NULL;
1893 EState *estate;
1894 ResultRelInfo *resultRelInfos;
1895 ResultRelInfo *resultRelInfo;
1896 SubTransactionId mySubid;
1897 ListCell *cell;
1898 Oid *logrelids;
1901 * Check the explicitly-specified relations.
1903 * In CASCADE mode, suck in all referencing relations as well. This
1904 * requires multiple iterations to find indirectly-dependent relations. At
1905 * each phase, we need to exclusive-lock new rels before looking for their
1906 * dependencies, else we might miss something. Also, we check each rel as
1907 * soon as we open it, to avoid a faux pas such as holding lock for a long
1908 * time on a rel we have no permissions for.
1910 rels = list_copy(explicit_rels);
1911 if (behavior == DROP_CASCADE)
1913 for (;;)
1915 List *newrelids;
1917 newrelids = heap_truncate_find_FKs(relids);
1918 if (newrelids == NIL)
1919 break; /* nothing else to add */
1921 foreach(cell, newrelids)
1923 Oid relid = lfirst_oid(cell);
1924 Relation rel;
1926 rel = table_open(relid, AccessExclusiveLock);
1927 ereport(NOTICE,
1928 (errmsg("truncate cascades to table \"%s\"",
1929 RelationGetRelationName(rel))));
1930 truncate_check_rel(relid, rel->rd_rel);
1931 truncate_check_perms(relid, rel->rd_rel);
1932 truncate_check_activity(rel);
1933 rels = lappend(rels, rel);
1934 relids = lappend_oid(relids, relid);
1936 /* Log this relation only if needed for logical decoding */
1937 if (RelationIsLogicallyLogged(rel))
1938 relids_logged = lappend_oid(relids_logged, relid);
1944 * Check foreign key references. In CASCADE mode, this should be
1945 * unnecessary since we just pulled in all the references; but as a
1946 * cross-check, do it anyway if in an Assert-enabled build.
1948 #ifdef USE_ASSERT_CHECKING
1949 heap_truncate_check_FKs(rels, false);
1950 #else
1951 if (behavior == DROP_RESTRICT)
1952 heap_truncate_check_FKs(rels, false);
1953 #endif
1956 * If we are asked to restart sequences, find all the sequences, lock them
1957 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
1958 * We want to do this early since it's pointless to do all the truncation
1959 * work only to fail on sequence permissions.
1961 if (restart_seqs)
1963 foreach(cell, rels)
1965 Relation rel = (Relation) lfirst(cell);
1966 List *seqlist = getOwnedSequences(RelationGetRelid(rel));
1967 ListCell *seqcell;
1969 foreach(seqcell, seqlist)
1971 Oid seq_relid = lfirst_oid(seqcell);
1972 Relation seq_rel;
1974 seq_rel = relation_open(seq_relid, AccessExclusiveLock);
1976 /* This check must match AlterSequence! */
1977 if (!object_ownercheck(RelationRelationId, seq_relid, GetUserId()))
1978 aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_SEQUENCE,
1979 RelationGetRelationName(seq_rel));
1981 seq_relids = lappend_oid(seq_relids, seq_relid);
1983 relation_close(seq_rel, NoLock);
1988 /* Prepare to catch AFTER triggers. */
1989 AfterTriggerBeginQuery();
1992 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
1993 * each relation. We don't need to call ExecOpenIndices, though.
1995 * We put the ResultRelInfos in the es_opened_result_relations list, even
1996 * though we don't have a range table and don't populate the
1997 * es_result_relations array. That's a bit bogus, but it's enough to make
1998 * ExecGetTriggerResultRel() find them.
2000 estate = CreateExecutorState();
2001 resultRelInfos = (ResultRelInfo *)
2002 palloc(list_length(rels) * sizeof(ResultRelInfo));
2003 resultRelInfo = resultRelInfos;
2004 foreach(cell, rels)
2006 Relation rel = (Relation) lfirst(cell);
2008 InitResultRelInfo(resultRelInfo,
2009 rel,
2010 0, /* dummy rangetable index */
2011 NULL,
2013 estate->es_opened_result_relations =
2014 lappend(estate->es_opened_result_relations, resultRelInfo);
2015 resultRelInfo++;
2019 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2020 * truncating (this is because one of them might throw an error). Also, if
2021 * we were to allow them to prevent statement execution, that would need
2022 * to be handled here.
2024 resultRelInfo = resultRelInfos;
2025 foreach(cell, rels)
2027 UserContext ucxt;
2029 if (run_as_table_owner)
2030 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2031 &ucxt);
2032 ExecBSTruncateTriggers(estate, resultRelInfo);
2033 if (run_as_table_owner)
2034 RestoreUserContext(&ucxt);
2035 resultRelInfo++;
2039 * OK, truncate each table.
2041 mySubid = GetCurrentSubTransactionId();
2043 foreach(cell, rels)
2045 Relation rel = (Relation) lfirst(cell);
2047 /* Skip partitioned tables as there is nothing to do */
2048 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2049 continue;
2052 * Build the lists of foreign tables belonging to each foreign server
2053 * and pass each list to the foreign data wrapper's callback function,
2054 * so that each server can truncate its all foreign tables in bulk.
2055 * Each list is saved as a single entry in a hash table that uses the
2056 * server OID as lookup key.
2058 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2060 Oid serverid = GetForeignServerIdByRelId(RelationGetRelid(rel));
2061 bool found;
2062 ForeignTruncateInfo *ft_info;
2064 /* First time through, initialize hashtable for foreign tables */
2065 if (!ft_htab)
2067 HASHCTL hctl;
2069 memset(&hctl, 0, sizeof(HASHCTL));
2070 hctl.keysize = sizeof(Oid);
2071 hctl.entrysize = sizeof(ForeignTruncateInfo);
2072 hctl.hcxt = CurrentMemoryContext;
2074 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2075 32, /* start small and extend */
2076 &hctl,
2077 HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
2080 /* Find or create cached entry for the foreign table */
2081 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2082 if (!found)
2083 ft_info->rels = NIL;
2086 * Save the foreign table in the entry of the server that the
2087 * foreign table belongs to.
2089 ft_info->rels = lappend(ft_info->rels, rel);
2090 continue;
2094 * Normally, we need a transaction-safe truncation here. However, if
2095 * the table was either created in the current (sub)transaction or has
2096 * a new relfilenumber in the current (sub)transaction, then we can
2097 * just truncate it in-place, because a rollback would cause the whole
2098 * table or the current physical file to be thrown away anyway.
2100 if (rel->rd_createSubid == mySubid ||
2101 rel->rd_newRelfilelocatorSubid == mySubid)
2103 /* Immediate, non-rollbackable truncation is OK */
2104 heap_truncate_one_rel(rel);
2106 else
2108 Oid heap_relid;
2109 Oid toast_relid;
2110 ReindexParams reindex_params = {0};
2113 * This effectively deletes all rows in the table, and may be done
2114 * in a serializable transaction. In that case we must record a
2115 * rw-conflict in to this transaction from each transaction
2116 * holding a predicate lock on the table.
2118 CheckTableForSerializableConflictIn(rel);
2121 * Need the full transaction-safe pushups.
2123 * Create a new empty storage file for the relation, and assign it
2124 * as the relfilenumber value. The old storage file is scheduled
2125 * for deletion at commit.
2127 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2129 heap_relid = RelationGetRelid(rel);
2132 * The same for the toast table, if any.
2134 toast_relid = rel->rd_rel->reltoastrelid;
2135 if (OidIsValid(toast_relid))
2137 Relation toastrel = relation_open(toast_relid,
2138 AccessExclusiveLock);
2140 RelationSetNewRelfilenumber(toastrel,
2141 toastrel->rd_rel->relpersistence);
2142 table_close(toastrel, NoLock);
2146 * Reconstruct the indexes to match, and we're done.
2148 reindex_relation(NULL, heap_relid, REINDEX_REL_PROCESS_TOAST,
2149 &reindex_params);
2152 pgstat_count_truncate(rel);
2155 /* Now go through the hash table, and truncate foreign tables */
2156 if (ft_htab)
2158 ForeignTruncateInfo *ft_info;
2159 HASH_SEQ_STATUS seq;
2161 hash_seq_init(&seq, ft_htab);
2163 PG_TRY();
2165 while ((ft_info = hash_seq_search(&seq)) != NULL)
2167 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2169 /* truncate_check_rel() has checked that already */
2170 Assert(routine->ExecForeignTruncate != NULL);
2172 routine->ExecForeignTruncate(ft_info->rels,
2173 behavior,
2174 restart_seqs);
2177 PG_FINALLY();
2179 hash_destroy(ft_htab);
2181 PG_END_TRY();
2185 * Restart owned sequences if we were asked to.
2187 foreach(cell, seq_relids)
2189 Oid seq_relid = lfirst_oid(cell);
2191 ResetSequence(seq_relid);
2195 * Write a WAL record to allow this set of actions to be logically
2196 * decoded.
2198 * Assemble an array of relids so we can write a single WAL record for the
2199 * whole action.
2201 if (relids_logged != NIL)
2203 xl_heap_truncate xlrec;
2204 int i = 0;
2206 /* should only get here if wal_level >= logical */
2207 Assert(XLogLogicalInfoActive());
2209 logrelids = palloc(list_length(relids_logged) * sizeof(Oid));
2210 foreach(cell, relids_logged)
2211 logrelids[i++] = lfirst_oid(cell);
2213 xlrec.dbId = MyDatabaseId;
2214 xlrec.nrelids = list_length(relids_logged);
2215 xlrec.flags = 0;
2216 if (behavior == DROP_CASCADE)
2217 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2218 if (restart_seqs)
2219 xlrec.flags |= XLH_TRUNCATE_RESTART_SEQS;
2221 XLogBeginInsert();
2222 XLogRegisterData((char *) &xlrec, SizeOfHeapTruncate);
2223 XLogRegisterData((char *) logrelids, list_length(relids_logged) * sizeof(Oid));
2225 XLogSetRecordFlags(XLOG_INCLUDE_ORIGIN);
2227 (void) XLogInsert(RM_HEAP_ID, XLOG_HEAP_TRUNCATE);
2231 * Process all AFTER STATEMENT TRUNCATE triggers.
2233 resultRelInfo = resultRelInfos;
2234 foreach(cell, rels)
2236 UserContext ucxt;
2238 if (run_as_table_owner)
2239 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2240 &ucxt);
2241 ExecASTruncateTriggers(estate, resultRelInfo);
2242 if (run_as_table_owner)
2243 RestoreUserContext(&ucxt);
2244 resultRelInfo++;
2247 /* Handle queued AFTER triggers */
2248 AfterTriggerEndQuery(estate);
2250 /* We can clean up the EState now */
2251 FreeExecutorState(estate);
2254 * Close any rels opened by CASCADE (can't do this while EState still
2255 * holds refs)
2257 rels = list_difference_ptr(rels, explicit_rels);
2258 foreach(cell, rels)
2260 Relation rel = (Relation) lfirst(cell);
2262 table_close(rel, NoLock);
2267 * Check that a given relation is safe to truncate. Subroutine for
2268 * ExecuteTruncate() and RangeVarCallbackForTruncate().
2270 static void
2271 truncate_check_rel(Oid relid, Form_pg_class reltuple)
2273 char *relname = NameStr(reltuple->relname);
2276 * Only allow truncate on regular tables, foreign tables using foreign
2277 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2278 * latter are only being included here for the following checks; no
2279 * physical truncation will occur in their case.).
2281 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2283 Oid serverid = GetForeignServerIdByRelId(relid);
2284 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2286 if (!fdwroutine->ExecForeignTruncate)
2287 ereport(ERROR,
2288 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2289 errmsg("cannot truncate foreign table \"%s\"",
2290 relname)));
2292 else if (reltuple->relkind != RELKIND_RELATION &&
2293 reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2294 ereport(ERROR,
2295 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2296 errmsg("\"%s\" is not a table", relname)));
2299 * Most system catalogs can't be truncated at all, or at least not unless
2300 * allow_system_table_mods=on. As an exception, however, we allow
2301 * pg_largeobject to be truncated as part of pg_upgrade, because we need
2302 * to change its relfilenode to match the old cluster, and allowing a
2303 * TRUNCATE command to be executed is the easiest way of doing that.
2305 if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2306 && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2307 ereport(ERROR,
2308 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2309 errmsg("permission denied: \"%s\" is a system catalog",
2310 relname)));
2312 InvokeObjectTruncateHook(relid);
2316 * Check that current user has the permission to truncate given relation.
2318 static void
2319 truncate_check_perms(Oid relid, Form_pg_class reltuple)
2321 char *relname = NameStr(reltuple->relname);
2322 AclResult aclresult;
2324 /* Permissions checks */
2325 aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2326 if (aclresult != ACLCHECK_OK)
2327 aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2328 relname);
2332 * Set of extra sanity checks to check if a given relation is safe to
2333 * truncate. This is split with truncate_check_rel() as
2334 * RangeVarCallbackForTruncate() cannot open a Relation yet.
2336 static void
2337 truncate_check_activity(Relation rel)
2340 * Don't allow truncate on temp tables of other backends ... their local
2341 * buffer manager is not going to cope.
2343 if (RELATION_IS_OTHER_TEMP(rel))
2344 ereport(ERROR,
2345 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2346 errmsg("cannot truncate temporary tables of other sessions")));
2349 * Also check for active uses of the relation in the current transaction,
2350 * including open scans and pending AFTER trigger events.
2352 CheckTableNotInUse(rel, "TRUNCATE");
2356 * storage_name
2357 * returns the name corresponding to a typstorage/attstorage enum value
2359 static const char *
2360 storage_name(char c)
2362 switch (c)
2364 case TYPSTORAGE_PLAIN:
2365 return "PLAIN";
2366 case TYPSTORAGE_EXTERNAL:
2367 return "EXTERNAL";
2368 case TYPSTORAGE_EXTENDED:
2369 return "EXTENDED";
2370 case TYPSTORAGE_MAIN:
2371 return "MAIN";
2372 default:
2373 return "???";
2377 /*----------
2378 * MergeAttributes
2379 * Returns new schema given initial schema and superclasses.
2381 * Input arguments:
2382 * 'columns' is the column/attribute definition for the table. (It's a list
2383 * of ColumnDef's.) It is destructively changed.
2384 * 'supers' is a list of OIDs of parent relations, already locked by caller.
2385 * 'relpersistence' is the persistence type of the table.
2386 * 'is_partition' tells if the table is a partition.
2388 * Output arguments:
2389 * 'supconstr' receives a list of constraints belonging to the parents,
2390 * updated as necessary to be valid for the child.
2392 * Return value:
2393 * Completed schema list.
2395 * Notes:
2396 * The order in which the attributes are inherited is very important.
2397 * Intuitively, the inherited attributes should come first. If a table
2398 * inherits from multiple parents, the order of those attributes are
2399 * according to the order of the parents specified in CREATE TABLE.
2401 * Here's an example:
2403 * create table person (name text, age int4, location point);
2404 * create table emp (salary int4, manager text) inherits(person);
2405 * create table student (gpa float8) inherits (person);
2406 * create table stud_emp (percent int4) inherits (emp, student);
2408 * The order of the attributes of stud_emp is:
2410 * person {1:name, 2:age, 3:location}
2411 * / \
2412 * {6:gpa} student emp {4:salary, 5:manager}
2413 * \ /
2414 * stud_emp {7:percent}
2416 * If the same attribute name appears multiple times, then it appears
2417 * in the result table in the proper location for its first appearance.
2419 * Constraints (including not-null constraints) for the child table
2420 * are the union of all relevant constraints, from both the child schema
2421 * and parent tables.
2423 * The default value for a child column is defined as:
2424 * (1) If the child schema specifies a default, that value is used.
2425 * (2) If neither the child nor any parent specifies a default, then
2426 * the column will not have a default.
2427 * (3) If conflicting defaults are inherited from different parents
2428 * (and not overridden by the child), an error is raised.
2429 * (4) Otherwise the inherited default is used.
2431 * Note that the default-value infrastructure is used for generated
2432 * columns' expressions too, so most of the preceding paragraph applies
2433 * to generation expressions too. We insist that a child column be
2434 * generated if and only if its parent(s) are, but it need not have
2435 * the same generation expression.
2436 *----------
2438 static List *
2439 MergeAttributes(List *columns, const List *supers, char relpersistence,
2440 bool is_partition, List **supconstr)
2442 List *inh_columns = NIL;
2443 List *constraints = NIL;
2444 bool have_bogus_defaults = false;
2445 int child_attno;
2446 static Node bogus_marker = {0}; /* marks conflicting defaults */
2447 List *saved_columns = NIL;
2448 ListCell *lc;
2451 * Check for and reject tables with too many columns. We perform this
2452 * check relatively early for two reasons: (a) we don't run the risk of
2453 * overflowing an AttrNumber in subsequent code (b) an O(n^2) algorithm is
2454 * okay if we're processing <= 1600 columns, but could take minutes to
2455 * execute if the user attempts to create a table with hundreds of
2456 * thousands of columns.
2458 * Note that we also need to check that we do not exceed this figure after
2459 * including columns from inherited relations.
2461 if (list_length(columns) > MaxHeapAttributeNumber)
2462 ereport(ERROR,
2463 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2464 errmsg("tables can have at most %d columns",
2465 MaxHeapAttributeNumber)));
2468 * Check for duplicate names in the explicit list of attributes.
2470 * Although we might consider merging such entries in the same way that we
2471 * handle name conflicts for inherited attributes, it seems to make more
2472 * sense to assume such conflicts are errors.
2474 * We don't use foreach() here because we have two nested loops over the
2475 * columns list, with possible element deletions in the inner one. If we
2476 * used foreach_delete_current() it could only fix up the state of one of
2477 * the loops, so it seems cleaner to use looping over list indexes for
2478 * both loops. Note that any deletion will happen beyond where the outer
2479 * loop is, so its index never needs adjustment.
2481 for (int coldefpos = 0; coldefpos < list_length(columns); coldefpos++)
2483 ColumnDef *coldef = list_nth_node(ColumnDef, columns, coldefpos);
2485 if (!is_partition && coldef->typeName == NULL)
2488 * Typed table column option that does not belong to a column from
2489 * the type. This works because the columns from the type come
2490 * first in the list. (We omit this check for partition column
2491 * lists; those are processed separately below.)
2493 ereport(ERROR,
2494 (errcode(ERRCODE_UNDEFINED_COLUMN),
2495 errmsg("column \"%s\" does not exist",
2496 coldef->colname)));
2499 /* restpos scans all entries beyond coldef; incr is in loop body */
2500 for (int restpos = coldefpos + 1; restpos < list_length(columns);)
2502 ColumnDef *restdef = list_nth_node(ColumnDef, columns, restpos);
2504 if (strcmp(coldef->colname, restdef->colname) == 0)
2506 if (coldef->is_from_type)
2509 * merge the column options into the column from the type
2511 coldef->is_not_null = restdef->is_not_null;
2512 coldef->raw_default = restdef->raw_default;
2513 coldef->cooked_default = restdef->cooked_default;
2514 coldef->constraints = restdef->constraints;
2515 coldef->is_from_type = false;
2516 columns = list_delete_nth_cell(columns, restpos);
2518 else
2519 ereport(ERROR,
2520 (errcode(ERRCODE_DUPLICATE_COLUMN),
2521 errmsg("column \"%s\" specified more than once",
2522 coldef->colname)));
2524 else
2525 restpos++;
2530 * In case of a partition, there are no new column definitions, only dummy
2531 * ColumnDefs created for column constraints. Set them aside for now and
2532 * process them at the end.
2534 if (is_partition)
2536 saved_columns = columns;
2537 columns = NIL;
2541 * Scan the parents left-to-right, and merge their attributes to form a
2542 * list of inherited columns (inh_columns).
2544 child_attno = 0;
2545 foreach(lc, supers)
2547 Oid parent = lfirst_oid(lc);
2548 Relation relation;
2549 TupleDesc tupleDesc;
2550 TupleConstr *constr;
2551 AttrMap *newattmap;
2552 List *inherited_defaults;
2553 List *cols_with_defaults;
2554 ListCell *lc1;
2555 ListCell *lc2;
2557 /* caller already got lock */
2558 relation = table_open(parent, NoLock);
2561 * Check for active uses of the parent partitioned table in the
2562 * current transaction, such as being used in some manner by an
2563 * enclosing command.
2565 if (is_partition)
2566 CheckTableNotInUse(relation, "CREATE TABLE .. PARTITION OF");
2569 * We do not allow partitioned tables and partitions to participate in
2570 * regular inheritance.
2572 if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !is_partition)
2573 ereport(ERROR,
2574 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2575 errmsg("cannot inherit from partitioned table \"%s\"",
2576 RelationGetRelationName(relation))));
2577 if (relation->rd_rel->relispartition && !is_partition)
2578 ereport(ERROR,
2579 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2580 errmsg("cannot inherit from partition \"%s\"",
2581 RelationGetRelationName(relation))));
2583 if (relation->rd_rel->relkind != RELKIND_RELATION &&
2584 relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
2585 relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
2586 ereport(ERROR,
2587 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2588 errmsg("inherited relation \"%s\" is not a table or foreign table",
2589 RelationGetRelationName(relation))));
2592 * If the parent is permanent, so must be all of its partitions. Note
2593 * that inheritance allows that case.
2595 if (is_partition &&
2596 relation->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
2597 relpersistence == RELPERSISTENCE_TEMP)
2598 ereport(ERROR,
2599 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2600 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
2601 RelationGetRelationName(relation))));
2603 /* Permanent rels cannot inherit from temporary ones */
2604 if (relpersistence != RELPERSISTENCE_TEMP &&
2605 relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
2606 ereport(ERROR,
2607 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2608 errmsg(!is_partition
2609 ? "cannot inherit from temporary relation \"%s\""
2610 : "cannot create a permanent relation as partition of temporary relation \"%s\"",
2611 RelationGetRelationName(relation))));
2613 /* If existing rel is temp, it must belong to this session */
2614 if (relation->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
2615 !relation->rd_islocaltemp)
2616 ereport(ERROR,
2617 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2618 errmsg(!is_partition
2619 ? "cannot inherit from temporary relation of another session"
2620 : "cannot create as partition of temporary relation of another session")));
2623 * We should have an UNDER permission flag for this, but for now,
2624 * demand that creator of a child table own the parent.
2626 if (!object_ownercheck(RelationRelationId, RelationGetRelid(relation), GetUserId()))
2627 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(relation->rd_rel->relkind),
2628 RelationGetRelationName(relation));
2630 tupleDesc = RelationGetDescr(relation);
2631 constr = tupleDesc->constr;
2634 * newattmap->attnums[] will contain the child-table attribute numbers
2635 * for the attributes of this parent table. (They are not the same
2636 * for parents after the first one, nor if we have dropped columns.)
2638 newattmap = make_attrmap(tupleDesc->natts);
2640 /* We can't process inherited defaults until newattmap is complete. */
2641 inherited_defaults = cols_with_defaults = NIL;
2643 for (AttrNumber parent_attno = 1; parent_attno <= tupleDesc->natts;
2644 parent_attno++)
2646 Form_pg_attribute attribute = TupleDescAttr(tupleDesc,
2647 parent_attno - 1);
2648 char *attributeName = NameStr(attribute->attname);
2649 int exist_attno;
2650 ColumnDef *newdef;
2651 ColumnDef *mergeddef;
2654 * Ignore dropped columns in the parent.
2656 if (attribute->attisdropped)
2657 continue; /* leave newattmap->attnums entry as zero */
2660 * Create new column definition
2662 newdef = makeColumnDef(attributeName, attribute->atttypid,
2663 attribute->atttypmod, attribute->attcollation);
2664 newdef->is_not_null = attribute->attnotnull;
2665 newdef->storage = attribute->attstorage;
2666 newdef->generated = attribute->attgenerated;
2667 if (CompressionMethodIsValid(attribute->attcompression))
2668 newdef->compression =
2669 pstrdup(GetCompressionMethodName(attribute->attcompression));
2672 * Regular inheritance children are independent enough not to
2673 * inherit identity columns. But partitions are integral part of
2674 * a partitioned table and inherit identity column.
2676 if (is_partition)
2677 newdef->identity = attribute->attidentity;
2680 * Does it match some previously considered column from another
2681 * parent?
2683 exist_attno = findAttrByName(attributeName, inh_columns);
2684 if (exist_attno > 0)
2687 * Yes, try to merge the two column definitions.
2689 mergeddef = MergeInheritedAttribute(inh_columns, exist_attno, newdef);
2691 newattmap->attnums[parent_attno - 1] = exist_attno;
2694 * Partitions have only one parent, so conflict should never
2695 * occur.
2697 Assert(!is_partition);
2699 else
2702 * No, create a new inherited column
2704 newdef->inhcount = 1;
2705 newdef->is_local = false;
2706 inh_columns = lappend(inh_columns, newdef);
2708 newattmap->attnums[parent_attno - 1] = ++child_attno;
2709 mergeddef = newdef;
2713 * Locate default/generation expression if any
2715 if (attribute->atthasdef)
2717 Node *this_default;
2719 this_default = TupleDescGetDefault(tupleDesc, parent_attno);
2720 if (this_default == NULL)
2721 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
2722 parent_attno, RelationGetRelationName(relation));
2725 * If it's a GENERATED default, it might contain Vars that
2726 * need to be mapped to the inherited column(s)' new numbers.
2727 * We can't do that till newattmap is ready, so just remember
2728 * all the inherited default expressions for the moment.
2730 inherited_defaults = lappend(inherited_defaults, this_default);
2731 cols_with_defaults = lappend(cols_with_defaults, mergeddef);
2736 * Now process any inherited default expressions, adjusting attnos
2737 * using the completed newattmap map.
2739 forboth(lc1, inherited_defaults, lc2, cols_with_defaults)
2741 Node *this_default = (Node *) lfirst(lc1);
2742 ColumnDef *def = (ColumnDef *) lfirst(lc2);
2743 bool found_whole_row;
2745 /* Adjust Vars to match new table's column numbering */
2746 this_default = map_variable_attnos(this_default,
2747 1, 0,
2748 newattmap,
2749 InvalidOid, &found_whole_row);
2752 * For the moment we have to reject whole-row variables. We could
2753 * convert them, if we knew the new table's rowtype OID, but that
2754 * hasn't been assigned yet. (A variable could only appear in a
2755 * generation expression, so the error message is correct.)
2757 if (found_whole_row)
2758 ereport(ERROR,
2759 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2760 errmsg("cannot convert whole-row table reference"),
2761 errdetail("Generation expression for column \"%s\" contains a whole-row reference to table \"%s\".",
2762 def->colname,
2763 RelationGetRelationName(relation))));
2766 * If we already had a default from some prior parent, check to
2767 * see if they are the same. If so, no problem; if not, mark the
2768 * column as having a bogus default. Below, we will complain if
2769 * the bogus default isn't overridden by the child columns.
2771 Assert(def->raw_default == NULL);
2772 if (def->cooked_default == NULL)
2773 def->cooked_default = this_default;
2774 else if (!equal(def->cooked_default, this_default))
2776 def->cooked_default = &bogus_marker;
2777 have_bogus_defaults = true;
2782 * Now copy the CHECK constraints of this parent, adjusting attnos
2783 * using the completed newattmap map. Identically named constraints
2784 * are merged if possible, else we throw error.
2786 if (constr && constr->num_check > 0)
2788 ConstrCheck *check = constr->check;
2790 for (int i = 0; i < constr->num_check; i++)
2792 char *name = check[i].ccname;
2793 Node *expr;
2794 bool found_whole_row;
2796 /* ignore if the constraint is non-inheritable */
2797 if (check[i].ccnoinherit)
2798 continue;
2800 /* Adjust Vars to match new table's column numbering */
2801 expr = map_variable_attnos(stringToNode(check[i].ccbin),
2802 1, 0,
2803 newattmap,
2804 InvalidOid, &found_whole_row);
2807 * For the moment we have to reject whole-row variables. We
2808 * could convert them, if we knew the new table's rowtype OID,
2809 * but that hasn't been assigned yet.
2811 if (found_whole_row)
2812 ereport(ERROR,
2813 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2814 errmsg("cannot convert whole-row table reference"),
2815 errdetail("Constraint \"%s\" contains a whole-row reference to table \"%s\".",
2816 name,
2817 RelationGetRelationName(relation))));
2819 constraints = MergeCheckConstraint(constraints, name, expr);
2823 free_attrmap(newattmap);
2826 * Close the parent rel, but keep our lock on it until xact commit.
2827 * That will prevent someone else from deleting or ALTERing the parent
2828 * before the child is committed.
2830 table_close(relation, NoLock);
2834 * If we had no inherited attributes, the result columns are just the
2835 * explicitly declared columns. Otherwise, we need to merge the declared
2836 * columns into the inherited column list. Although, we never have any
2837 * explicitly declared columns if the table is a partition.
2839 if (inh_columns != NIL)
2841 int newcol_attno = 0;
2843 foreach(lc, columns)
2845 ColumnDef *newdef = lfirst_node(ColumnDef, lc);
2846 char *attributeName = newdef->colname;
2847 int exist_attno;
2850 * Partitions have only one parent and have no column definitions
2851 * of their own, so conflict should never occur.
2853 Assert(!is_partition);
2855 newcol_attno++;
2858 * Does it match some inherited column?
2860 exist_attno = findAttrByName(attributeName, inh_columns);
2861 if (exist_attno > 0)
2864 * Yes, try to merge the two column definitions.
2866 MergeChildAttribute(inh_columns, exist_attno, newcol_attno, newdef);
2868 else
2871 * No, attach new column unchanged to result columns.
2873 inh_columns = lappend(inh_columns, newdef);
2877 columns = inh_columns;
2880 * Check that we haven't exceeded the legal # of columns after merging
2881 * in inherited columns.
2883 if (list_length(columns) > MaxHeapAttributeNumber)
2884 ereport(ERROR,
2885 (errcode(ERRCODE_TOO_MANY_COLUMNS),
2886 errmsg("tables can have at most %d columns",
2887 MaxHeapAttributeNumber)));
2891 * Now that we have the column definition list for a partition, we can
2892 * check whether the columns referenced in the column constraint specs
2893 * actually exist. Also, we merge parent's not-null constraints and
2894 * defaults into each corresponding column definition.
2896 if (is_partition)
2898 foreach(lc, saved_columns)
2900 ColumnDef *restdef = lfirst(lc);
2901 bool found = false;
2902 ListCell *l;
2904 foreach(l, columns)
2906 ColumnDef *coldef = lfirst(l);
2908 if (strcmp(coldef->colname, restdef->colname) == 0)
2910 found = true;
2911 coldef->is_not_null |= restdef->is_not_null;
2914 * Check for conflicts related to generated columns.
2916 * Same rules as above: generated-ness has to match the
2917 * parent, but the contents of the generation expression
2918 * can be different.
2920 if (coldef->generated)
2922 if (restdef->raw_default && !restdef->generated)
2923 ereport(ERROR,
2924 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2925 errmsg("column \"%s\" inherits from generated column but specifies default",
2926 restdef->colname)));
2927 if (restdef->identity)
2928 ereport(ERROR,
2929 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2930 errmsg("column \"%s\" inherits from generated column but specifies identity",
2931 restdef->colname)));
2933 else
2935 if (restdef->generated)
2936 ereport(ERROR,
2937 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2938 errmsg("child column \"%s\" specifies generation expression",
2939 restdef->colname),
2940 errhint("A child table column cannot be generated unless its parent column is.")));
2944 * Override the parent's default value for this column
2945 * (coldef->cooked_default) with the partition's local
2946 * definition (restdef->raw_default), if there's one. It
2947 * should be physically impossible to get a cooked default
2948 * in the local definition or a raw default in the
2949 * inherited definition, but make sure they're nulls, for
2950 * future-proofing.
2952 Assert(restdef->cooked_default == NULL);
2953 Assert(coldef->raw_default == NULL);
2954 if (restdef->raw_default)
2956 coldef->raw_default = restdef->raw_default;
2957 coldef->cooked_default = NULL;
2962 /* complain for constraints on columns not in parent */
2963 if (!found)
2964 ereport(ERROR,
2965 (errcode(ERRCODE_UNDEFINED_COLUMN),
2966 errmsg("column \"%s\" does not exist",
2967 restdef->colname)));
2972 * If we found any conflicting parent default values, check to make sure
2973 * they were overridden by the child.
2975 if (have_bogus_defaults)
2977 foreach(lc, columns)
2979 ColumnDef *def = lfirst(lc);
2981 if (def->cooked_default == &bogus_marker)
2983 if (def->generated)
2984 ereport(ERROR,
2985 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2986 errmsg("column \"%s\" inherits conflicting generation expressions",
2987 def->colname),
2988 errhint("To resolve the conflict, specify a generation expression explicitly.")));
2989 else
2990 ereport(ERROR,
2991 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
2992 errmsg("column \"%s\" inherits conflicting default values",
2993 def->colname),
2994 errhint("To resolve the conflict, specify a default explicitly.")));
2999 *supconstr = constraints;
3001 return columns;
3006 * MergeCheckConstraint
3007 * Try to merge an inherited CHECK constraint with previous ones
3009 * If we inherit identically-named constraints from multiple parents, we must
3010 * merge them, or throw an error if they don't have identical definitions.
3012 * constraints is a list of CookedConstraint structs for previous constraints.
3014 * If the new constraint matches an existing one, then the existing
3015 * constraint's inheritance count is updated. If there is a conflict (same
3016 * name but different expression), throw an error. If the constraint neither
3017 * matches nor conflicts with an existing one, a new constraint is appended to
3018 * the list.
3020 static List *
3021 MergeCheckConstraint(List *constraints, const char *name, Node *expr)
3023 ListCell *lc;
3024 CookedConstraint *newcon;
3026 foreach(lc, constraints)
3028 CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3030 Assert(ccon->contype == CONSTR_CHECK);
3032 /* Non-matching names never conflict */
3033 if (strcmp(ccon->name, name) != 0)
3034 continue;
3036 if (equal(expr, ccon->expr))
3038 /* OK to merge constraint with existing */
3039 ccon->inhcount++;
3040 if (ccon->inhcount < 0)
3041 ereport(ERROR,
3042 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3043 errmsg("too many inheritance parents"));
3044 return constraints;
3047 ereport(ERROR,
3048 (errcode(ERRCODE_DUPLICATE_OBJECT),
3049 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3050 name)));
3054 * Constraint couldn't be merged with an existing one and also didn't
3055 * conflict with an existing one, so add it as a new one to the list.
3057 newcon = palloc0_object(CookedConstraint);
3058 newcon->contype = CONSTR_CHECK;
3059 newcon->name = pstrdup(name);
3060 newcon->expr = expr;
3061 newcon->inhcount = 1;
3062 return lappend(constraints, newcon);
3066 * MergeChildAttribute
3067 * Merge given child attribute definition into given inherited attribute.
3069 * Input arguments:
3070 * 'inh_columns' is the list of inherited ColumnDefs.
3071 * 'exist_attno' is the number of the inherited attribute in inh_columns
3072 * 'newcol_attno' is the attribute number in child table's schema definition
3073 * 'newdef' is the column/attribute definition from the child table.
3075 * The ColumnDef in 'inh_columns' list is modified. The child attribute's
3076 * ColumnDef remains unchanged.
3078 * Notes:
3079 * - The attribute is merged according to the rules laid out in the prologue
3080 * of MergeAttributes().
3081 * - If matching inherited attribute exists but the child attribute can not be
3082 * merged into it, the function throws respective errors.
3083 * - A partition can not have its own column definitions. Hence this function
3084 * is applicable only to a regular inheritance child.
3086 static void
3087 MergeChildAttribute(List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
3089 char *attributeName = newdef->colname;
3090 ColumnDef *inhdef;
3091 Oid inhtypeid,
3092 newtypeid;
3093 int32 inhtypmod,
3094 newtypmod;
3095 Oid inhcollid,
3096 newcollid;
3098 if (exist_attno == newcol_attno)
3099 ereport(NOTICE,
3100 (errmsg("merging column \"%s\" with inherited definition",
3101 attributeName)));
3102 else
3103 ereport(NOTICE,
3104 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3105 errdetail("User-specified column moved to the position of the inherited column.")));
3107 inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3110 * Must have the same type and typmod
3112 typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3113 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3114 if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3115 ereport(ERROR,
3116 (errcode(ERRCODE_DATATYPE_MISMATCH),
3117 errmsg("column \"%s\" has a type conflict",
3118 attributeName),
3119 errdetail("%s versus %s",
3120 format_type_with_typemod(inhtypeid, inhtypmod),
3121 format_type_with_typemod(newtypeid, newtypmod))));
3124 * Must have the same collation
3126 inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3127 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3128 if (inhcollid != newcollid)
3129 ereport(ERROR,
3130 (errcode(ERRCODE_COLLATION_MISMATCH),
3131 errmsg("column \"%s\" has a collation conflict",
3132 attributeName),
3133 errdetail("\"%s\" versus \"%s\"",
3134 get_collation_name(inhcollid),
3135 get_collation_name(newcollid))));
3138 * Identity is never inherited by a regular inheritance child. Pick
3139 * child's identity definition if there's one.
3141 inhdef->identity = newdef->identity;
3144 * Copy storage parameter
3146 if (inhdef->storage == 0)
3147 inhdef->storage = newdef->storage;
3148 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3149 ereport(ERROR,
3150 (errcode(ERRCODE_DATATYPE_MISMATCH),
3151 errmsg("column \"%s\" has a storage parameter conflict",
3152 attributeName),
3153 errdetail("%s versus %s",
3154 storage_name(inhdef->storage),
3155 storage_name(newdef->storage))));
3158 * Copy compression parameter
3160 if (inhdef->compression == NULL)
3161 inhdef->compression = newdef->compression;
3162 else if (newdef->compression != NULL)
3164 if (strcmp(inhdef->compression, newdef->compression) != 0)
3165 ereport(ERROR,
3166 (errcode(ERRCODE_DATATYPE_MISMATCH),
3167 errmsg("column \"%s\" has a compression method conflict",
3168 attributeName),
3169 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3173 * Merge of not-null constraints = OR 'em together
3175 inhdef->is_not_null |= newdef->is_not_null;
3178 * Check for conflicts related to generated columns.
3180 * If the parent column is generated, the child column will be made a
3181 * generated column if it isn't already. If it is a generated column,
3182 * we'll take its generation expression in preference to the parent's. We
3183 * must check that the child column doesn't specify a default value or
3184 * identity, which matches the rules for a single column in
3185 * parse_utilcmd.c.
3187 * Conversely, if the parent column is not generated, the child column
3188 * can't be either. (We used to allow that, but it results in being able
3189 * to override the generation expression via UPDATEs through the parent.)
3191 if (inhdef->generated)
3193 if (newdef->raw_default && !newdef->generated)
3194 ereport(ERROR,
3195 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3196 errmsg("column \"%s\" inherits from generated column but specifies default",
3197 inhdef->colname)));
3198 if (newdef->identity)
3199 ereport(ERROR,
3200 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3201 errmsg("column \"%s\" inherits from generated column but specifies identity",
3202 inhdef->colname)));
3204 else
3206 if (newdef->generated)
3207 ereport(ERROR,
3208 (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3209 errmsg("child column \"%s\" specifies generation expression",
3210 inhdef->colname),
3211 errhint("A child table column cannot be generated unless its parent column is.")));
3215 * If new def has a default, override previous default
3217 if (newdef->raw_default != NULL)
3219 inhdef->raw_default = newdef->raw_default;
3220 inhdef->cooked_default = newdef->cooked_default;
3223 /* Mark the column as locally defined */
3224 inhdef->is_local = true;
3228 * MergeInheritedAttribute
3229 * Merge given parent attribute definition into specified attribute
3230 * inherited from the previous parents.
3232 * Input arguments:
3233 * 'inh_columns' is the list of previously inherited ColumnDefs.
3234 * 'exist_attno' is the number the existing matching attribute in inh_columns.
3235 * 'newdef' is the new parent column/attribute definition to be merged.
3237 * The matching ColumnDef in 'inh_columns' list is modified and returned.
3239 * Notes:
3240 * - The attribute is merged according to the rules laid out in the prologue
3241 * of MergeAttributes().
3242 * - If matching inherited attribute exists but the new attribute can not be
3243 * merged into it, the function throws respective errors.
3244 * - A partition inherits from only a single parent. Hence this function is
3245 * applicable only to a regular inheritance.
3247 static ColumnDef *
3248 MergeInheritedAttribute(List *inh_columns,
3249 int exist_attno,
3250 const ColumnDef *newdef)
3252 char *attributeName = newdef->colname;
3253 ColumnDef *prevdef;
3254 Oid prevtypeid,
3255 newtypeid;
3256 int32 prevtypmod,
3257 newtypmod;
3258 Oid prevcollid,
3259 newcollid;
3261 ereport(NOTICE,
3262 (errmsg("merging multiple inherited definitions of column \"%s\"",
3263 attributeName)));
3264 prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3267 * Must have the same type and typmod
3269 typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3270 typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3271 if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3272 ereport(ERROR,
3273 (errcode(ERRCODE_DATATYPE_MISMATCH),
3274 errmsg("inherited column \"%s\" has a type conflict",
3275 attributeName),
3276 errdetail("%s versus %s",
3277 format_type_with_typemod(prevtypeid, prevtypmod),
3278 format_type_with_typemod(newtypeid, newtypmod))));
3281 * Merge of not-null constraints = OR 'em together
3283 prevdef->is_not_null |= newdef->is_not_null;
3286 * Must have the same collation
3288 prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3289 newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3290 if (prevcollid != newcollid)
3291 ereport(ERROR,
3292 (errcode(ERRCODE_COLLATION_MISMATCH),
3293 errmsg("inherited column \"%s\" has a collation conflict",
3294 attributeName),
3295 errdetail("\"%s\" versus \"%s\"",
3296 get_collation_name(prevcollid),
3297 get_collation_name(newcollid))));
3300 * Copy/check storage parameter
3302 if (prevdef->storage == 0)
3303 prevdef->storage = newdef->storage;
3304 else if (prevdef->storage != newdef->storage)
3305 ereport(ERROR,
3306 (errcode(ERRCODE_DATATYPE_MISMATCH),
3307 errmsg("inherited column \"%s\" has a storage parameter conflict",
3308 attributeName),
3309 errdetail("%s versus %s",
3310 storage_name(prevdef->storage),
3311 storage_name(newdef->storage))));
3314 * Copy/check compression parameter
3316 if (prevdef->compression == NULL)
3317 prevdef->compression = newdef->compression;
3318 else if (newdef->compression != NULL)
3320 if (strcmp(prevdef->compression, newdef->compression) != 0)
3321 ereport(ERROR,
3322 (errcode(ERRCODE_DATATYPE_MISMATCH),
3323 errmsg("column \"%s\" has a compression method conflict",
3324 attributeName),
3325 errdetail("%s versus %s",
3326 prevdef->compression, newdef->compression)));
3330 * Check for GENERATED conflicts
3332 if (prevdef->generated != newdef->generated)
3333 ereport(ERROR,
3334 (errcode(ERRCODE_DATATYPE_MISMATCH),
3335 errmsg("inherited column \"%s\" has a generation conflict",
3336 attributeName)));
3339 * Default and other constraints are handled by the caller.
3342 prevdef->inhcount++;
3343 if (prevdef->inhcount < 0)
3344 ereport(ERROR,
3345 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3346 errmsg("too many inheritance parents"));
3348 return prevdef;
3352 * StoreCatalogInheritance
3353 * Updates the system catalogs with proper inheritance information.
3355 * supers is a list of the OIDs of the new relation's direct ancestors.
3357 static void
3358 StoreCatalogInheritance(Oid relationId, List *supers,
3359 bool child_is_partition)
3361 Relation relation;
3362 int32 seqNumber;
3363 ListCell *entry;
3366 * sanity checks
3368 Assert(OidIsValid(relationId));
3370 if (supers == NIL)
3371 return;
3374 * Store INHERITS information in pg_inherits using direct ancestors only.
3375 * Also enter dependencies on the direct ancestors, and make sure they are
3376 * marked with relhassubclass = true.
3378 * (Once upon a time, both direct and indirect ancestors were found here
3379 * and then entered into pg_ipl. Since that catalog doesn't exist
3380 * anymore, there's no need to look for indirect ancestors.)
3382 relation = table_open(InheritsRelationId, RowExclusiveLock);
3384 seqNumber = 1;
3385 foreach(entry, supers)
3387 Oid parentOid = lfirst_oid(entry);
3389 StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3390 child_is_partition);
3391 seqNumber++;
3394 table_close(relation, RowExclusiveLock);
3398 * Make catalog entries showing relationId as being an inheritance child
3399 * of parentOid. inhRelation is the already-opened pg_inherits catalog.
3401 static void
3402 StoreCatalogInheritance1(Oid relationId, Oid parentOid,
3403 int32 seqNumber, Relation inhRelation,
3404 bool child_is_partition)
3406 ObjectAddress childobject,
3407 parentobject;
3409 /* store the pg_inherits row */
3410 StoreSingleInheritance(relationId, parentOid, seqNumber);
3413 * Store a dependency too
3415 parentobject.classId = RelationRelationId;
3416 parentobject.objectId = parentOid;
3417 parentobject.objectSubId = 0;
3418 childobject.classId = RelationRelationId;
3419 childobject.objectId = relationId;
3420 childobject.objectSubId = 0;
3422 recordDependencyOn(&childobject, &parentobject,
3423 child_dependency_type(child_is_partition));
3426 * Post creation hook of this inheritance. Since object_access_hook
3427 * doesn't take multiple object identifiers, we relay oid of parent
3428 * relation using auxiliary_id argument.
3430 InvokeObjectPostAlterHookArg(InheritsRelationId,
3431 relationId, 0,
3432 parentOid, false);
3435 * Mark the parent as having subclasses.
3437 SetRelationHasSubclass(parentOid, true);
3441 * Look for an existing column entry with the given name.
3443 * Returns the index (starting with 1) if attribute already exists in columns,
3444 * 0 if it doesn't.
3446 static int
3447 findAttrByName(const char *attributeName, const List *columns)
3449 ListCell *lc;
3450 int i = 1;
3452 foreach(lc, columns)
3454 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3455 return i;
3457 i++;
3459 return 0;
3464 * SetRelationHasSubclass
3465 * Set the value of the relation's relhassubclass field in pg_class.
3467 * It's always safe to set this field to true, because all SQL commands are
3468 * ready to see true and then find no children. On the other hand, commands
3469 * generally assume zero children if this is false.
3471 * Caller must hold any self-exclusive lock until end of transaction. If the
3472 * new value is false, caller must have acquired that lock before reading the
3473 * evidence that justified the false value. That way, it properly waits if
3474 * another backend is simultaneously concluding no need to change the tuple
3475 * (new and old values are true).
3477 * NOTE: an important side-effect of this operation is that an SI invalidation
3478 * message is sent out to all backends --- including me --- causing plans
3479 * referencing the relation to be rebuilt with the new list of children.
3480 * This must happen even if we find that no change is needed in the pg_class
3481 * row.
3483 void
3484 SetRelationHasSubclass(Oid relationId, bool relhassubclass)
3486 Relation relationRelation;
3487 HeapTuple tuple;
3488 Form_pg_class classtuple;
3490 Assert(CheckRelationOidLockedByMe(relationId,
3491 ShareUpdateExclusiveLock, false) ||
3492 CheckRelationOidLockedByMe(relationId,
3493 ShareRowExclusiveLock, true));
3496 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3498 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3499 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3500 if (!HeapTupleIsValid(tuple))
3501 elog(ERROR, "cache lookup failed for relation %u", relationId);
3502 classtuple = (Form_pg_class) GETSTRUCT(tuple);
3504 if (classtuple->relhassubclass != relhassubclass)
3506 classtuple->relhassubclass = relhassubclass;
3507 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3509 else
3511 /* no need to change tuple, but force relcache rebuild anyway */
3512 CacheInvalidateRelcacheByTuple(tuple);
3515 heap_freetuple(tuple);
3516 table_close(relationRelation, RowExclusiveLock);
3520 * CheckRelationTableSpaceMove
3521 * Check if relation can be moved to new tablespace.
3523 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3525 * Returns true if the relation can be moved to the new tablespace; raises
3526 * an error if it is not possible to do the move; returns false if the move
3527 * would have no effect.
3529 bool
3530 CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
3532 Oid oldTableSpaceId;
3535 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3536 * stored as 0.
3538 oldTableSpaceId = rel->rd_rel->reltablespace;
3539 if (newTableSpaceId == oldTableSpaceId ||
3540 (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3541 return false;
3544 * We cannot support moving mapped relations into different tablespaces.
3545 * (In particular this eliminates all shared catalogs.)
3547 if (RelationIsMapped(rel))
3548 ereport(ERROR,
3549 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3550 errmsg("cannot move system relation \"%s\"",
3551 RelationGetRelationName(rel))));
3553 /* Cannot move a non-shared relation into pg_global */
3554 if (newTableSpaceId == GLOBALTABLESPACE_OID)
3555 ereport(ERROR,
3556 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3557 errmsg("only shared relations can be placed in pg_global tablespace")));
3560 * Do not allow moving temp tables of other backends ... their local
3561 * buffer manager is not going to cope.
3563 if (RELATION_IS_OTHER_TEMP(rel))
3564 ereport(ERROR,
3565 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3566 errmsg("cannot move temporary tables of other sessions")));
3568 return true;
3572 * SetRelationTableSpace
3573 * Set new reltablespace and relfilenumber in pg_class entry.
3575 * newTableSpaceId is the new tablespace for the relation, and
3576 * newRelFilenumber its new filenumber. If newRelFilenumber is
3577 * InvalidRelFileNumber, this field is not updated.
3579 * NOTE: The caller must hold AccessExclusiveLock on the relation.
3581 * The caller of this routine had better check if a relation can be
3582 * moved to this new tablespace by calling CheckRelationTableSpaceMove()
3583 * first, and is responsible for making the change visible with
3584 * CommandCounterIncrement().
3586 void
3587 SetRelationTableSpace(Relation rel,
3588 Oid newTableSpaceId,
3589 RelFileNumber newRelFilenumber)
3591 Relation pg_class;
3592 HeapTuple tuple;
3593 Form_pg_class rd_rel;
3594 Oid reloid = RelationGetRelid(rel);
3596 Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3598 /* Get a modifiable copy of the relation's pg_class row. */
3599 pg_class = table_open(RelationRelationId, RowExclusiveLock);
3601 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3602 if (!HeapTupleIsValid(tuple))
3603 elog(ERROR, "cache lookup failed for relation %u", reloid);
3604 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3606 /* Update the pg_class row. */
3607 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3608 InvalidOid : newTableSpaceId;
3609 if (RelFileNumberIsValid(newRelFilenumber))
3610 rd_rel->relfilenode = newRelFilenumber;
3611 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3614 * Record dependency on tablespace. This is only required for relations
3615 * that have no physical storage.
3617 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3618 changeDependencyOnTablespace(RelationRelationId, reloid,
3619 rd_rel->reltablespace);
3621 heap_freetuple(tuple);
3622 table_close(pg_class, RowExclusiveLock);
3626 * renameatt_check - basic sanity checks before attribute rename
3628 static void
3629 renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
3631 char relkind = classform->relkind;
3633 if (classform->reloftype && !recursing)
3634 ereport(ERROR,
3635 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3636 errmsg("cannot rename column of typed table")));
3639 * Renaming the columns of sequences or toast tables doesn't actually
3640 * break anything from the system's point of view, since internal
3641 * references are by attnum. But it doesn't seem right to allow users to
3642 * change names that are hardcoded into the system, hence the following
3643 * restriction.
3645 if (relkind != RELKIND_RELATION &&
3646 relkind != RELKIND_VIEW &&
3647 relkind != RELKIND_MATVIEW &&
3648 relkind != RELKIND_COMPOSITE_TYPE &&
3649 relkind != RELKIND_INDEX &&
3650 relkind != RELKIND_PARTITIONED_INDEX &&
3651 relkind != RELKIND_FOREIGN_TABLE &&
3652 relkind != RELKIND_PARTITIONED_TABLE)
3653 ereport(ERROR,
3654 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3655 errmsg("cannot rename columns of relation \"%s\"",
3656 NameStr(classform->relname)),
3657 errdetail_relkind_not_supported(relkind)));
3660 * permissions checking. only the owner of a class can change its schema.
3662 if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3663 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(myrelid)),
3664 NameStr(classform->relname));
3665 if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3666 ereport(ERROR,
3667 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3668 errmsg("permission denied: \"%s\" is a system catalog",
3669 NameStr(classform->relname))));
3673 * renameatt_internal - workhorse for renameatt
3675 * Return value is the attribute number in the 'myrelid' relation.
3677 static AttrNumber
3678 renameatt_internal(Oid myrelid,
3679 const char *oldattname,
3680 const char *newattname,
3681 bool recurse,
3682 bool recursing,
3683 int expected_parents,
3684 DropBehavior behavior)
3686 Relation targetrelation;
3687 Relation attrelation;
3688 HeapTuple atttup;
3689 Form_pg_attribute attform;
3690 AttrNumber attnum;
3693 * Grab an exclusive lock on the target table, which we will NOT release
3694 * until end of transaction.
3696 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3697 renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3700 * if the 'recurse' flag is set then we are supposed to rename this
3701 * attribute in all classes that inherit from 'relname' (as well as in
3702 * 'relname').
3704 * any permissions or problems with duplicate attributes will cause the
3705 * whole transaction to abort, which is what we want -- all or nothing.
3707 if (recurse)
3709 List *child_oids,
3710 *child_numparents;
3711 ListCell *lo,
3712 *li;
3715 * we need the number of parents for each child so that the recursive
3716 * calls to renameatt() can determine whether there are any parents
3717 * outside the inheritance hierarchy being processed.
3719 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3720 &child_numparents);
3723 * find_all_inheritors does the recursive search of the inheritance
3724 * hierarchy, so all we have to do is process all of the relids in the
3725 * list that it returns.
3727 forboth(lo, child_oids, li, child_numparents)
3729 Oid childrelid = lfirst_oid(lo);
3730 int numparents = lfirst_int(li);
3732 if (childrelid == myrelid)
3733 continue;
3734 /* note we need not recurse again */
3735 renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3738 else
3741 * If we are told not to recurse, there had better not be any child
3742 * tables; else the rename would put them out of step.
3744 * expected_parents will only be 0 if we are not already recursing.
3746 if (expected_parents == 0 &&
3747 find_inheritance_children(myrelid, NoLock) != NIL)
3748 ereport(ERROR,
3749 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3750 errmsg("inherited column \"%s\" must be renamed in child tables too",
3751 oldattname)));
3754 /* rename attributes in typed tables of composite type */
3755 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3757 List *child_oids;
3758 ListCell *lo;
3760 child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3761 RelationGetRelationName(targetrelation),
3762 behavior);
3764 foreach(lo, child_oids)
3765 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3768 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3770 atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3771 if (!HeapTupleIsValid(atttup))
3772 ereport(ERROR,
3773 (errcode(ERRCODE_UNDEFINED_COLUMN),
3774 errmsg("column \"%s\" does not exist",
3775 oldattname)));
3776 attform = (Form_pg_attribute) GETSTRUCT(atttup);
3778 attnum = attform->attnum;
3779 if (attnum <= 0)
3780 ereport(ERROR,
3781 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3782 errmsg("cannot rename system column \"%s\"",
3783 oldattname)));
3786 * if the attribute is inherited, forbid the renaming. if this is a
3787 * top-level call to renameatt(), then expected_parents will be 0, so the
3788 * effect of this code will be to prohibit the renaming if the attribute
3789 * is inherited at all. if this is a recursive call to renameatt(),
3790 * expected_parents will be the number of parents the current relation has
3791 * within the inheritance hierarchy being processed, so we'll prohibit the
3792 * renaming only if there are additional parents from elsewhere.
3794 if (attform->attinhcount > expected_parents)
3795 ereport(ERROR,
3796 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3797 errmsg("cannot rename inherited column \"%s\"",
3798 oldattname)));
3800 /* new name should not already exist */
3801 (void) check_for_column_name_collision(targetrelation, newattname, false);
3803 /* apply the update */
3804 namestrcpy(&(attform->attname), newattname);
3806 CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3808 InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3810 heap_freetuple(atttup);
3812 table_close(attrelation, RowExclusiveLock);
3814 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3816 return attnum;
3820 * Perform permissions and integrity checks before acquiring a relation lock.
3822 static void
3823 RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid,
3824 void *arg)
3826 HeapTuple tuple;
3827 Form_pg_class form;
3829 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3830 if (!HeapTupleIsValid(tuple))
3831 return; /* concurrently dropped */
3832 form = (Form_pg_class) GETSTRUCT(tuple);
3833 renameatt_check(relid, form, false);
3834 ReleaseSysCache(tuple);
3838 * renameatt - changes the name of an attribute in a relation
3840 * The returned ObjectAddress is that of the renamed column.
3842 ObjectAddress
3843 renameatt(RenameStmt *stmt)
3845 Oid relid;
3846 AttrNumber attnum;
3847 ObjectAddress address;
3849 /* lock level taken here should match renameatt_internal */
3850 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
3851 stmt->missing_ok ? RVR_MISSING_OK : 0,
3852 RangeVarCallbackForRenameAttribute,
3853 NULL);
3855 if (!OidIsValid(relid))
3857 ereport(NOTICE,
3858 (errmsg("relation \"%s\" does not exist, skipping",
3859 stmt->relation->relname)));
3860 return InvalidObjectAddress;
3863 attnum =
3864 renameatt_internal(relid,
3865 stmt->subname, /* old att name */
3866 stmt->newname, /* new att name */
3867 stmt->relation->inh, /* recursive? */
3868 false, /* recursing? */
3869 0, /* expected inhcount */
3870 stmt->behavior);
3872 ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3874 return address;
3878 * same logic as renameatt_internal
3880 static ObjectAddress
3881 rename_constraint_internal(Oid myrelid,
3882 Oid mytypid,
3883 const char *oldconname,
3884 const char *newconname,
3885 bool recurse,
3886 bool recursing,
3887 int expected_parents)
3889 Relation targetrelation = NULL;
3890 Oid constraintOid;
3891 HeapTuple tuple;
3892 Form_pg_constraint con;
3893 ObjectAddress address;
3895 Assert(!myrelid || !mytypid);
3897 if (mytypid)
3899 constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3901 else
3903 targetrelation = relation_open(myrelid, AccessExclusiveLock);
3906 * don't tell it whether we're recursing; we allow changing typed
3907 * tables here
3909 renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3911 constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3914 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3915 if (!HeapTupleIsValid(tuple))
3916 elog(ERROR, "cache lookup failed for constraint %u",
3917 constraintOid);
3918 con = (Form_pg_constraint) GETSTRUCT(tuple);
3920 if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3922 if (recurse)
3924 List *child_oids,
3925 *child_numparents;
3926 ListCell *lo,
3927 *li;
3929 child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3930 &child_numparents);
3932 forboth(lo, child_oids, li, child_numparents)
3934 Oid childrelid = lfirst_oid(lo);
3935 int numparents = lfirst_int(li);
3937 if (childrelid == myrelid)
3938 continue;
3940 rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3943 else
3945 if (expected_parents == 0 &&
3946 find_inheritance_children(myrelid, NoLock) != NIL)
3947 ereport(ERROR,
3948 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3949 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3950 oldconname)));
3953 if (con->coninhcount > expected_parents)
3954 ereport(ERROR,
3955 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3956 errmsg("cannot rename inherited constraint \"%s\"",
3957 oldconname)));
3960 if (con->conindid
3961 && (con->contype == CONSTRAINT_PRIMARY
3962 || con->contype == CONSTRAINT_UNIQUE
3963 || con->contype == CONSTRAINT_EXCLUSION))
3964 /* rename the index; this renames the constraint as well */
3965 RenameRelationInternal(con->conindid, newconname, false, true);
3966 else
3967 RenameConstraintById(constraintOid, newconname);
3969 ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3971 ReleaseSysCache(tuple);
3973 if (targetrelation)
3976 * Invalidate relcache so as others can see the new constraint name.
3978 CacheInvalidateRelcache(targetrelation);
3980 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3983 return address;
3986 ObjectAddress
3987 RenameConstraint(RenameStmt *stmt)
3989 Oid relid = InvalidOid;
3990 Oid typid = InvalidOid;
3992 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3994 Relation rel;
3995 HeapTuple tup;
3997 typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3998 rel = table_open(TypeRelationId, RowExclusiveLock);
3999 tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4000 if (!HeapTupleIsValid(tup))
4001 elog(ERROR, "cache lookup failed for type %u", typid);
4002 checkDomainOwner(tup);
4003 ReleaseSysCache(tup);
4004 table_close(rel, NoLock);
4006 else
4008 /* lock level taken here should match rename_constraint_internal */
4009 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
4010 stmt->missing_ok ? RVR_MISSING_OK : 0,
4011 RangeVarCallbackForRenameAttribute,
4012 NULL);
4013 if (!OidIsValid(relid))
4015 ereport(NOTICE,
4016 (errmsg("relation \"%s\" does not exist, skipping",
4017 stmt->relation->relname)));
4018 return InvalidObjectAddress;
4022 return
4023 rename_constraint_internal(relid, typid,
4024 stmt->subname,
4025 stmt->newname,
4026 (stmt->relation &&
4027 stmt->relation->inh), /* recursive? */
4028 false, /* recursing? */
4029 0 /* expected inhcount */ );
4033 * Execute ALTER TABLE/INDEX/SEQUENCE/VIEW/MATERIALIZED VIEW/FOREIGN TABLE
4034 * RENAME
4036 ObjectAddress
4037 RenameRelation(RenameStmt *stmt)
4039 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4040 Oid relid;
4041 ObjectAddress address;
4044 * Grab an exclusive lock on the target table, index, sequence, view,
4045 * materialized view, or foreign table, which we will NOT release until
4046 * end of transaction.
4048 * Lock level used here should match RenameRelationInternal, to avoid lock
4049 * escalation. However, because ALTER INDEX can be used with any relation
4050 * type, we mustn't believe without verification.
4052 for (;;)
4054 LOCKMODE lockmode;
4055 char relkind;
4056 bool obj_is_index;
4058 lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4060 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4061 stmt->missing_ok ? RVR_MISSING_OK : 0,
4062 RangeVarCallbackForAlterRelation,
4063 (void *) stmt);
4065 if (!OidIsValid(relid))
4067 ereport(NOTICE,
4068 (errmsg("relation \"%s\" does not exist, skipping",
4069 stmt->relation->relname)));
4070 return InvalidObjectAddress;
4074 * We allow mismatched statement and object types (e.g., ALTER INDEX
4075 * to rename a table), but we might've used the wrong lock level. If
4076 * that happens, retry with the correct lock level. We don't bother
4077 * if we already acquired AccessExclusiveLock with an index, however.
4079 relkind = get_rel_relkind(relid);
4080 obj_is_index = (relkind == RELKIND_INDEX ||
4081 relkind == RELKIND_PARTITIONED_INDEX);
4082 if (obj_is_index || is_index_stmt == obj_is_index)
4083 break;
4085 UnlockRelationOid(relid, lockmode);
4086 is_index_stmt = obj_is_index;
4089 /* Do the work */
4090 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4092 ObjectAddressSet(address, RelationRelationId, relid);
4094 return address;
4098 * RenameRelationInternal - change the name of a relation
4100 void
4101 RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
4103 Relation targetrelation;
4104 Relation relrelation; /* for RELATION relation */
4105 HeapTuple reltup;
4106 Form_pg_class relform;
4107 Oid namespaceId;
4110 * Grab a lock on the target relation, which we will NOT release until end
4111 * of transaction. We need at least a self-exclusive lock so that
4112 * concurrent DDL doesn't overwrite the rename if they start updating
4113 * while still seeing the old version. The lock also guards against
4114 * triggering relcache reloads in concurrent sessions, which might not
4115 * handle this information changing under them. For indexes, we can use a
4116 * reduced lock level because RelationReloadIndexInfo() handles indexes
4117 * specially.
4119 targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4120 namespaceId = RelationGetNamespace(targetrelation);
4123 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4125 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4127 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4128 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4129 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4130 relform = (Form_pg_class) GETSTRUCT(reltup);
4132 if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4133 ereport(ERROR,
4134 (errcode(ERRCODE_DUPLICATE_TABLE),
4135 errmsg("relation \"%s\" already exists",
4136 newrelname)));
4139 * RenameRelation is careful not to believe the caller's idea of the
4140 * relation kind being handled. We don't have to worry about this, but
4141 * let's not be totally oblivious to it. We can process an index as
4142 * not-an-index, but not the other way around.
4144 Assert(!is_index ||
4145 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4146 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4149 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4150 * because it's a copy...)
4152 namestrcpy(&(relform->relname), newrelname);
4154 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4156 InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4157 InvalidOid, is_internal);
4159 heap_freetuple(reltup);
4160 table_close(relrelation, RowExclusiveLock);
4163 * Also rename the associated type, if any.
4165 if (OidIsValid(targetrelation->rd_rel->reltype))
4166 RenameTypeInternal(targetrelation->rd_rel->reltype,
4167 newrelname, namespaceId);
4170 * Also rename the associated constraint, if any.
4172 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4173 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4175 Oid constraintId = get_index_constraint(myrelid);
4177 if (OidIsValid(constraintId))
4178 RenameConstraintById(constraintId, newrelname);
4182 * Close rel, but keep lock!
4184 relation_close(targetrelation, NoLock);
4188 * ResetRelRewrite - reset relrewrite
4190 void
4191 ResetRelRewrite(Oid myrelid)
4193 Relation relrelation; /* for RELATION relation */
4194 HeapTuple reltup;
4195 Form_pg_class relform;
4198 * Find relation's pg_class tuple.
4200 relrelation = table_open(RelationRelationId, RowExclusiveLock);
4202 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4203 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4204 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4205 relform = (Form_pg_class) GETSTRUCT(reltup);
4208 * Update pg_class tuple.
4210 relform->relrewrite = InvalidOid;
4212 CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4214 heap_freetuple(reltup);
4215 table_close(relrelation, RowExclusiveLock);
4219 * Disallow ALTER TABLE (and similar commands) when the current backend has
4220 * any open reference to the target table besides the one just acquired by
4221 * the calling command; this implies there's an open cursor or active plan.
4222 * We need this check because our lock doesn't protect us against stomping
4223 * on our own foot, only other people's feet!
4225 * For ALTER TABLE, the only case known to cause serious trouble is ALTER
4226 * COLUMN TYPE, and some changes are obviously pretty benign, so this could
4227 * possibly be relaxed to only error out for certain types of alterations.
4228 * But the use-case for allowing any of these things is not obvious, so we
4229 * won't work hard at it for now.
4231 * We also reject these commands if there are any pending AFTER trigger events
4232 * for the rel. This is certainly necessary for the rewriting variants of
4233 * ALTER TABLE, because they don't preserve tuple TIDs and so the pending
4234 * events would try to fetch the wrong tuples. It might be overly cautious
4235 * in other cases, but again it seems better to err on the side of paranoia.
4237 * REINDEX calls this with "rel" referencing the index to be rebuilt; here
4238 * we are worried about active indexscans on the index. The trigger-event
4239 * check can be skipped, since we are doing no damage to the parent table.
4241 * The statement name (eg, "ALTER TABLE") is passed for use in error messages.
4243 void
4244 CheckTableNotInUse(Relation rel, const char *stmt)
4246 int expected_refcnt;
4248 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4249 if (rel->rd_refcnt != expected_refcnt)
4250 ereport(ERROR,
4251 (errcode(ERRCODE_OBJECT_IN_USE),
4252 /* translator: first %s is a SQL command, eg ALTER TABLE */
4253 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4254 stmt, RelationGetRelationName(rel))));
4256 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4257 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4258 AfterTriggerPendingOnRel(RelationGetRelid(rel)))
4259 ereport(ERROR,
4260 (errcode(ERRCODE_OBJECT_IN_USE),
4261 /* translator: first %s is a SQL command, eg ALTER TABLE */
4262 errmsg("cannot %s \"%s\" because it has pending trigger events",
4263 stmt, RelationGetRelationName(rel))));
4267 * CheckAlterTableIsSafe
4268 * Verify that it's safe to allow ALTER TABLE on this relation.
4270 * This consists of CheckTableNotInUse() plus a check that the relation
4271 * isn't another session's temp table. We must split out the temp-table
4272 * check because there are callers of CheckTableNotInUse() that don't want
4273 * that, notably DROP TABLE. (We must allow DROP or we couldn't clean out
4274 * an orphaned temp schema.) Compare truncate_check_activity().
4276 static void
4277 CheckAlterTableIsSafe(Relation rel)
4280 * Don't allow ALTER on temp tables of other backends. Their local buffer
4281 * manager is not going to cope if we need to change the table's contents.
4282 * Even if we don't, there may be optimizations that assume temp tables
4283 * aren't subject to such interference.
4285 if (RELATION_IS_OTHER_TEMP(rel))
4286 ereport(ERROR,
4287 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4288 errmsg("cannot alter temporary tables of other sessions")));
4291 * Also check for active uses of the relation in the current transaction,
4292 * including open scans and pending AFTER trigger events.
4294 CheckTableNotInUse(rel, "ALTER TABLE");
4298 * AlterTableLookupRelation
4299 * Look up, and lock, the OID for the relation named by an alter table
4300 * statement.
4303 AlterTableLookupRelation(AlterTableStmt *stmt, LOCKMODE lockmode)
4305 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4306 stmt->missing_ok ? RVR_MISSING_OK : 0,
4307 RangeVarCallbackForAlterRelation,
4308 (void *) stmt);
4312 * AlterTable
4313 * Execute ALTER TABLE, which can be a list of subcommands
4315 * ALTER TABLE is performed in three phases:
4316 * 1. Examine subcommands and perform pre-transformation checking.
4317 * 2. Validate and transform subcommands, and update system catalogs.
4318 * 3. Scan table(s) to check new constraints, and optionally recopy
4319 * the data into new table(s).
4320 * Phase 3 is not performed unless one or more of the subcommands requires
4321 * it. The intention of this design is to allow multiple independent
4322 * updates of the table schema to be performed with only one pass over the
4323 * data.
4325 * ATPrepCmd performs phase 1. A "work queue" entry is created for
4326 * each table to be affected (there may be multiple affected tables if the
4327 * commands traverse a table inheritance hierarchy). Also we do preliminary
4328 * validation of the subcommands. Because earlier subcommands may change
4329 * the catalog state seen by later commands, there are limits to what can
4330 * be done in this phase. Generally, this phase acquires table locks,
4331 * checks permissions and relkind, and recurses to find child tables.
4333 * ATRewriteCatalogs performs phase 2 for each affected table.
4334 * Certain subcommands need to be performed before others to avoid
4335 * unnecessary conflicts; for example, DROP COLUMN should come before
4336 * ADD COLUMN. Therefore phase 1 divides the subcommands into multiple
4337 * lists, one for each logical "pass" of phase 2.
4339 * ATRewriteTables performs phase 3 for those tables that need it.
4341 * For most subcommand types, phases 2 and 3 do no explicit recursion,
4342 * since phase 1 already does it. However, for certain subcommand types
4343 * it is only possible to determine how to recurse at phase 2 time; for
4344 * those cases, phase 1 sets the cmd->recurse flag.
4346 * Thanks to the magic of MVCC, an error anywhere along the way rolls back
4347 * the whole operation; we don't have to do anything special to clean up.
4349 * The caller must lock the relation, with an appropriate lock level
4350 * for the subcommands requested, using AlterTableGetLockLevel(stmt->cmds)
4351 * or higher. We pass the lock level down
4352 * so that we can apply it recursively to inherited tables. Note that the
4353 * lock level we want as we recurse might well be higher than required for
4354 * that specific subcommand. So we pass down the overall lock requirement,
4355 * rather than reassess it at lower levels.
4357 * The caller also provides a "context" which is to be passed back to
4358 * utility.c when we need to execute a subcommand such as CREATE INDEX.
4359 * Some of the fields therein, such as the relid, are used here as well.
4361 void
4362 AlterTable(AlterTableStmt *stmt, LOCKMODE lockmode,
4363 AlterTableUtilityContext *context)
4365 Relation rel;
4367 /* Caller is required to provide an adequate lock. */
4368 rel = relation_open(context->relid, NoLock);
4370 CheckAlterTableIsSafe(rel);
4372 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4376 * AlterTableInternal
4378 * ALTER TABLE with target specified by OID
4380 * We do not reject if the relation is already open, because it's quite
4381 * likely that one or more layers of caller have it open. That means it
4382 * is unsafe to use this entry point for alterations that could break
4383 * existing query plans. On the assumption it's not used for such, we
4384 * don't have to reject pending AFTER triggers, either.
4386 * Also, since we don't have an AlterTableUtilityContext, this cannot be
4387 * used for any subcommand types that require parse transformation or
4388 * could generate subcommands that have to be passed to ProcessUtility.
4390 void
4391 AlterTableInternal(Oid relid, List *cmds, bool recurse)
4393 Relation rel;
4394 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4396 rel = relation_open(relid, lockmode);
4398 EventTriggerAlterTableRelid(relid);
4400 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4404 * AlterTableGetLockLevel
4406 * Sets the overall lock level required for the supplied list of subcommands.
4407 * Policy for doing this set according to needs of AlterTable(), see
4408 * comments there for overall explanation.
4410 * Function is called before and after parsing, so it must give same
4411 * answer each time it is called. Some subcommands are transformed
4412 * into other subcommand types, so the transform must never be made to a
4413 * lower lock level than previously assigned. All transforms are noted below.
4415 * Since this is called before we lock the table we cannot use table metadata
4416 * to influence the type of lock we acquire.
4418 * There should be no lockmodes hardcoded into the subcommand functions. All
4419 * lockmode decisions for ALTER TABLE are made here only. The one exception is
4420 * ALTER TABLE RENAME which is treated as a different statement type T_RenameStmt
4421 * and does not travel through this section of code and cannot be combined with
4422 * any of the subcommands given here.
4424 * Note that Hot Standby only knows about AccessExclusiveLocks on the primary
4425 * so any changes that might affect SELECTs running on standbys need to use
4426 * AccessExclusiveLocks even if you think a lesser lock would do, unless you
4427 * have a solution for that also.
4429 * Also note that pg_dump uses only an AccessShareLock, meaning that anything
4430 * that takes a lock less than AccessExclusiveLock can change object definitions
4431 * while pg_dump is running. Be careful to check that the appropriate data is
4432 * derived by pg_dump using an MVCC snapshot, rather than syscache lookups,
4433 * otherwise we might end up with an inconsistent dump that can't restore.
4435 LOCKMODE
4436 AlterTableGetLockLevel(List *cmds)
4439 * This only works if we read catalog tables using MVCC snapshots.
4441 ListCell *lcmd;
4442 LOCKMODE lockmode = ShareUpdateExclusiveLock;
4444 foreach(lcmd, cmds)
4446 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4447 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4449 switch (cmd->subtype)
4452 * These subcommands rewrite the heap, so require full locks.
4454 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4455 * to SELECT */
4456 case AT_SetAccessMethod: /* must rewrite heap */
4457 case AT_SetTableSpace: /* must rewrite heap */
4458 case AT_AlterColumnType: /* must rewrite heap */
4459 cmd_lockmode = AccessExclusiveLock;
4460 break;
4463 * These subcommands may require addition of toast tables. If
4464 * we add a toast table to a table currently being scanned, we
4465 * might miss data added to the new toast table by concurrent
4466 * insert transactions.
4468 case AT_SetStorage: /* may add toast tables, see
4469 * ATRewriteCatalogs() */
4470 cmd_lockmode = AccessExclusiveLock;
4471 break;
4474 * Removing constraints can affect SELECTs that have been
4475 * optimized assuming the constraint holds true. See also
4476 * CloneFkReferenced.
4478 case AT_DropConstraint: /* as DROP INDEX */
4479 case AT_DropNotNull: /* may change some SQL plans */
4480 cmd_lockmode = AccessExclusiveLock;
4481 break;
4484 * Subcommands that may be visible to concurrent SELECTs
4486 case AT_DropColumn: /* change visible to SELECT */
4487 case AT_AddColumnToView: /* CREATE VIEW */
4488 case AT_DropOids: /* used to equiv to DropColumn */
4489 case AT_EnableAlwaysRule: /* may change SELECT rules */
4490 case AT_EnableReplicaRule: /* may change SELECT rules */
4491 case AT_EnableRule: /* may change SELECT rules */
4492 case AT_DisableRule: /* may change SELECT rules */
4493 cmd_lockmode = AccessExclusiveLock;
4494 break;
4497 * Changing owner may remove implicit SELECT privileges
4499 case AT_ChangeOwner: /* change visible to SELECT */
4500 cmd_lockmode = AccessExclusiveLock;
4501 break;
4504 * Changing foreign table options may affect optimization.
4506 case AT_GenericOptions:
4507 case AT_AlterColumnGenericOptions:
4508 cmd_lockmode = AccessExclusiveLock;
4509 break;
4512 * These subcommands affect write operations only.
4514 case AT_EnableTrig:
4515 case AT_EnableAlwaysTrig:
4516 case AT_EnableReplicaTrig:
4517 case AT_EnableTrigAll:
4518 case AT_EnableTrigUser:
4519 case AT_DisableTrig:
4520 case AT_DisableTrigAll:
4521 case AT_DisableTrigUser:
4522 cmd_lockmode = ShareRowExclusiveLock;
4523 break;
4526 * These subcommands affect write operations only. XXX
4527 * Theoretically, these could be ShareRowExclusiveLock.
4529 case AT_ColumnDefault:
4530 case AT_CookedColumnDefault:
4531 case AT_AlterConstraint:
4532 case AT_AddIndex: /* from ADD CONSTRAINT */
4533 case AT_AddIndexConstraint:
4534 case AT_ReplicaIdentity:
4535 case AT_SetNotNull:
4536 case AT_EnableRowSecurity:
4537 case AT_DisableRowSecurity:
4538 case AT_ForceRowSecurity:
4539 case AT_NoForceRowSecurity:
4540 case AT_AddIdentity:
4541 case AT_DropIdentity:
4542 case AT_SetIdentity:
4543 case AT_SetExpression:
4544 case AT_DropExpression:
4545 case AT_SetCompression:
4546 cmd_lockmode = AccessExclusiveLock;
4547 break;
4549 case AT_AddConstraint:
4550 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4551 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4552 if (IsA(cmd->def, Constraint))
4554 Constraint *con = (Constraint *) cmd->def;
4556 switch (con->contype)
4558 case CONSTR_EXCLUSION:
4559 case CONSTR_PRIMARY:
4560 case CONSTR_UNIQUE:
4563 * Cases essentially the same as CREATE INDEX. We
4564 * could reduce the lock strength to ShareLock if
4565 * we can work out how to allow concurrent catalog
4566 * updates. XXX Might be set down to
4567 * ShareRowExclusiveLock but requires further
4568 * analysis.
4570 cmd_lockmode = AccessExclusiveLock;
4571 break;
4572 case CONSTR_FOREIGN:
4575 * We add triggers to both tables when we add a
4576 * Foreign Key, so the lock level must be at least
4577 * as strong as CREATE TRIGGER.
4579 cmd_lockmode = ShareRowExclusiveLock;
4580 break;
4582 default:
4583 cmd_lockmode = AccessExclusiveLock;
4586 break;
4589 * These subcommands affect inheritance behaviour. Queries
4590 * started before us will continue to see the old inheritance
4591 * behaviour, while queries started after we commit will see
4592 * new behaviour. No need to prevent reads or writes to the
4593 * subtable while we hook it up though. Changing the TupDesc
4594 * may be a problem, so keep highest lock.
4596 case AT_AddInherit:
4597 case AT_DropInherit:
4598 cmd_lockmode = AccessExclusiveLock;
4599 break;
4602 * These subcommands affect implicit row type conversion. They
4603 * have affects similar to CREATE/DROP CAST on queries. don't
4604 * provide for invalidating parse trees as a result of such
4605 * changes, so we keep these at AccessExclusiveLock.
4607 case AT_AddOf:
4608 case AT_DropOf:
4609 cmd_lockmode = AccessExclusiveLock;
4610 break;
4613 * Only used by CREATE OR REPLACE VIEW which must conflict
4614 * with an SELECTs currently using the view.
4616 case AT_ReplaceRelOptions:
4617 cmd_lockmode = AccessExclusiveLock;
4618 break;
4621 * These subcommands affect general strategies for performance
4622 * and maintenance, though don't change the semantic results
4623 * from normal data reads and writes. Delaying an ALTER TABLE
4624 * behind currently active writes only delays the point where
4625 * the new strategy begins to take effect, so there is no
4626 * benefit in waiting. In this case the minimum restriction
4627 * applies: we don't currently allow concurrent catalog
4628 * updates.
4630 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4631 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4632 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4633 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4634 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4635 cmd_lockmode = ShareUpdateExclusiveLock;
4636 break;
4638 case AT_SetLogged:
4639 case AT_SetUnLogged:
4640 cmd_lockmode = AccessExclusiveLock;
4641 break;
4643 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4644 cmd_lockmode = ShareUpdateExclusiveLock;
4645 break;
4648 * Rel options are more complex than first appears. Options
4649 * are set here for tables, views and indexes; for historical
4650 * reasons these can all be used with ALTER TABLE, so we can't
4651 * decide between them using the basic grammar.
4653 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4654 * getTables() */
4655 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4656 * getTables() */
4657 cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4658 break;
4660 case AT_AttachPartition:
4661 cmd_lockmode = ShareUpdateExclusiveLock;
4662 break;
4664 case AT_DetachPartition:
4665 if (((PartitionCmd *) cmd->def)->concurrent)
4666 cmd_lockmode = ShareUpdateExclusiveLock;
4667 else
4668 cmd_lockmode = AccessExclusiveLock;
4669 break;
4671 case AT_DetachPartitionFinalize:
4672 cmd_lockmode = ShareUpdateExclusiveLock;
4673 break;
4675 case AT_SplitPartition:
4676 cmd_lockmode = AccessExclusiveLock;
4677 break;
4679 case AT_MergePartitions:
4680 cmd_lockmode = AccessExclusiveLock;
4681 break;
4683 case AT_CheckNotNull:
4686 * This only examines the table's schema; but lock must be
4687 * strong enough to prevent concurrent DROP NOT NULL.
4689 cmd_lockmode = AccessShareLock;
4690 break;
4692 default: /* oops */
4693 elog(ERROR, "unrecognized alter table type: %d",
4694 (int) cmd->subtype);
4695 break;
4699 * Take the greatest lockmode from any subcommand
4701 if (cmd_lockmode > lockmode)
4702 lockmode = cmd_lockmode;
4705 return lockmode;
4709 * ATController provides top level control over the phases.
4711 * parsetree is passed in to allow it to be passed to event triggers
4712 * when requested.
4714 static void
4715 ATController(AlterTableStmt *parsetree,
4716 Relation rel, List *cmds, bool recurse, LOCKMODE lockmode,
4717 AlterTableUtilityContext *context)
4719 List *wqueue = NIL;
4720 ListCell *lcmd;
4722 /* Phase 1: preliminary examination of commands, create work queue */
4723 foreach(lcmd, cmds)
4725 AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4727 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4730 /* Close the relation, but keep lock until commit */
4731 relation_close(rel, NoLock);
4733 /* Phase 2: update system catalogs */
4734 ATRewriteCatalogs(&wqueue, lockmode, context);
4736 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4737 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4741 * ATPrepCmd
4743 * Traffic cop for ALTER TABLE Phase 1 operations, including simple
4744 * recursion and permission checks.
4746 * Caller must have acquired appropriate lock type on relation already.
4747 * This lock should be held until commit.
4749 static void
4750 ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
4751 bool recurse, bool recursing, LOCKMODE lockmode,
4752 AlterTableUtilityContext *context)
4754 AlteredTableInfo *tab;
4755 AlterTablePass pass = AT_PASS_UNSET;
4757 /* Find or create work queue entry for this table */
4758 tab = ATGetQueueEntry(wqueue, rel);
4761 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4762 * partitions that are pending detach.
4764 if (rel->rd_rel->relispartition &&
4765 cmd->subtype != AT_DetachPartitionFinalize &&
4766 PartitionHasPendingDetach(RelationGetRelid(rel)))
4767 ereport(ERROR,
4768 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4769 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4770 RelationGetRelationName(rel)),
4771 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4774 * Copy the original subcommand for each table, so we can scribble on it.
4775 * This avoids conflicts when different child tables need to make
4776 * different parse transformations (for example, the same column may have
4777 * different column numbers in different children).
4779 cmd = copyObject(cmd);
4782 * Do permissions and relkind checking, recursion to child tables if
4783 * needed, and any additional phase-1 processing needed. (But beware of
4784 * adding any processing that looks at table details that another
4785 * subcommand could change. In some cases we reject multiple subcommands
4786 * that could try to change the same state in contrary ways.)
4788 switch (cmd->subtype)
4790 case AT_AddColumn: /* ADD COLUMN */
4791 ATSimplePermissions(cmd->subtype, rel,
4792 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4793 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4794 lockmode, context);
4795 /* Recursion occurs during execution phase */
4796 pass = AT_PASS_ADD_COL;
4797 break;
4798 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4799 ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4800 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4801 lockmode, context);
4802 /* Recursion occurs during execution phase */
4803 pass = AT_PASS_ADD_COL;
4804 break;
4805 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4808 * We allow defaults on views so that INSERT into a view can have
4809 * default-ish behavior. This works because the rewriter
4810 * substitutes default values into INSERTs before it expands
4811 * rules.
4813 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4814 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4815 /* No command-specific prep needed */
4816 pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4817 break;
4818 case AT_CookedColumnDefault: /* add a pre-cooked default */
4819 /* This is currently used only in CREATE TABLE */
4820 /* (so the permission check really isn't necessary) */
4821 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4822 /* This command never recurses */
4823 pass = AT_PASS_ADD_OTHERCONSTR;
4824 break;
4825 case AT_AddIdentity:
4826 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4827 /* Set up recursion for phase 2; no other prep needed */
4828 if (recurse)
4829 cmd->recurse = true;
4830 pass = AT_PASS_ADD_OTHERCONSTR;
4831 break;
4832 case AT_SetIdentity:
4833 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4834 /* Set up recursion for phase 2; no other prep needed */
4835 if (recurse)
4836 cmd->recurse = true;
4837 /* This should run after AddIdentity, so do it in MISC pass */
4838 pass = AT_PASS_MISC;
4839 break;
4840 case AT_DropIdentity:
4841 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_FOREIGN_TABLE);
4842 /* Set up recursion for phase 2; no other prep needed */
4843 if (recurse)
4844 cmd->recurse = true;
4845 pass = AT_PASS_DROP;
4846 break;
4847 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4848 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4849 ATPrepDropNotNull(rel, recurse, recursing);
4850 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4851 pass = AT_PASS_DROP;
4852 break;
4853 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4854 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4855 /* Need command-specific recursion decision */
4856 ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4857 lockmode, context);
4858 pass = AT_PASS_COL_ATTRS;
4859 break;
4860 case AT_CheckNotNull: /* check column is already marked NOT NULL */
4861 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4862 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4863 /* No command-specific prep needed */
4864 pass = AT_PASS_COL_ATTRS;
4865 break;
4866 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4867 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4868 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4869 pass = AT_PASS_SET_EXPRESSION;
4870 break;
4871 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4872 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4873 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4874 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4875 pass = AT_PASS_DROP;
4876 break;
4877 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4878 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX | ATT_PARTITIONED_INDEX | ATT_FOREIGN_TABLE);
4879 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4880 /* No command-specific prep needed */
4881 pass = AT_PASS_MISC;
4882 break;
4883 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4884 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4885 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4886 /* This command never recurses */
4887 pass = AT_PASS_MISC;
4888 break;
4889 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4890 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_FOREIGN_TABLE);
4891 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4892 /* No command-specific prep needed */
4893 pass = AT_PASS_MISC;
4894 break;
4895 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4896 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4897 /* This command never recurses */
4898 /* No command-specific prep needed */
4899 pass = AT_PASS_MISC;
4900 break;
4901 case AT_DropColumn: /* DROP COLUMN */
4902 ATSimplePermissions(cmd->subtype, rel,
4903 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4904 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4905 lockmode, context);
4906 /* Recursion occurs during execution phase */
4907 pass = AT_PASS_DROP;
4908 break;
4909 case AT_AddIndex: /* ADD INDEX */
4910 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4911 /* This command never recurses */
4912 /* No command-specific prep needed */
4913 pass = AT_PASS_ADD_INDEX;
4914 break;
4915 case AT_AddConstraint: /* ADD CONSTRAINT */
4916 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4917 /* Recursion occurs during execution phase */
4918 /* No command-specific prep needed except saving recurse flag */
4919 if (recurse)
4920 cmd->recurse = true;
4921 pass = AT_PASS_ADD_CONSTR;
4922 break;
4923 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4924 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
4925 /* This command never recurses */
4926 /* No command-specific prep needed */
4927 pass = AT_PASS_ADD_INDEXCONSTR;
4928 break;
4929 case AT_DropConstraint: /* DROP CONSTRAINT */
4930 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
4931 ATCheckPartitionsNotInUse(rel, lockmode);
4932 /* Other recursion occurs during execution phase */
4933 /* No command-specific prep needed except saving recurse flag */
4934 if (recurse)
4935 cmd->recurse = true;
4936 pass = AT_PASS_DROP;
4937 break;
4938 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4939 ATSimplePermissions(cmd->subtype, rel,
4940 ATT_TABLE | ATT_COMPOSITE_TYPE | ATT_FOREIGN_TABLE);
4941 /* See comments for ATPrepAlterColumnType */
4942 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4943 AT_PASS_UNSET, context);
4944 Assert(cmd != NULL);
4945 /* Performs own recursion */
4946 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4947 lockmode, context);
4948 pass = AT_PASS_ALTER_TYPE;
4949 break;
4950 case AT_AlterColumnGenericOptions:
4951 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
4952 /* This command never recurses */
4953 /* No command-specific prep needed */
4954 pass = AT_PASS_MISC;
4955 break;
4956 case AT_ChangeOwner: /* ALTER OWNER */
4957 /* This command never recurses */
4958 /* No command-specific prep needed */
4959 pass = AT_PASS_MISC;
4960 break;
4961 case AT_ClusterOn: /* CLUSTER ON */
4962 case AT_DropCluster: /* SET WITHOUT CLUSTER */
4963 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
4964 /* These commands never recurse */
4965 /* No command-specific prep needed */
4966 pass = AT_PASS_MISC;
4967 break;
4968 case AT_SetLogged: /* SET LOGGED */
4969 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4970 if (tab->chgPersistence)
4971 ereport(ERROR,
4972 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4973 errmsg("cannot change persistence setting twice")));
4974 tab->chgPersistence = ATPrepChangePersistence(rel, true);
4975 /* force rewrite if necessary; see comment in ATRewriteTables */
4976 if (tab->chgPersistence)
4978 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4979 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4981 pass = AT_PASS_MISC;
4982 break;
4983 case AT_SetUnLogged: /* SET UNLOGGED */
4984 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_SEQUENCE);
4985 if (tab->chgPersistence)
4986 ereport(ERROR,
4987 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4988 errmsg("cannot change persistence setting twice")));
4989 tab->chgPersistence = ATPrepChangePersistence(rel, false);
4990 /* force rewrite if necessary; see comment in ATRewriteTables */
4991 if (tab->chgPersistence)
4993 tab->rewrite |= AT_REWRITE_ALTER_PERSISTENCE;
4994 tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4996 pass = AT_PASS_MISC;
4997 break;
4998 case AT_DropOids: /* SET WITHOUT OIDS */
4999 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5000 pass = AT_PASS_DROP;
5001 break;
5002 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5003 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5005 /* check if another access method change was already requested */
5006 if (tab->chgAccessMethod)
5007 ereport(ERROR,
5008 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5009 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5011 ATPrepSetAccessMethod(tab, rel, cmd->name);
5012 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5013 break;
5014 case AT_SetTableSpace: /* SET TABLESPACE */
5015 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW | ATT_INDEX |
5016 ATT_PARTITIONED_INDEX);
5017 /* This command never recurses */
5018 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5019 pass = AT_PASS_MISC; /* doesn't actually matter */
5020 break;
5021 case AT_SetRelOptions: /* SET (...) */
5022 case AT_ResetRelOptions: /* RESET (...) */
5023 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5024 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_VIEW | ATT_MATVIEW | ATT_INDEX);
5025 /* This command never recurses */
5026 /* No command-specific prep needed */
5027 pass = AT_PASS_MISC;
5028 break;
5029 case AT_AddInherit: /* INHERIT */
5030 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5031 /* This command never recurses */
5032 ATPrepAddInherit(rel);
5033 pass = AT_PASS_MISC;
5034 break;
5035 case AT_DropInherit: /* NO INHERIT */
5036 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5037 /* This command never recurses */
5038 /* No command-specific prep needed */
5039 pass = AT_PASS_MISC;
5040 break;
5041 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5042 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5043 /* Recursion occurs during execution phase */
5044 pass = AT_PASS_MISC;
5045 break;
5046 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5047 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5048 /* Recursion occurs during execution phase */
5049 /* No command-specific prep needed except saving recurse flag */
5050 if (recurse)
5051 cmd->recurse = true;
5052 pass = AT_PASS_MISC;
5053 break;
5054 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5055 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_MATVIEW);
5056 pass = AT_PASS_MISC;
5057 /* This command never recurses */
5058 /* No command-specific prep needed */
5059 break;
5060 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5061 case AT_EnableAlwaysTrig:
5062 case AT_EnableReplicaTrig:
5063 case AT_EnableTrigAll:
5064 case AT_EnableTrigUser:
5065 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5066 case AT_DisableTrigAll:
5067 case AT_DisableTrigUser:
5068 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
5069 /* Set up recursion for phase 2; no other prep needed */
5070 if (recurse)
5071 cmd->recurse = true;
5072 pass = AT_PASS_MISC;
5073 break;
5074 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5075 case AT_EnableAlwaysRule:
5076 case AT_EnableReplicaRule:
5077 case AT_DisableRule:
5078 case AT_AddOf: /* OF */
5079 case AT_DropOf: /* NOT OF */
5080 case AT_EnableRowSecurity:
5081 case AT_DisableRowSecurity:
5082 case AT_ForceRowSecurity:
5083 case AT_NoForceRowSecurity:
5084 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5085 /* These commands never recurse */
5086 /* No command-specific prep needed */
5087 pass = AT_PASS_MISC;
5088 break;
5089 case AT_GenericOptions:
5090 ATSimplePermissions(cmd->subtype, rel, ATT_FOREIGN_TABLE);
5091 /* No command-specific prep needed */
5092 pass = AT_PASS_MISC;
5093 break;
5094 case AT_AttachPartition:
5095 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE | ATT_PARTITIONED_INDEX);
5096 /* No command-specific prep needed */
5097 pass = AT_PASS_MISC;
5098 break;
5099 case AT_DetachPartition:
5100 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5101 /* No command-specific prep needed */
5102 pass = AT_PASS_MISC;
5103 break;
5104 case AT_DetachPartitionFinalize:
5105 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5106 /* No command-specific prep needed */
5107 pass = AT_PASS_MISC;
5108 break;
5109 case AT_SplitPartition:
5110 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5111 /* No command-specific prep needed */
5112 pass = AT_PASS_MISC;
5113 break;
5114 case AT_MergePartitions:
5115 ATSimplePermissions(cmd->subtype, rel, ATT_TABLE);
5116 /* No command-specific prep needed */
5117 pass = AT_PASS_MISC;
5118 break;
5119 default: /* oops */
5120 elog(ERROR, "unrecognized alter table type: %d",
5121 (int) cmd->subtype);
5122 pass = AT_PASS_UNSET; /* keep compiler quiet */
5123 break;
5125 Assert(pass > AT_PASS_UNSET);
5127 /* Add the subcommand to the appropriate list for phase 2 */
5128 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5132 * ATRewriteCatalogs
5134 * Traffic cop for ALTER TABLE Phase 2 operations. Subcommands are
5135 * dispatched in a "safe" execution order (designed to avoid unnecessary
5136 * conflicts).
5138 static void
5139 ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode,
5140 AlterTableUtilityContext *context)
5142 ListCell *ltab;
5145 * We process all the tables "in parallel", one pass at a time. This is
5146 * needed because we may have to propagate work from one table to another
5147 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5148 * re-adding of the foreign key constraint to the other table). Work can
5149 * only be propagated into later passes, however.
5151 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5153 /* Go through each table that needs to be processed */
5154 foreach(ltab, *wqueue)
5156 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5157 List *subcmds = tab->subcmds[pass];
5158 ListCell *lcmd;
5160 if (subcmds == NIL)
5161 continue;
5164 * Open the relation and store it in tab. This allows subroutines
5165 * close and reopen, if necessary. Appropriate lock was obtained
5166 * by phase 1, needn't get it again.
5168 tab->rel = relation_open(tab->relid, NoLock);
5170 foreach(lcmd, subcmds)
5171 ATExecCmd(wqueue, tab,
5172 lfirst_node(AlterTableCmd, lcmd),
5173 lockmode, pass, context);
5176 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5177 * (this is not done in ATExecAlterColumnType since it should be
5178 * done only once if multiple columns of a table are altered).
5180 if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5181 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5183 if (tab->rel)
5185 relation_close(tab->rel, NoLock);
5186 tab->rel = NULL;
5191 /* Check to see if a toast table must be added. */
5192 foreach(ltab, *wqueue)
5194 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5197 * If the table is source table of ATTACH PARTITION command, we did
5198 * not modify anything about it that will change its toasting
5199 * requirement, so no need to check.
5201 if (((tab->relkind == RELKIND_RELATION ||
5202 tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5203 tab->partition_constraint == NULL) ||
5204 tab->relkind == RELKIND_MATVIEW)
5205 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5210 * ATExecCmd: dispatch a subcommand to appropriate execution routine
5212 static void
5213 ATExecCmd(List **wqueue, AlteredTableInfo *tab,
5214 AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass,
5215 AlterTableUtilityContext *context)
5217 ObjectAddress address = InvalidObjectAddress;
5218 Relation rel = tab->rel;
5220 switch (cmd->subtype)
5222 case AT_AddColumn: /* ADD COLUMN */
5223 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5224 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5225 cmd->recurse, false,
5226 lockmode, cur_pass, context);
5227 break;
5228 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5229 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5230 break;
5231 case AT_CookedColumnDefault: /* add a pre-cooked default */
5232 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5233 break;
5234 case AT_AddIdentity:
5235 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5236 cur_pass, context);
5237 Assert(cmd != NULL);
5238 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5239 break;
5240 case AT_SetIdentity:
5241 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5242 cur_pass, context);
5243 Assert(cmd != NULL);
5244 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5245 break;
5246 case AT_DropIdentity:
5247 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5248 break;
5249 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5250 address = ATExecDropNotNull(rel, cmd->name, lockmode);
5251 break;
5252 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5253 address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
5254 break;
5255 case AT_CheckNotNull: /* check column is already marked NOT NULL */
5256 ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
5257 break;
5258 case AT_SetExpression:
5259 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5260 break;
5261 case AT_DropExpression:
5262 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5263 break;
5264 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5265 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5266 break;
5267 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5268 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5269 break;
5270 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5271 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5272 break;
5273 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5274 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5275 break;
5276 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5277 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5278 lockmode);
5279 break;
5280 case AT_DropColumn: /* DROP COLUMN */
5281 address = ATExecDropColumn(wqueue, rel, cmd->name,
5282 cmd->behavior, cmd->recurse, false,
5283 cmd->missing_ok, lockmode,
5284 NULL);
5285 break;
5286 case AT_AddIndex: /* ADD INDEX */
5287 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5288 lockmode);
5289 break;
5290 case AT_ReAddIndex: /* ADD INDEX */
5291 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5292 lockmode);
5293 break;
5294 case AT_ReAddStatistics: /* ADD STATISTICS */
5295 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5296 true, lockmode);
5297 break;
5298 case AT_AddConstraint: /* ADD CONSTRAINT */
5299 /* Transform the command only during initial examination */
5300 if (cur_pass == AT_PASS_ADD_CONSTR)
5301 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5302 cmd->recurse, lockmode,
5303 cur_pass, context);
5304 /* Depending on constraint type, might be no more work to do now */
5305 if (cmd != NULL)
5306 address =
5307 ATExecAddConstraint(wqueue, tab, rel,
5308 (Constraint *) cmd->def,
5309 cmd->recurse, false, lockmode);
5310 break;
5311 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5312 address =
5313 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5314 true, true, lockmode);
5315 break;
5316 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5317 * constraint */
5318 address =
5319 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5320 ((AlterDomainStmt *) cmd->def)->def,
5321 NULL);
5322 break;
5323 case AT_ReAddComment: /* Re-add existing comment */
5324 address = CommentObject((CommentStmt *) cmd->def);
5325 break;
5326 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5327 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5328 lockmode);
5329 break;
5330 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5331 address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5332 break;
5333 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5334 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5335 false, lockmode);
5336 break;
5337 case AT_DropConstraint: /* DROP CONSTRAINT */
5338 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5339 cmd->recurse, false,
5340 cmd->missing_ok, lockmode);
5341 break;
5342 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5343 /* parse transformation was done earlier */
5344 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5345 break;
5346 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5347 address =
5348 ATExecAlterColumnGenericOptions(rel, cmd->name,
5349 (List *) cmd->def, lockmode);
5350 break;
5351 case AT_ChangeOwner: /* ALTER OWNER */
5352 ATExecChangeOwner(RelationGetRelid(rel),
5353 get_rolespec_oid(cmd->newowner, false),
5354 false, lockmode);
5355 break;
5356 case AT_ClusterOn: /* CLUSTER ON */
5357 address = ATExecClusterOn(rel, cmd->name, lockmode);
5358 break;
5359 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5360 ATExecDropCluster(rel, lockmode);
5361 break;
5362 case AT_SetLogged: /* SET LOGGED */
5363 case AT_SetUnLogged: /* SET UNLOGGED */
5364 break;
5365 case AT_DropOids: /* SET WITHOUT OIDS */
5366 /* nothing to do here, oid columns don't exist anymore */
5367 break;
5368 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5371 * Only do this for partitioned tables, for which this is just a
5372 * catalog change. Tables with storage are handled by Phase 3.
5374 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5375 tab->chgAccessMethod)
5376 ATExecSetAccessMethodNoStorage(rel, tab->newAccessMethod);
5377 break;
5378 case AT_SetTableSpace: /* SET TABLESPACE */
5381 * Only do this for partitioned tables and indexes, for which this
5382 * is just a catalog change. Other relation types which have
5383 * storage are handled by Phase 3.
5385 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5386 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5387 ATExecSetTableSpaceNoStorage(rel, tab->newTableSpace);
5389 break;
5390 case AT_SetRelOptions: /* SET (...) */
5391 case AT_ResetRelOptions: /* RESET (...) */
5392 case AT_ReplaceRelOptions: /* replace entire option list */
5393 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5394 break;
5395 case AT_EnableTrig: /* ENABLE TRIGGER name */
5396 ATExecEnableDisableTrigger(rel, cmd->name,
5397 TRIGGER_FIRES_ON_ORIGIN, false,
5398 cmd->recurse,
5399 lockmode);
5400 break;
5401 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5402 ATExecEnableDisableTrigger(rel, cmd->name,
5403 TRIGGER_FIRES_ALWAYS, false,
5404 cmd->recurse,
5405 lockmode);
5406 break;
5407 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5408 ATExecEnableDisableTrigger(rel, cmd->name,
5409 TRIGGER_FIRES_ON_REPLICA, false,
5410 cmd->recurse,
5411 lockmode);
5412 break;
5413 case AT_DisableTrig: /* DISABLE TRIGGER name */
5414 ATExecEnableDisableTrigger(rel, cmd->name,
5415 TRIGGER_DISABLED, false,
5416 cmd->recurse,
5417 lockmode);
5418 break;
5419 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5420 ATExecEnableDisableTrigger(rel, NULL,
5421 TRIGGER_FIRES_ON_ORIGIN, false,
5422 cmd->recurse,
5423 lockmode);
5424 break;
5425 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5426 ATExecEnableDisableTrigger(rel, NULL,
5427 TRIGGER_DISABLED, false,
5428 cmd->recurse,
5429 lockmode);
5430 break;
5431 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5432 ATExecEnableDisableTrigger(rel, NULL,
5433 TRIGGER_FIRES_ON_ORIGIN, true,
5434 cmd->recurse,
5435 lockmode);
5436 break;
5437 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5438 ATExecEnableDisableTrigger(rel, NULL,
5439 TRIGGER_DISABLED, true,
5440 cmd->recurse,
5441 lockmode);
5442 break;
5444 case AT_EnableRule: /* ENABLE RULE name */
5445 ATExecEnableDisableRule(rel, cmd->name,
5446 RULE_FIRES_ON_ORIGIN, lockmode);
5447 break;
5448 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5449 ATExecEnableDisableRule(rel, cmd->name,
5450 RULE_FIRES_ALWAYS, lockmode);
5451 break;
5452 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5453 ATExecEnableDisableRule(rel, cmd->name,
5454 RULE_FIRES_ON_REPLICA, lockmode);
5455 break;
5456 case AT_DisableRule: /* DISABLE RULE name */
5457 ATExecEnableDisableRule(rel, cmd->name,
5458 RULE_DISABLED, lockmode);
5459 break;
5461 case AT_AddInherit:
5462 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5463 break;
5464 case AT_DropInherit:
5465 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5466 break;
5467 case AT_AddOf:
5468 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5469 break;
5470 case AT_DropOf:
5471 ATExecDropOf(rel, lockmode);
5472 break;
5473 case AT_ReplicaIdentity:
5474 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5475 break;
5476 case AT_EnableRowSecurity:
5477 ATExecSetRowSecurity(rel, true);
5478 break;
5479 case AT_DisableRowSecurity:
5480 ATExecSetRowSecurity(rel, false);
5481 break;
5482 case AT_ForceRowSecurity:
5483 ATExecForceNoForceRowSecurity(rel, true);
5484 break;
5485 case AT_NoForceRowSecurity:
5486 ATExecForceNoForceRowSecurity(rel, false);
5487 break;
5488 case AT_GenericOptions:
5489 ATExecGenericOptions(rel, (List *) cmd->def);
5490 break;
5491 case AT_AttachPartition:
5492 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5493 cur_pass, context);
5494 Assert(cmd != NULL);
5495 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5496 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5497 context);
5498 else
5499 address = ATExecAttachPartitionIdx(wqueue, rel,
5500 ((PartitionCmd *) cmd->def)->name);
5501 break;
5502 case AT_DetachPartition:
5503 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5504 cur_pass, context);
5505 Assert(cmd != NULL);
5506 /* ATPrepCmd ensures it must be a table */
5507 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5508 address = ATExecDetachPartition(wqueue, tab, rel,
5509 ((PartitionCmd *) cmd->def)->name,
5510 ((PartitionCmd *) cmd->def)->concurrent);
5511 break;
5512 case AT_DetachPartitionFinalize:
5513 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5514 break;
5515 case AT_SplitPartition:
5516 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5517 cur_pass, context);
5518 Assert(cmd != NULL);
5519 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5520 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5521 context);
5522 break;
5523 case AT_MergePartitions:
5524 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5525 cur_pass, context);
5526 Assert(cmd != NULL);
5527 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5528 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5529 context);
5530 break;
5531 default: /* oops */
5532 elog(ERROR, "unrecognized alter table type: %d",
5533 (int) cmd->subtype);
5534 break;
5538 * Report the subcommand to interested event triggers.
5540 if (cmd)
5541 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5544 * Bump the command counter to ensure the next subcommand in the sequence
5545 * can see the changes so far
5547 CommandCounterIncrement();
5551 * ATParseTransformCmd: perform parse transformation for one subcommand
5553 * Returns the transformed subcommand tree, if there is one, else NULL.
5555 * The parser may hand back additional AlterTableCmd(s) and/or other
5556 * utility statements, either before or after the original subcommand.
5557 * Other AlterTableCmds are scheduled into the appropriate slot of the
5558 * AlteredTableInfo (they had better be for later passes than the current one).
5559 * Utility statements that are supposed to happen before the AlterTableCmd
5560 * are executed immediately. Those that are supposed to happen afterwards
5561 * are added to the tab->afterStmts list to be done at the very end.
5563 static AlterTableCmd *
5564 ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
5565 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
5566 AlterTablePass cur_pass, AlterTableUtilityContext *context)
5568 AlterTableCmd *newcmd = NULL;
5569 AlterTableStmt *atstmt = makeNode(AlterTableStmt);
5570 List *beforeStmts;
5571 List *afterStmts;
5572 ListCell *lc;
5574 /* Gin up an AlterTableStmt with just this subcommand and this table */
5575 atstmt->relation =
5576 makeRangeVar(get_namespace_name(RelationGetNamespace(rel)),
5577 pstrdup(RelationGetRelationName(rel)),
5578 -1);
5579 atstmt->relation->inh = recurse;
5580 atstmt->cmds = list_make1(cmd);
5581 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5582 atstmt->missing_ok = false;
5584 /* Transform the AlterTableStmt */
5585 atstmt = transformAlterTableStmt(RelationGetRelid(rel),
5586 atstmt,
5587 context->queryString,
5588 &beforeStmts,
5589 &afterStmts);
5591 /* Execute any statements that should happen before these subcommand(s) */
5592 foreach(lc, beforeStmts)
5594 Node *stmt = (Node *) lfirst(lc);
5596 ProcessUtilityForAlterTable(stmt, context);
5597 CommandCounterIncrement();
5600 /* Examine the transformed subcommands and schedule them appropriately */
5601 foreach(lc, atstmt->cmds)
5603 AlterTableCmd *cmd2 = lfirst_node(AlterTableCmd, lc);
5604 AlterTablePass pass;
5607 * This switch need only cover the subcommand types that can be added
5608 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5609 * executing the subcommand immediately, as a substitute for the
5610 * original subcommand. (Note, however, that this does cause
5611 * AT_AddConstraint subcommands to be rescheduled into later passes,
5612 * which is important for index and foreign key constraints.)
5614 * We assume we needn't do any phase-1 checks for added subcommands.
5616 switch (cmd2->subtype)
5618 case AT_SetNotNull:
5619 /* Need command-specific recursion decision */
5620 ATPrepSetNotNull(wqueue, rel, cmd2,
5621 recurse, false,
5622 lockmode, context);
5623 pass = AT_PASS_COL_ATTRS;
5624 break;
5625 case AT_AddIndex:
5626 /* This command never recurses */
5627 /* No command-specific prep needed */
5628 pass = AT_PASS_ADD_INDEX;
5629 break;
5630 case AT_AddIndexConstraint:
5631 /* This command never recurses */
5632 /* No command-specific prep needed */
5633 pass = AT_PASS_ADD_INDEXCONSTR;
5634 break;
5635 case AT_AddConstraint:
5636 /* Recursion occurs during execution phase */
5637 if (recurse)
5638 cmd2->recurse = true;
5639 switch (castNode(Constraint, cmd2->def)->contype)
5641 case CONSTR_PRIMARY:
5642 case CONSTR_UNIQUE:
5643 case CONSTR_EXCLUSION:
5644 pass = AT_PASS_ADD_INDEXCONSTR;
5645 break;
5646 default:
5647 pass = AT_PASS_ADD_OTHERCONSTR;
5648 break;
5650 break;
5651 case AT_AlterColumnGenericOptions:
5652 /* This command never recurses */
5653 /* No command-specific prep needed */
5654 pass = AT_PASS_MISC;
5655 break;
5656 default:
5657 pass = cur_pass;
5658 break;
5661 if (pass < cur_pass)
5663 /* Cannot schedule into a pass we already finished */
5664 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5665 pass);
5667 else if (pass > cur_pass)
5669 /* OK, queue it up for later */
5670 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5672 else
5675 * We should see at most one subcommand for the current pass,
5676 * which is the transformed version of the original subcommand.
5678 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5680 /* Found the transformed version of our subcommand */
5681 newcmd = cmd2;
5683 else
5684 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5685 pass);
5689 /* Queue up any after-statements to happen at the end */
5690 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5692 return newcmd;
5696 * ATRewriteTables: ALTER TABLE phase 3
5698 static void
5699 ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode,
5700 AlterTableUtilityContext *context)
5702 ListCell *ltab;
5704 /* Go through each table that needs to be checked or rewritten */
5705 foreach(ltab, *wqueue)
5707 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5709 /* Relations without storage may be ignored here */
5710 if (!RELKIND_HAS_STORAGE(tab->relkind))
5711 continue;
5714 * If we change column data types, the operation has to be propagated
5715 * to tables that use this table's rowtype as a column type.
5716 * tab->newvals will also be non-NULL in the case where we're adding a
5717 * column with a default. We choose to forbid that case as well,
5718 * since composite types might eventually support defaults.
5720 * (Eventually we'll probably need to check for composite type
5721 * dependencies even when we're just scanning the table without a
5722 * rewrite, but at the moment a composite type does not enforce any
5723 * constraints, so it's not necessary/appropriate to enforce them just
5724 * during ALTER.)
5726 if (tab->newvals != NIL || tab->rewrite > 0)
5728 Relation rel;
5730 rel = table_open(tab->relid, NoLock);
5731 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5732 table_close(rel, NoLock);
5736 * We only need to rewrite the table if at least one column needs to
5737 * be recomputed, or we are changing its persistence or access method.
5739 * There are two reasons for requiring a rewrite when changing
5740 * persistence: on one hand, we need to ensure that the buffers
5741 * belonging to each of the two relations are marked with or without
5742 * BM_PERMANENT properly. On the other hand, since rewriting creates
5743 * and assigns a new relfilenumber, we automatically create or drop an
5744 * init fork for the relation as appropriate.
5746 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5748 /* Build a temporary relation and copy data */
5749 Relation OldHeap;
5750 Oid OIDNewHeap;
5751 Oid NewAccessMethod;
5752 Oid NewTableSpace;
5753 char persistence;
5755 OldHeap = table_open(tab->relid, NoLock);
5758 * We don't support rewriting of system catalogs; there are too
5759 * many corner cases and too little benefit. In particular this
5760 * is certainly not going to work for mapped catalogs.
5762 if (IsSystemRelation(OldHeap))
5763 ereport(ERROR,
5764 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5765 errmsg("cannot rewrite system relation \"%s\"",
5766 RelationGetRelationName(OldHeap))));
5768 if (RelationIsUsedAsCatalogTable(OldHeap))
5769 ereport(ERROR,
5770 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5771 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5772 RelationGetRelationName(OldHeap))));
5775 * Don't allow rewrite on temp tables of other backends ... their
5776 * local buffer manager is not going to cope. (This is redundant
5777 * with the check in CheckAlterTableIsSafe, but for safety we'll
5778 * check here too.)
5780 if (RELATION_IS_OTHER_TEMP(OldHeap))
5781 ereport(ERROR,
5782 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5783 errmsg("cannot rewrite temporary tables of other sessions")));
5786 * Select destination tablespace (same as original unless user
5787 * requested a change)
5789 if (tab->newTableSpace)
5790 NewTableSpace = tab->newTableSpace;
5791 else
5792 NewTableSpace = OldHeap->rd_rel->reltablespace;
5795 * Select destination access method (same as original unless user
5796 * requested a change)
5798 if (tab->chgAccessMethod)
5799 NewAccessMethod = tab->newAccessMethod;
5800 else
5801 NewAccessMethod = OldHeap->rd_rel->relam;
5804 * Select persistence of transient table (same as original unless
5805 * user requested a change)
5807 persistence = tab->chgPersistence ?
5808 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
5810 table_close(OldHeap, NoLock);
5813 * Fire off an Event Trigger now, before actually rewriting the
5814 * table.
5816 * We don't support Event Trigger for nested commands anywhere,
5817 * here included, and parsetree is given NULL when coming from
5818 * AlterTableInternal.
5820 * And fire it only once.
5822 if (parsetree)
5823 EventTriggerTableRewrite((Node *) parsetree,
5824 tab->relid,
5825 tab->rewrite);
5828 * Create transient table that will receive the modified data.
5830 * Ensure it is marked correctly as logged or unlogged. We have
5831 * to do this here so that buffers for the new relfilenumber will
5832 * have the right persistence set, and at the same time ensure
5833 * that the original filenumbers's buffers will get read in with
5834 * the correct setting (i.e. the original one). Otherwise a
5835 * rollback after the rewrite would possibly result with buffers
5836 * for the original filenumbers having the wrong persistence
5837 * setting.
5839 * NB: This relies on swap_relation_files() also swapping the
5840 * persistence. That wouldn't work for pg_class, but that can't be
5841 * unlogged anyway.
5843 OIDNewHeap = make_new_heap(tab->relid, NewTableSpace, NewAccessMethod,
5844 persistence, lockmode);
5847 * Copy the heap data into the new table with the desired
5848 * modifications, and test the current data within the table
5849 * against new constraints generated by ALTER TABLE commands.
5851 ATRewriteTable(tab, OIDNewHeap, lockmode);
5854 * Swap the physical files of the old and new heaps, then rebuild
5855 * indexes and discard the old heap. We can use RecentXmin for
5856 * the table's new relfrozenxid because we rewrote all the tuples
5857 * in ATRewriteTable, so no older Xid remains in the table. Also,
5858 * we never try to swap toast tables by content, since we have no
5859 * interest in letting this code work on system catalogs.
5861 finish_heap_swap(tab->relid, OIDNewHeap,
5862 false, false, true,
5863 !OidIsValid(tab->newTableSpace),
5864 RecentXmin,
5865 ReadNextMultiXactId(),
5866 persistence);
5868 InvokeObjectPostAlterHook(RelationRelationId, tab->relid, 0);
5870 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
5872 if (tab->chgPersistence)
5873 SequenceChangePersistence(tab->relid, tab->newrelpersistence);
5875 else
5878 * If required, test the current data within the table against new
5879 * constraints generated by ALTER TABLE commands, but don't
5880 * rebuild data.
5882 if (tab->constraints != NIL || tab->verify_new_notnull ||
5883 tab->partition_constraint != NULL)
5884 ATRewriteTable(tab, InvalidOid, lockmode);
5887 * If we had SET TABLESPACE but no reason to reconstruct tuples,
5888 * just do a block-by-block copy.
5890 if (tab->newTableSpace)
5891 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
5895 * Also change persistence of owned sequences, so that it matches the
5896 * table persistence.
5898 if (tab->chgPersistence)
5900 List *seqlist = getOwnedSequences(tab->relid);
5901 ListCell *lc;
5903 foreach(lc, seqlist)
5905 Oid seq_relid = lfirst_oid(lc);
5907 SequenceChangePersistence(seq_relid, tab->newrelpersistence);
5913 * Foreign key constraints are checked in a final pass, since (a) it's
5914 * generally best to examine each one separately, and (b) it's at least
5915 * theoretically possible that we have changed both relations of the
5916 * foreign key, and we'd better have finished both rewrites before we try
5917 * to read the tables.
5919 foreach(ltab, *wqueue)
5921 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5922 Relation rel = NULL;
5923 ListCell *lcon;
5925 /* Relations without storage may be ignored here too */
5926 if (!RELKIND_HAS_STORAGE(tab->relkind))
5927 continue;
5929 foreach(lcon, tab->constraints)
5931 NewConstraint *con = lfirst(lcon);
5933 if (con->contype == CONSTR_FOREIGN)
5935 Constraint *fkconstraint = (Constraint *) con->qual;
5936 Relation refrel;
5938 if (rel == NULL)
5940 /* Long since locked, no need for another */
5941 rel = table_open(tab->relid, NoLock);
5944 refrel = table_open(con->refrelid, RowShareLock);
5946 validateForeignKeyConstraint(fkconstraint->conname, rel, refrel,
5947 con->refindid,
5948 con->conid);
5951 * No need to mark the constraint row as validated, we did
5952 * that when we inserted the row earlier.
5955 table_close(refrel, NoLock);
5959 if (rel)
5960 table_close(rel, NoLock);
5963 /* Finally, run any afterStmts that were queued up */
5964 foreach(ltab, *wqueue)
5966 AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5967 ListCell *lc;
5969 foreach(lc, tab->afterStmts)
5971 Node *stmt = (Node *) lfirst(lc);
5973 ProcessUtilityForAlterTable(stmt, context);
5974 CommandCounterIncrement();
5980 * ATRewriteTable: scan or rewrite one table
5982 * OIDNewHeap is InvalidOid if we don't need to rewrite
5984 static void
5985 ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
5987 Relation oldrel;
5988 Relation newrel;
5989 TupleDesc oldTupDesc;
5990 TupleDesc newTupDesc;
5991 bool needscan = false;
5992 List *notnull_attrs;
5993 int i;
5994 ListCell *l;
5995 EState *estate;
5996 CommandId mycid;
5997 BulkInsertState bistate;
5998 int ti_options;
5999 ExprState *partqualstate = NULL;
6002 * Open the relation(s). We have surely already locked the existing
6003 * table.
6005 oldrel = table_open(tab->relid, NoLock);
6006 oldTupDesc = tab->oldDesc;
6007 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6009 if (OidIsValid(OIDNewHeap))
6010 newrel = table_open(OIDNewHeap, lockmode);
6011 else
6012 newrel = NULL;
6015 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6016 * is empty, so don't bother using it.
6018 if (newrel)
6020 mycid = GetCurrentCommandId(true);
6021 bistate = GetBulkInsertState();
6022 ti_options = TABLE_INSERT_SKIP_FSM;
6024 else
6026 /* keep compiler quiet about using these uninitialized */
6027 mycid = 0;
6028 bistate = NULL;
6029 ti_options = 0;
6033 * Generate the constraint and default execution states
6036 estate = CreateExecutorState();
6038 /* Build the needed expression execution states */
6039 foreach(l, tab->constraints)
6041 NewConstraint *con = lfirst(l);
6043 switch (con->contype)
6045 case CONSTR_CHECK:
6046 needscan = true;
6047 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6048 break;
6049 case CONSTR_FOREIGN:
6050 /* Nothing to do here */
6051 break;
6052 default:
6053 elog(ERROR, "unrecognized constraint type: %d",
6054 (int) con->contype);
6058 /* Build expression execution states for partition check quals */
6059 if (tab->partition_constraint)
6061 needscan = true;
6062 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6065 foreach(l, tab->newvals)
6067 NewColumnValue *ex = lfirst(l);
6069 /* expr already planned */
6070 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6073 notnull_attrs = NIL;
6074 if (newrel || tab->verify_new_notnull)
6077 * If we are rebuilding the tuples OR if we added any new but not
6078 * verified not-null constraints, check all not-null constraints. This
6079 * is a bit of overkill but it minimizes risk of bugs, and
6080 * heap_attisnull is a pretty cheap test anyway.
6082 for (i = 0; i < newTupDesc->natts; i++)
6084 Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6086 if (attr->attnotnull && !attr->attisdropped)
6087 notnull_attrs = lappend_int(notnull_attrs, i);
6089 if (notnull_attrs)
6090 needscan = true;
6093 if (newrel || needscan)
6095 ExprContext *econtext;
6096 TupleTableSlot *oldslot;
6097 TupleTableSlot *newslot;
6098 TableScanDesc scan;
6099 MemoryContext oldCxt;
6100 List *dropped_attrs = NIL;
6101 ListCell *lc;
6102 Snapshot snapshot;
6104 if (newrel)
6105 ereport(DEBUG1,
6106 (errmsg_internal("rewriting table \"%s\"",
6107 RelationGetRelationName(oldrel))));
6108 else
6109 ereport(DEBUG1,
6110 (errmsg_internal("verifying table \"%s\"",
6111 RelationGetRelationName(oldrel))));
6113 if (newrel)
6116 * All predicate locks on the tuples or pages are about to be made
6117 * invalid, because we move tuples around. Promote them to
6118 * relation locks.
6120 TransferPredicateLocksToHeapRelation(oldrel);
6123 econtext = GetPerTupleExprContext(estate);
6126 * Create necessary tuple slots. When rewriting, two slots are needed,
6127 * otherwise one suffices. In the case where one slot suffices, we
6128 * need to use the new tuple descriptor, otherwise some constraints
6129 * can't be evaluated. Note that even when the tuple layout is the
6130 * same and no rewrite is required, the tupDescs might not be
6131 * (consider ADD COLUMN without a default).
6133 if (tab->rewrite)
6135 Assert(newrel != NULL);
6136 oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6137 table_slot_callbacks(oldrel));
6138 newslot = MakeSingleTupleTableSlot(newTupDesc,
6139 table_slot_callbacks(newrel));
6142 * Set all columns in the new slot to NULL initially, to ensure
6143 * columns added as part of the rewrite are initialized to NULL.
6144 * That is necessary as tab->newvals will not contain an
6145 * expression for columns with a NULL default, e.g. when adding a
6146 * column without a default together with a column with a default
6147 * requiring an actual rewrite.
6149 ExecStoreAllNullTuple(newslot);
6151 else
6153 oldslot = MakeSingleTupleTableSlot(newTupDesc,
6154 table_slot_callbacks(oldrel));
6155 newslot = NULL;
6159 * Any attributes that are dropped according to the new tuple
6160 * descriptor can be set to NULL. We precompute the list of dropped
6161 * attributes to avoid needing to do so in the per-tuple loop.
6163 for (i = 0; i < newTupDesc->natts; i++)
6165 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6166 dropped_attrs = lappend_int(dropped_attrs, i);
6170 * Scan through the rows, generating a new row if needed and then
6171 * checking all the constraints.
6173 snapshot = RegisterSnapshot(GetLatestSnapshot());
6174 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6177 * Switch to per-tuple memory context and reset it for each tuple
6178 * produced, so we don't leak memory.
6180 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
6182 while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6184 TupleTableSlot *insertslot;
6186 if (tab->rewrite > 0)
6188 /* Extract data from old tuple */
6189 slot_getallattrs(oldslot);
6190 ExecClearTuple(newslot);
6192 /* copy attributes */
6193 memcpy(newslot->tts_values, oldslot->tts_values,
6194 sizeof(Datum) * oldslot->tts_nvalid);
6195 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6196 sizeof(bool) * oldslot->tts_nvalid);
6198 /* Set dropped attributes to null in new tuple */
6199 foreach(lc, dropped_attrs)
6200 newslot->tts_isnull[lfirst_int(lc)] = true;
6203 * Constraints and GENERATED expressions might reference the
6204 * tableoid column, so fill tts_tableOid with the desired
6205 * value. (We must do this each time, because it gets
6206 * overwritten with newrel's OID during storing.)
6208 newslot->tts_tableOid = RelationGetRelid(oldrel);
6211 * Process supplied expressions to replace selected columns.
6213 * First, evaluate expressions whose inputs come from the old
6214 * tuple.
6216 econtext->ecxt_scantuple = oldslot;
6218 foreach(l, tab->newvals)
6220 NewColumnValue *ex = lfirst(l);
6222 if (ex->is_generated)
6223 continue;
6225 newslot->tts_values[ex->attnum - 1]
6226 = ExecEvalExpr(ex->exprstate,
6227 econtext,
6228 &newslot->tts_isnull[ex->attnum - 1]);
6231 ExecStoreVirtualTuple(newslot);
6234 * Now, evaluate any expressions whose inputs come from the
6235 * new tuple. We assume these columns won't reference each
6236 * other, so that there's no ordering dependency.
6238 econtext->ecxt_scantuple = newslot;
6240 foreach(l, tab->newvals)
6242 NewColumnValue *ex = lfirst(l);
6244 if (!ex->is_generated)
6245 continue;
6247 newslot->tts_values[ex->attnum - 1]
6248 = ExecEvalExpr(ex->exprstate,
6249 econtext,
6250 &newslot->tts_isnull[ex->attnum - 1]);
6253 insertslot = newslot;
6255 else
6258 * If there's no rewrite, old and new table are guaranteed to
6259 * have the same AM, so we can just use the old slot to verify
6260 * new constraints etc.
6262 insertslot = oldslot;
6265 /* Now check any constraints on the possibly-changed tuple */
6266 econtext->ecxt_scantuple = insertslot;
6268 foreach(l, notnull_attrs)
6270 int attn = lfirst_int(l);
6272 if (slot_attisnull(insertslot, attn + 1))
6274 Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6276 ereport(ERROR,
6277 (errcode(ERRCODE_NOT_NULL_VIOLATION),
6278 errmsg("column \"%s\" of relation \"%s\" contains null values",
6279 NameStr(attr->attname),
6280 RelationGetRelationName(oldrel)),
6281 errtablecol(oldrel, attn + 1)));
6285 foreach(l, tab->constraints)
6287 NewConstraint *con = lfirst(l);
6289 switch (con->contype)
6291 case CONSTR_CHECK:
6292 if (!ExecCheck(con->qualstate, econtext))
6293 ereport(ERROR,
6294 (errcode(ERRCODE_CHECK_VIOLATION),
6295 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6296 con->name,
6297 RelationGetRelationName(oldrel)),
6298 errtableconstraint(oldrel, con->name)));
6299 break;
6300 case CONSTR_FOREIGN:
6301 /* Nothing to do here */
6302 break;
6303 default:
6304 elog(ERROR, "unrecognized constraint type: %d",
6305 (int) con->contype);
6309 if (partqualstate && !ExecCheck(partqualstate, econtext))
6311 if (tab->validate_default)
6312 ereport(ERROR,
6313 (errcode(ERRCODE_CHECK_VIOLATION),
6314 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6315 RelationGetRelationName(oldrel)),
6316 errtable(oldrel)));
6317 else
6318 ereport(ERROR,
6319 (errcode(ERRCODE_CHECK_VIOLATION),
6320 errmsg("partition constraint of relation \"%s\" is violated by some row",
6321 RelationGetRelationName(oldrel)),
6322 errtable(oldrel)));
6325 /* Write the tuple out to the new relation */
6326 if (newrel)
6327 table_tuple_insert(newrel, insertslot, mycid,
6328 ti_options, bistate);
6330 ResetExprContext(econtext);
6332 CHECK_FOR_INTERRUPTS();
6335 MemoryContextSwitchTo(oldCxt);
6336 table_endscan(scan);
6337 UnregisterSnapshot(snapshot);
6339 ExecDropSingleTupleTableSlot(oldslot);
6340 if (newslot)
6341 ExecDropSingleTupleTableSlot(newslot);
6344 FreeExecutorState(estate);
6346 table_close(oldrel, NoLock);
6347 if (newrel)
6349 FreeBulkInsertState(bistate);
6351 table_finish_bulk_insert(newrel, ti_options);
6353 table_close(newrel, NoLock);
6358 * ATGetQueueEntry: find or create an entry in the ALTER TABLE work queue
6360 static AlteredTableInfo *
6361 ATGetQueueEntry(List **wqueue, Relation rel)
6363 Oid relid = RelationGetRelid(rel);
6364 AlteredTableInfo *tab;
6365 ListCell *ltab;
6367 foreach(ltab, *wqueue)
6369 tab = (AlteredTableInfo *) lfirst(ltab);
6370 if (tab->relid == relid)
6371 return tab;
6375 * Not there, so add it. Note that we make a copy of the relation's
6376 * existing descriptor before anything interesting can happen to it.
6378 tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6379 tab->relid = relid;
6380 tab->rel = NULL; /* set later */
6381 tab->relkind = rel->rd_rel->relkind;
6382 tab->oldDesc = CreateTupleDescCopyConstr(RelationGetDescr(rel));
6383 tab->newAccessMethod = InvalidOid;
6384 tab->chgAccessMethod = false;
6385 tab->newTableSpace = InvalidOid;
6386 tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6387 tab->chgPersistence = false;
6389 *wqueue = lappend(*wqueue, tab);
6391 return tab;
6394 static const char *
6395 alter_table_type_to_string(AlterTableType cmdtype)
6397 switch (cmdtype)
6399 case AT_AddColumn:
6400 case AT_AddColumnToView:
6401 return "ADD COLUMN";
6402 case AT_ColumnDefault:
6403 case AT_CookedColumnDefault:
6404 return "ALTER COLUMN ... SET DEFAULT";
6405 case AT_DropNotNull:
6406 return "ALTER COLUMN ... DROP NOT NULL";
6407 case AT_SetNotNull:
6408 return "ALTER COLUMN ... SET NOT NULL";
6409 case AT_SetExpression:
6410 return "ALTER COLUMN ... SET EXPRESSION";
6411 case AT_DropExpression:
6412 return "ALTER COLUMN ... DROP EXPRESSION";
6413 case AT_CheckNotNull:
6414 return NULL; /* not real grammar */
6415 case AT_SetStatistics:
6416 return "ALTER COLUMN ... SET STATISTICS";
6417 case AT_SetOptions:
6418 return "ALTER COLUMN ... SET";
6419 case AT_ResetOptions:
6420 return "ALTER COLUMN ... RESET";
6421 case AT_SetStorage:
6422 return "ALTER COLUMN ... SET STORAGE";
6423 case AT_SetCompression:
6424 return "ALTER COLUMN ... SET COMPRESSION";
6425 case AT_DropColumn:
6426 return "DROP COLUMN";
6427 case AT_AddIndex:
6428 case AT_ReAddIndex:
6429 return NULL; /* not real grammar */
6430 case AT_AddConstraint:
6431 case AT_ReAddConstraint:
6432 case AT_ReAddDomainConstraint:
6433 case AT_AddIndexConstraint:
6434 return "ADD CONSTRAINT";
6435 case AT_AlterConstraint:
6436 return "ALTER CONSTRAINT";
6437 case AT_ValidateConstraint:
6438 return "VALIDATE CONSTRAINT";
6439 case AT_DropConstraint:
6440 return "DROP CONSTRAINT";
6441 case AT_ReAddComment:
6442 return NULL; /* not real grammar */
6443 case AT_AlterColumnType:
6444 return "ALTER COLUMN ... SET DATA TYPE";
6445 case AT_AlterColumnGenericOptions:
6446 return "ALTER COLUMN ... OPTIONS";
6447 case AT_ChangeOwner:
6448 return "OWNER TO";
6449 case AT_ClusterOn:
6450 return "CLUSTER ON";
6451 case AT_DropCluster:
6452 return "SET WITHOUT CLUSTER";
6453 case AT_SetAccessMethod:
6454 return "SET ACCESS METHOD";
6455 case AT_SetLogged:
6456 return "SET LOGGED";
6457 case AT_SetUnLogged:
6458 return "SET UNLOGGED";
6459 case AT_DropOids:
6460 return "SET WITHOUT OIDS";
6461 case AT_SetTableSpace:
6462 return "SET TABLESPACE";
6463 case AT_SetRelOptions:
6464 return "SET";
6465 case AT_ResetRelOptions:
6466 return "RESET";
6467 case AT_ReplaceRelOptions:
6468 return NULL; /* not real grammar */
6469 case AT_EnableTrig:
6470 return "ENABLE TRIGGER";
6471 case AT_EnableAlwaysTrig:
6472 return "ENABLE ALWAYS TRIGGER";
6473 case AT_EnableReplicaTrig:
6474 return "ENABLE REPLICA TRIGGER";
6475 case AT_DisableTrig:
6476 return "DISABLE TRIGGER";
6477 case AT_EnableTrigAll:
6478 return "ENABLE TRIGGER ALL";
6479 case AT_DisableTrigAll:
6480 return "DISABLE TRIGGER ALL";
6481 case AT_EnableTrigUser:
6482 return "ENABLE TRIGGER USER";
6483 case AT_DisableTrigUser:
6484 return "DISABLE TRIGGER USER";
6485 case AT_EnableRule:
6486 return "ENABLE RULE";
6487 case AT_EnableAlwaysRule:
6488 return "ENABLE ALWAYS RULE";
6489 case AT_EnableReplicaRule:
6490 return "ENABLE REPLICA RULE";
6491 case AT_DisableRule:
6492 return "DISABLE RULE";
6493 case AT_AddInherit:
6494 return "INHERIT";
6495 case AT_DropInherit:
6496 return "NO INHERIT";
6497 case AT_AddOf:
6498 return "OF";
6499 case AT_DropOf:
6500 return "NOT OF";
6501 case AT_ReplicaIdentity:
6502 return "REPLICA IDENTITY";
6503 case AT_EnableRowSecurity:
6504 return "ENABLE ROW SECURITY";
6505 case AT_DisableRowSecurity:
6506 return "DISABLE ROW SECURITY";
6507 case AT_ForceRowSecurity:
6508 return "FORCE ROW SECURITY";
6509 case AT_NoForceRowSecurity:
6510 return "NO FORCE ROW SECURITY";
6511 case AT_GenericOptions:
6512 return "OPTIONS";
6513 case AT_AttachPartition:
6514 return "ATTACH PARTITION";
6515 case AT_DetachPartition:
6516 return "DETACH PARTITION";
6517 case AT_DetachPartitionFinalize:
6518 return "DETACH PARTITION ... FINALIZE";
6519 case AT_SplitPartition:
6520 return "SPLIT PARTITION";
6521 case AT_MergePartitions:
6522 return "MERGE PARTITIONS";
6523 case AT_AddIdentity:
6524 return "ALTER COLUMN ... ADD IDENTITY";
6525 case AT_SetIdentity:
6526 return "ALTER COLUMN ... SET";
6527 case AT_DropIdentity:
6528 return "ALTER COLUMN ... DROP IDENTITY";
6529 case AT_ReAddStatistics:
6530 return NULL; /* not real grammar */
6533 return NULL;
6537 * ATSimplePermissions
6539 * - Ensure that it is a relation (or possibly a view)
6540 * - Ensure this user is the owner
6541 * - Ensure that it is not a system table
6543 static void
6544 ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
6546 int actual_target;
6548 switch (rel->rd_rel->relkind)
6550 case RELKIND_RELATION:
6551 case RELKIND_PARTITIONED_TABLE:
6552 actual_target = ATT_TABLE;
6553 break;
6554 case RELKIND_VIEW:
6555 actual_target = ATT_VIEW;
6556 break;
6557 case RELKIND_MATVIEW:
6558 actual_target = ATT_MATVIEW;
6559 break;
6560 case RELKIND_INDEX:
6561 actual_target = ATT_INDEX;
6562 break;
6563 case RELKIND_PARTITIONED_INDEX:
6564 actual_target = ATT_PARTITIONED_INDEX;
6565 break;
6566 case RELKIND_COMPOSITE_TYPE:
6567 actual_target = ATT_COMPOSITE_TYPE;
6568 break;
6569 case RELKIND_FOREIGN_TABLE:
6570 actual_target = ATT_FOREIGN_TABLE;
6571 break;
6572 case RELKIND_SEQUENCE:
6573 actual_target = ATT_SEQUENCE;
6574 break;
6575 default:
6576 actual_target = 0;
6577 break;
6580 /* Wrong target type? */
6581 if ((actual_target & allowed_targets) == 0)
6583 const char *action_str = alter_table_type_to_string(cmdtype);
6585 if (action_str)
6586 ereport(ERROR,
6587 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6588 /* translator: %s is a group of some SQL keywords */
6589 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6590 action_str, RelationGetRelationName(rel)),
6591 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6592 else
6593 /* internal error? */
6594 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6595 RelationGetRelationName(rel));
6598 /* Permissions checks */
6599 if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6600 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(rel->rd_rel->relkind),
6601 RelationGetRelationName(rel));
6603 if (!allowSystemTableMods && IsSystemRelation(rel))
6604 ereport(ERROR,
6605 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6606 errmsg("permission denied: \"%s\" is a system catalog",
6607 RelationGetRelationName(rel))));
6611 * ATSimpleRecursion
6613 * Simple table recursion sufficient for most ALTER TABLE operations.
6614 * All direct and indirect children are processed in an unspecified order.
6615 * Note that if a child inherits from the original table via multiple
6616 * inheritance paths, it will be visited just once.
6618 static void
6619 ATSimpleRecursion(List **wqueue, Relation rel,
6620 AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode,
6621 AlterTableUtilityContext *context)
6624 * Propagate to children, if desired and if there are (or might be) any
6625 * children.
6627 if (recurse && rel->rd_rel->relhassubclass)
6629 Oid relid = RelationGetRelid(rel);
6630 ListCell *child;
6631 List *children;
6633 children = find_all_inheritors(relid, lockmode, NULL);
6636 * find_all_inheritors does the recursive search of the inheritance
6637 * hierarchy, so all we have to do is process all of the relids in the
6638 * list that it returns.
6640 foreach(child, children)
6642 Oid childrelid = lfirst_oid(child);
6643 Relation childrel;
6645 if (childrelid == relid)
6646 continue;
6647 /* find_all_inheritors already got lock */
6648 childrel = relation_open(childrelid, NoLock);
6649 CheckAlterTableIsSafe(childrel);
6650 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6651 relation_close(childrel, NoLock);
6657 * Obtain list of partitions of the given table, locking them all at the given
6658 * lockmode and ensuring that they all pass CheckAlterTableIsSafe.
6660 * This function is a no-op if the given relation is not a partitioned table;
6661 * in particular, nothing is done if it's a legacy inheritance parent.
6663 static void
6664 ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
6666 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6668 List *inh;
6669 ListCell *cell;
6671 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6672 /* first element is the parent rel; must ignore it */
6673 for_each_from(cell, inh, 1)
6675 Relation childrel;
6677 /* find_all_inheritors already got lock */
6678 childrel = table_open(lfirst_oid(cell), NoLock);
6679 CheckAlterTableIsSafe(childrel);
6680 table_close(childrel, NoLock);
6682 list_free(inh);
6687 * ATTypedTableRecursion
6689 * Propagate ALTER TYPE operations to the typed tables of that type.
6690 * Also check the RESTRICT/CASCADE behavior. Given CASCADE, also permit
6691 * recursion to inheritance children of the typed tables.
6693 static void
6694 ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd,
6695 LOCKMODE lockmode, AlterTableUtilityContext *context)
6697 ListCell *child;
6698 List *children;
6700 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6702 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6703 RelationGetRelationName(rel),
6704 cmd->behavior);
6706 foreach(child, children)
6708 Oid childrelid = lfirst_oid(child);
6709 Relation childrel;
6711 childrel = relation_open(childrelid, lockmode);
6712 CheckAlterTableIsSafe(childrel);
6713 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6714 relation_close(childrel, NoLock);
6720 * find_composite_type_dependencies
6722 * Check to see if the type "typeOid" is being used as a column in some table
6723 * (possibly nested several levels deep in composite types, arrays, etc!).
6724 * Eventually, we'd like to propagate the check or rewrite operation
6725 * into such tables, but for now, just error out if we find any.
6727 * Caller should provide either the associated relation of a rowtype,
6728 * or a type name (not both) for use in the error message, if any.
6730 * Note that "typeOid" is not necessarily a composite type; it could also be
6731 * another container type such as an array or range, or a domain over one of
6732 * these things. The name of this function is therefore somewhat historical,
6733 * but it's not worth changing.
6735 * We assume that functions and views depending on the type are not reasons
6736 * to reject the ALTER. (How safe is this really?)
6738 void
6739 find_composite_type_dependencies(Oid typeOid, Relation origRelation,
6740 const char *origTypeName)
6742 Relation depRel;
6743 ScanKeyData key[2];
6744 SysScanDesc depScan;
6745 HeapTuple depTup;
6747 /* since this function recurses, it could be driven to stack overflow */
6748 check_stack_depth();
6751 * We scan pg_depend to find those things that depend on the given type.
6752 * (We assume we can ignore refobjsubid for a type.)
6754 depRel = table_open(DependRelationId, AccessShareLock);
6756 ScanKeyInit(&key[0],
6757 Anum_pg_depend_refclassid,
6758 BTEqualStrategyNumber, F_OIDEQ,
6759 ObjectIdGetDatum(TypeRelationId));
6760 ScanKeyInit(&key[1],
6761 Anum_pg_depend_refobjid,
6762 BTEqualStrategyNumber, F_OIDEQ,
6763 ObjectIdGetDatum(typeOid));
6765 depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6766 NULL, 2, key);
6768 while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6770 Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6771 Relation rel;
6772 TupleDesc tupleDesc;
6773 Form_pg_attribute att;
6775 /* Check for directly dependent types */
6776 if (pg_depend->classid == TypeRelationId)
6779 * This must be an array, domain, or range containing the given
6780 * type, so recursively check for uses of this type. Note that
6781 * any error message will mention the original type not the
6782 * container; this is intentional.
6784 find_composite_type_dependencies(pg_depend->objid,
6785 origRelation, origTypeName);
6786 continue;
6789 /* Else, ignore dependees that aren't relations */
6790 if (pg_depend->classid != RelationRelationId)
6791 continue;
6793 rel = relation_open(pg_depend->objid, AccessShareLock);
6794 tupleDesc = RelationGetDescr(rel);
6797 * If objsubid identifies a specific column, refer to that in error
6798 * messages. Otherwise, search to see if there's a user column of the
6799 * type. (We assume system columns are never of interesting types.)
6800 * The search is needed because an index containing an expression
6801 * column of the target type will just be recorded as a whole-relation
6802 * dependency. If we do not find a column of the type, the dependency
6803 * must indicate that the type is transiently referenced in an index
6804 * expression but not stored on disk, which we assume is OK, just as
6805 * we do for references in views. (It could also be that the target
6806 * type is embedded in some container type that is stored in an index
6807 * column, but the previous recursion should catch such cases.)
6809 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6810 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6811 else
6813 att = NULL;
6814 for (int attno = 1; attno <= tupleDesc->natts; attno++)
6816 att = TupleDescAttr(tupleDesc, attno - 1);
6817 if (att->atttypid == typeOid && !att->attisdropped)
6818 break;
6819 att = NULL;
6821 if (att == NULL)
6823 /* No such column, so assume OK */
6824 relation_close(rel, AccessShareLock);
6825 continue;
6830 * We definitely should reject if the relation has storage. If it's
6831 * partitioned, then perhaps we don't have to reject: if there are
6832 * partitions then we'll fail when we find one, else there is no
6833 * stored data to worry about. However, it's possible that the type
6834 * change would affect conclusions about whether the type is sortable
6835 * or hashable and thus (if it's a partitioning column) break the
6836 * partitioning rule. For now, reject for partitioned rels too.
6838 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6839 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6841 if (origTypeName)
6842 ereport(ERROR,
6843 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6844 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6845 origTypeName,
6846 RelationGetRelationName(rel),
6847 NameStr(att->attname))));
6848 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6849 ereport(ERROR,
6850 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6851 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6852 RelationGetRelationName(origRelation),
6853 RelationGetRelationName(rel),
6854 NameStr(att->attname))));
6855 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6856 ereport(ERROR,
6857 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6858 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6859 RelationGetRelationName(origRelation),
6860 RelationGetRelationName(rel),
6861 NameStr(att->attname))));
6862 else
6863 ereport(ERROR,
6864 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6865 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6866 RelationGetRelationName(origRelation),
6867 RelationGetRelationName(rel),
6868 NameStr(att->attname))));
6870 else if (OidIsValid(rel->rd_rel->reltype))
6873 * A view or composite type itself isn't a problem, but we must
6874 * recursively check for indirect dependencies via its rowtype.
6876 find_composite_type_dependencies(rel->rd_rel->reltype,
6877 origRelation, origTypeName);
6880 relation_close(rel, AccessShareLock);
6883 systable_endscan(depScan);
6885 relation_close(depRel, AccessShareLock);
6890 * find_typed_table_dependencies
6892 * Check to see if a composite type is being used as the type of a
6893 * typed table. Abort if any are found and behavior is RESTRICT.
6894 * Else return the list of tables.
6896 static List *
6897 find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
6899 Relation classRel;
6900 ScanKeyData key[1];
6901 TableScanDesc scan;
6902 HeapTuple tuple;
6903 List *result = NIL;
6905 classRel = table_open(RelationRelationId, AccessShareLock);
6907 ScanKeyInit(&key[0],
6908 Anum_pg_class_reloftype,
6909 BTEqualStrategyNumber, F_OIDEQ,
6910 ObjectIdGetDatum(typeOid));
6912 scan = table_beginscan_catalog(classRel, 1, key);
6914 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6916 Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6918 if (behavior == DROP_RESTRICT)
6919 ereport(ERROR,
6920 (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6921 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6922 typeName),
6923 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6924 else
6925 result = lappend_oid(result, classform->oid);
6928 table_endscan(scan);
6929 table_close(classRel, AccessShareLock);
6931 return result;
6936 * check_of_type
6938 * Check whether a type is suitable for CREATE TABLE OF/ALTER TABLE OF. If it
6939 * isn't suitable, throw an error. Currently, we require that the type
6940 * originated with CREATE TYPE AS. We could support any row type, but doing so
6941 * would require handling a number of extra corner cases in the DDL commands.
6942 * (Also, allowing domain-over-composite would open up a can of worms about
6943 * whether and how the domain's constraints should apply to derived tables.)
6945 void
6946 check_of_type(HeapTuple typetuple)
6948 Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6949 bool typeOk = false;
6951 if (typ->typtype == TYPTYPE_COMPOSITE)
6953 Relation typeRelation;
6955 Assert(OidIsValid(typ->typrelid));
6956 typeRelation = relation_open(typ->typrelid, AccessShareLock);
6957 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6960 * Close the parent rel, but keep our AccessShareLock on it until xact
6961 * commit. That will prevent someone else from deleting or ALTERing
6962 * the type before the typed table creation/conversion commits.
6964 relation_close(typeRelation, NoLock);
6966 if (!typeOk)
6967 ereport(ERROR,
6968 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6969 errmsg("type %s is not a composite type",
6970 format_type_be(typ->oid))));
6975 * ALTER TABLE ADD COLUMN
6977 * Adds an additional attribute to a relation making the assumption that
6978 * CHECK, NOT NULL, and FOREIGN KEY constraints will be removed from the
6979 * AT_AddColumn AlterTableCmd by parse_utilcmd.c and added as independent
6980 * AlterTableCmd's.
6982 * ADD COLUMN cannot use the normal ALTER TABLE recursion mechanism, because we
6983 * have to decide at runtime whether to recurse or not depending on whether we
6984 * actually add a column or merely merge with an existing column. (We can't
6985 * check this in a static pre-pass because it won't handle multiple inheritance
6986 * situations correctly.)
6988 static void
6989 ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
6990 bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode,
6991 AlterTableUtilityContext *context)
6993 if (rel->rd_rel->reloftype && !recursing)
6994 ereport(ERROR,
6995 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6996 errmsg("cannot add column to typed table")));
6998 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6999 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7001 if (recurse && !is_view)
7002 cmd->recurse = true;
7006 * Add a column to a table. The return value is the address of the
7007 * new column in the parent relation.
7009 * cmd is pass-by-ref so that we can replace it with the parse-transformed
7010 * copy (but that happens only after we check for IF NOT EXISTS).
7012 static ObjectAddress
7013 ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
7014 AlterTableCmd **cmd, bool recurse, bool recursing,
7015 LOCKMODE lockmode, AlterTablePass cur_pass,
7016 AlterTableUtilityContext *context)
7018 Oid myrelid = RelationGetRelid(rel);
7019 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7020 bool if_not_exists = (*cmd)->missing_ok;
7021 Relation pgclass,
7022 attrdesc;
7023 HeapTuple reltup;
7024 Form_pg_attribute attribute;
7025 int newattnum;
7026 char relkind;
7027 Expr *defval;
7028 List *children;
7029 ListCell *child;
7030 AlterTableCmd *childcmd;
7031 ObjectAddress address;
7032 TupleDesc tupdesc;
7034 /* since this function recurses, it could be driven to stack overflow */
7035 check_stack_depth();
7037 /* At top level, permission check was done in ATPrepCmd, else do it */
7038 if (recursing)
7039 ATSimplePermissions((*cmd)->subtype, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
7041 if (rel->rd_rel->relispartition && !recursing)
7042 ereport(ERROR,
7043 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7044 errmsg("cannot add column to a partition")));
7046 attrdesc = table_open(AttributeRelationId, RowExclusiveLock);
7049 * Are we adding the column to a recursion child? If so, check whether to
7050 * merge with an existing definition for the column. If we do merge, we
7051 * must not recurse. Children will already have the column, and recursing
7052 * into them would mess up attinhcount.
7054 if (colDef->inhcount > 0)
7056 HeapTuple tuple;
7058 /* Does child already have a column by this name? */
7059 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7060 if (HeapTupleIsValid(tuple))
7062 Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
7063 Oid ctypeId;
7064 int32 ctypmod;
7065 Oid ccollid;
7067 /* Child column must match on type, typmod, and collation */
7068 typenameTypeIdAndMod(NULL, colDef->typeName, &ctypeId, &ctypmod);
7069 if (ctypeId != childatt->atttypid ||
7070 ctypmod != childatt->atttypmod)
7071 ereport(ERROR,
7072 (errcode(ERRCODE_DATATYPE_MISMATCH),
7073 errmsg("child table \"%s\" has different type for column \"%s\"",
7074 RelationGetRelationName(rel), colDef->colname)));
7075 ccollid = GetColumnDefCollation(NULL, colDef, ctypeId);
7076 if (ccollid != childatt->attcollation)
7077 ereport(ERROR,
7078 (errcode(ERRCODE_COLLATION_MISMATCH),
7079 errmsg("child table \"%s\" has different collation for column \"%s\"",
7080 RelationGetRelationName(rel), colDef->colname),
7081 errdetail("\"%s\" versus \"%s\"",
7082 get_collation_name(ccollid),
7083 get_collation_name(childatt->attcollation))));
7085 /* Bump the existing child att's inhcount */
7086 childatt->attinhcount++;
7087 if (childatt->attinhcount < 0)
7088 ereport(ERROR,
7089 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
7090 errmsg("too many inheritance parents"));
7091 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7093 heap_freetuple(tuple);
7095 /* Inform the user about the merge */
7096 ereport(NOTICE,
7097 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7098 colDef->colname, RelationGetRelationName(rel))));
7100 table_close(attrdesc, RowExclusiveLock);
7102 /* Make the child column change visible */
7103 CommandCounterIncrement();
7105 return InvalidObjectAddress;
7109 /* skip if the name already exists and if_not_exists is true */
7110 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7112 table_close(attrdesc, RowExclusiveLock);
7113 return InvalidObjectAddress;
7117 * Okay, we need to add the column, so go ahead and do parse
7118 * transformation. This can result in queueing up, or even immediately
7119 * executing, subsidiary operations (such as creation of unique indexes);
7120 * so we mustn't do it until we have made the if_not_exists check.
7122 * When recursing, the command was already transformed and we needn't do
7123 * so again. Also, if context isn't given we can't transform. (That
7124 * currently happens only for AT_AddColumnToView; we expect that view.c
7125 * passed us a ColumnDef that doesn't need work.)
7127 if (context != NULL && !recursing)
7129 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7130 cur_pass, context);
7131 Assert(*cmd != NULL);
7132 colDef = castNode(ColumnDef, (*cmd)->def);
7136 * Regular inheritance children are independent enough not to inherit the
7137 * identity column from parent hence cannot recursively add identity
7138 * column if the table has inheritance children.
7140 * Partitions, on the other hand, are integral part of a partitioned table
7141 * and inherit identity column. Hence propagate identity column down the
7142 * partition hierarchy.
7144 if (colDef->identity &&
7145 recurse &&
7146 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7147 find_inheritance_children(myrelid, NoLock) != NIL)
7148 ereport(ERROR,
7149 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7150 errmsg("cannot recursively add identity column to table that has child tables")));
7152 pgclass = table_open(RelationRelationId, RowExclusiveLock);
7154 reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
7155 if (!HeapTupleIsValid(reltup))
7156 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7157 relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
7159 /* Determine the new attribute's number */
7160 newattnum = ((Form_pg_class) GETSTRUCT(reltup))->relnatts + 1;
7161 if (newattnum > MaxHeapAttributeNumber)
7162 ereport(ERROR,
7163 (errcode(ERRCODE_TOO_MANY_COLUMNS),
7164 errmsg("tables can have at most %d columns",
7165 MaxHeapAttributeNumber)));
7168 * Construct new attribute's pg_attribute entry.
7170 tupdesc = BuildDescForRelation(list_make1(colDef));
7172 attribute = TupleDescAttr(tupdesc, 0);
7174 /* Fix up attribute number */
7175 attribute->attnum = newattnum;
7177 /* make sure datatype is legal for a column */
7178 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7179 list_make1_oid(rel->rd_rel->reltype),
7182 InsertPgAttributeTuples(attrdesc, tupdesc, myrelid, NULL, NULL);
7184 table_close(attrdesc, RowExclusiveLock);
7187 * Update pg_class tuple as appropriate
7189 ((Form_pg_class) GETSTRUCT(reltup))->relnatts = newattnum;
7191 CatalogTupleUpdate(pgclass, &reltup->t_self, reltup);
7193 heap_freetuple(reltup);
7195 /* Post creation hook for new attribute */
7196 InvokeObjectPostCreateHook(RelationRelationId, myrelid, newattnum);
7198 table_close(pgclass, RowExclusiveLock);
7200 /* Make the attribute's catalog entry visible */
7201 CommandCounterIncrement();
7204 * Store the DEFAULT, if any, in the catalogs
7206 if (colDef->raw_default)
7208 RawColumnDefault *rawEnt;
7210 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7211 rawEnt->attnum = attribute->attnum;
7212 rawEnt->raw_default = copyObject(colDef->raw_default);
7215 * Attempt to skip a complete table rewrite by storing the specified
7216 * DEFAULT value outside of the heap. This may be disabled inside
7217 * AddRelationNewConstraints if the optimization cannot be applied.
7219 rawEnt->missingMode = (!colDef->generated);
7221 rawEnt->generated = colDef->generated;
7224 * This function is intended for CREATE TABLE, so it processes a
7225 * _list_ of defaults, but we just do one.
7227 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7228 false, true, false, NULL);
7230 /* Make the additional catalog changes visible */
7231 CommandCounterIncrement();
7234 * Did the request for a missing value work? If not we'll have to do a
7235 * rewrite
7237 if (!rawEnt->missingMode)
7238 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7242 * Tell Phase 3 to fill in the default expression, if there is one.
7244 * If there is no default, Phase 3 doesn't have to do anything, because
7245 * that effectively means that the default is NULL. The heap tuple access
7246 * routines always check for attnum > # of attributes in tuple, and return
7247 * NULL if so, so without any modification of the tuple data we will get
7248 * the effect of NULL values in the new column.
7250 * An exception occurs when the new column is of a domain type: the domain
7251 * might have a not-null constraint, or a check constraint that indirectly
7252 * rejects nulls. If there are any domain constraints then we construct
7253 * an explicit NULL default value that will be passed through
7254 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7255 * rewriting the table which we really don't have to do, but the present
7256 * design of domain processing doesn't offer any simple way of checking
7257 * the constraints more directly.)
7259 * Note: we use build_column_default, and not just the cooked default
7260 * returned by AddRelationNewConstraints, so that the right thing happens
7261 * when a datatype's default applies.
7263 * Note: it might seem that this should happen at the end of Phase 2, so
7264 * that the effects of subsequent subcommands can be taken into account.
7265 * It's intentional that we do it now, though. The new column should be
7266 * filled according to what is said in the ADD COLUMN subcommand, so that
7267 * the effects are the same as if this subcommand had been run by itself
7268 * and the later subcommands had been issued in new ALTER TABLE commands.
7270 * We can skip this entirely for relations without storage, since Phase 3
7271 * is certainly not going to touch them. System attributes don't have
7272 * interesting defaults, either.
7274 if (RELKIND_HAS_STORAGE(relkind))
7277 * For an identity column, we can't use build_column_default(),
7278 * because the sequence ownership isn't set yet. So do it manually.
7280 if (colDef->identity)
7282 NextValueExpr *nve = makeNode(NextValueExpr);
7284 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7285 nve->typeId = attribute->atttypid;
7287 defval = (Expr *) nve;
7289 /* must do a rewrite for identity columns */
7290 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7292 else
7293 defval = (Expr *) build_column_default(rel, attribute->attnum);
7295 if (!defval && DomainHasConstraints(attribute->atttypid))
7297 Oid baseTypeId;
7298 int32 baseTypeMod;
7299 Oid baseTypeColl;
7301 baseTypeMod = attribute->atttypmod;
7302 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7303 baseTypeColl = get_typcollation(baseTypeId);
7304 defval = (Expr *) makeNullConst(baseTypeId, baseTypeMod, baseTypeColl);
7305 defval = (Expr *) coerce_to_target_type(NULL,
7306 (Node *) defval,
7307 baseTypeId,
7308 attribute->atttypid,
7309 attribute->atttypmod,
7310 COERCION_ASSIGNMENT,
7311 COERCE_IMPLICIT_CAST,
7312 -1);
7313 if (defval == NULL) /* should not happen */
7314 elog(ERROR, "failed to coerce base type to domain");
7317 if (defval)
7319 NewColumnValue *newval;
7321 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
7322 newval->attnum = attribute->attnum;
7323 newval->expr = expression_planner(defval);
7324 newval->is_generated = (colDef->generated != '\0');
7326 tab->newvals = lappend(tab->newvals, newval);
7329 if (DomainHasConstraints(attribute->atttypid))
7330 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
7332 if (!TupleDescAttr(rel->rd_att, attribute->attnum - 1)->atthasmissing)
7335 * If the new column is NOT NULL, and there is no missing value,
7336 * tell Phase 3 it needs to check for NULLs.
7338 tab->verify_new_notnull |= colDef->is_not_null;
7343 * Add needed dependency entries for the new column.
7345 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7346 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7349 * Propagate to children as appropriate. Unlike most other ALTER
7350 * routines, we have to do this one level of recursion at a time; we can't
7351 * use find_all_inheritors to do it in one pass.
7353 children =
7354 find_inheritance_children(RelationGetRelid(rel), lockmode);
7357 * If we are told not to recurse, there had better not be any child
7358 * tables; else the addition would put them out of step.
7360 if (children && !recurse)
7361 ereport(ERROR,
7362 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7363 errmsg("column must be added to child tables too")));
7365 /* Children should see column as singly inherited */
7366 if (!recursing)
7368 childcmd = copyObject(*cmd);
7369 colDef = castNode(ColumnDef, childcmd->def);
7370 colDef->inhcount = 1;
7371 colDef->is_local = false;
7373 else
7374 childcmd = *cmd; /* no need to copy again */
7376 foreach(child, children)
7378 Oid childrelid = lfirst_oid(child);
7379 Relation childrel;
7380 AlteredTableInfo *childtab;
7382 /* find_inheritance_children already got lock */
7383 childrel = table_open(childrelid, NoLock);
7384 CheckAlterTableIsSafe(childrel);
7386 /* Find or create work queue entry for this table */
7387 childtab = ATGetQueueEntry(wqueue, childrel);
7389 /* Recurse to child; return value is ignored */
7390 ATExecAddColumn(wqueue, childtab, childrel,
7391 &childcmd, recurse, true,
7392 lockmode, cur_pass, context);
7394 table_close(childrel, NoLock);
7397 ObjectAddressSubSet(address, RelationRelationId, myrelid, newattnum);
7398 return address;
7402 * If a new or renamed column will collide with the name of an existing
7403 * column and if_not_exists is false then error out, else do nothing.
7405 static bool
7406 check_for_column_name_collision(Relation rel, const char *colname,
7407 bool if_not_exists)
7409 HeapTuple attTuple;
7410 int attnum;
7413 * this test is deliberately not attisdropped-aware, since if one tries to
7414 * add a column matching a dropped column name, it's gonna fail anyway.
7416 attTuple = SearchSysCache2(ATTNAME,
7417 ObjectIdGetDatum(RelationGetRelid(rel)),
7418 PointerGetDatum(colname));
7419 if (!HeapTupleIsValid(attTuple))
7420 return true;
7422 attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7423 ReleaseSysCache(attTuple);
7426 * We throw a different error message for conflicts with system column
7427 * names, since they are normally not shown and the user might otherwise
7428 * be confused about the reason for the conflict.
7430 if (attnum <= 0)
7431 ereport(ERROR,
7432 (errcode(ERRCODE_DUPLICATE_COLUMN),
7433 errmsg("column name \"%s\" conflicts with a system column name",
7434 colname)));
7435 else
7437 if (if_not_exists)
7439 ereport(NOTICE,
7440 (errcode(ERRCODE_DUPLICATE_COLUMN),
7441 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7442 colname, RelationGetRelationName(rel))));
7443 return false;
7446 ereport(ERROR,
7447 (errcode(ERRCODE_DUPLICATE_COLUMN),
7448 errmsg("column \"%s\" of relation \"%s\" already exists",
7449 colname, RelationGetRelationName(rel))));
7452 return true;
7456 * Install a column's dependency on its datatype.
7458 static void
7459 add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
7461 ObjectAddress myself,
7462 referenced;
7464 myself.classId = RelationRelationId;
7465 myself.objectId = relid;
7466 myself.objectSubId = attnum;
7467 referenced.classId = TypeRelationId;
7468 referenced.objectId = typid;
7469 referenced.objectSubId = 0;
7470 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7474 * Install a column's dependency on its collation.
7476 static void
7477 add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
7479 ObjectAddress myself,
7480 referenced;
7482 /* We know the default collation is pinned, so don't bother recording it */
7483 if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7485 myself.classId = RelationRelationId;
7486 myself.objectId = relid;
7487 myself.objectSubId = attnum;
7488 referenced.classId = CollationRelationId;
7489 referenced.objectId = collid;
7490 referenced.objectSubId = 0;
7491 recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7496 * ALTER TABLE ALTER COLUMN DROP NOT NULL
7499 static void
7500 ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
7503 * If the parent is a partitioned table, like check constraints, we do not
7504 * support removing the NOT NULL while partitions exist.
7506 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7508 PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
7510 Assert(partdesc != NULL);
7511 if (partdesc->nparts > 0 && !recurse && !recursing)
7512 ereport(ERROR,
7513 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7514 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7515 errhint("Do not specify the ONLY keyword.")));
7520 * Return the address of the modified column. If the column was already
7521 * nullable, InvalidObjectAddress is returned.
7523 static ObjectAddress
7524 ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
7526 HeapTuple tuple;
7527 Form_pg_attribute attTup;
7528 AttrNumber attnum;
7529 Relation attr_rel;
7530 List *indexoidlist;
7531 ObjectAddress address;
7534 * lookup the attribute
7536 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7538 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7539 if (!HeapTupleIsValid(tuple))
7540 ereport(ERROR,
7541 (errcode(ERRCODE_UNDEFINED_COLUMN),
7542 errmsg("column \"%s\" of relation \"%s\" does not exist",
7543 colName, RelationGetRelationName(rel))));
7544 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7545 attnum = attTup->attnum;
7547 /* Prevent them from altering a system attribute */
7548 if (attnum <= 0)
7549 ereport(ERROR,
7550 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7551 errmsg("cannot alter system column \"%s\"",
7552 colName)));
7554 if (attTup->attidentity)
7555 ereport(ERROR,
7556 (errcode(ERRCODE_SYNTAX_ERROR),
7557 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7558 colName, RelationGetRelationName(rel))));
7561 * Check that the attribute is not in a primary key or in an index used as
7562 * a replica identity.
7564 * Note: we'll throw error even if the pkey index is not valid.
7567 /* Loop over all indexes on the relation */
7568 indexoidlist = RelationGetIndexList(rel);
7570 foreach_oid(indexoid, indexoidlist)
7572 HeapTuple indexTuple;
7573 Form_pg_index indexStruct;
7575 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7576 if (!HeapTupleIsValid(indexTuple))
7577 elog(ERROR, "cache lookup failed for index %u", indexoid);
7578 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7581 * If the index is not a primary key or an index used as replica
7582 * identity, skip the check.
7584 if (indexStruct->indisprimary || indexStruct->indisreplident)
7587 * Loop over each attribute in the primary key or the index used
7588 * as replica identity and see if it matches the to-be-altered
7589 * attribute.
7591 for (int i = 0; i < indexStruct->indnkeyatts; i++)
7593 if (indexStruct->indkey.values[i] == attnum)
7595 if (indexStruct->indisprimary)
7596 ereport(ERROR,
7597 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7598 errmsg("column \"%s\" is in a primary key",
7599 colName)));
7600 else
7601 ereport(ERROR,
7602 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7603 errmsg("column \"%s\" is in index used as replica identity",
7604 colName)));
7609 ReleaseSysCache(indexTuple);
7612 list_free(indexoidlist);
7614 /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7615 if (rel->rd_rel->relispartition)
7617 Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7618 Relation parent = table_open(parentId, AccessShareLock);
7619 TupleDesc tupDesc = RelationGetDescr(parent);
7620 AttrNumber parent_attnum;
7622 parent_attnum = get_attnum(parentId, colName);
7623 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7624 ereport(ERROR,
7625 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7626 errmsg("column \"%s\" is marked NOT NULL in parent table",
7627 colName)));
7628 table_close(parent, AccessShareLock);
7632 * Okay, actually perform the catalog change ... if needed
7634 if (attTup->attnotnull)
7636 attTup->attnotnull = false;
7638 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7640 ObjectAddressSubSet(address, RelationRelationId,
7641 RelationGetRelid(rel), attnum);
7643 else
7644 address = InvalidObjectAddress;
7646 InvokeObjectPostAlterHook(RelationRelationId,
7647 RelationGetRelid(rel), attnum);
7649 table_close(attr_rel, RowExclusiveLock);
7651 return address;
7655 * ALTER TABLE ALTER COLUMN SET NOT NULL
7658 static void
7659 ATPrepSetNotNull(List **wqueue, Relation rel,
7660 AlterTableCmd *cmd, bool recurse, bool recursing,
7661 LOCKMODE lockmode, AlterTableUtilityContext *context)
7664 * If we're already recursing, there's nothing to do; the topmost
7665 * invocation of ATSimpleRecursion already visited all children.
7667 if (recursing)
7668 return;
7671 * If the target column is already marked NOT NULL, we can skip recursing
7672 * to children, because their columns should already be marked NOT NULL as
7673 * well. But there's no point in checking here unless the relation has
7674 * some children; else we can just wait till execution to check. (If it
7675 * does have children, however, this can save taking per-child locks
7676 * unnecessarily. This greatly improves concurrency in some parallel
7677 * restore scenarios.)
7679 * Unfortunately, we can only apply this optimization to partitioned
7680 * tables, because traditional inheritance doesn't enforce that child
7681 * columns be NOT NULL when their parent is. (That's a bug that should
7682 * get fixed someday.)
7684 if (rel->rd_rel->relhassubclass &&
7685 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7687 HeapTuple tuple;
7688 bool attnotnull;
7690 tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
7692 /* Might as well throw the error now, if name is bad */
7693 if (!HeapTupleIsValid(tuple))
7694 ereport(ERROR,
7695 (errcode(ERRCODE_UNDEFINED_COLUMN),
7696 errmsg("column \"%s\" of relation \"%s\" does not exist",
7697 cmd->name, RelationGetRelationName(rel))));
7699 attnotnull = ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull;
7700 ReleaseSysCache(tuple);
7701 if (attnotnull)
7702 return;
7706 * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7707 * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7708 * normal recursion logic.
7710 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
7711 !recurse)
7713 AlterTableCmd *newcmd = makeNode(AlterTableCmd);
7715 newcmd->subtype = AT_CheckNotNull;
7716 newcmd->name = pstrdup(cmd->name);
7717 ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
7719 else
7720 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
7724 * Return the address of the modified column. If the column was already NOT
7725 * NULL, InvalidObjectAddress is returned.
7727 static ObjectAddress
7728 ATExecSetNotNull(AlteredTableInfo *tab, Relation rel,
7729 const char *colName, LOCKMODE lockmode)
7731 HeapTuple tuple;
7732 AttrNumber attnum;
7733 Relation attr_rel;
7734 ObjectAddress address;
7737 * lookup the attribute
7739 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7741 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7743 if (!HeapTupleIsValid(tuple))
7744 ereport(ERROR,
7745 (errcode(ERRCODE_UNDEFINED_COLUMN),
7746 errmsg("column \"%s\" of relation \"%s\" does not exist",
7747 colName, RelationGetRelationName(rel))));
7749 attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
7751 /* Prevent them from altering a system attribute */
7752 if (attnum <= 0)
7753 ereport(ERROR,
7754 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7755 errmsg("cannot alter system column \"%s\"",
7756 colName)));
7759 * Okay, actually perform the catalog change ... if needed
7761 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7763 ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
7765 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7768 * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7769 * are set NOT NULL; however, if we can find a constraint which proves
7770 * this then we can skip that. We needn't bother looking if we've
7771 * already found that we must verify some other not-null constraint.
7773 if (!tab->verify_new_notnull &&
7774 !NotNullImpliedByRelConstraints(rel, (Form_pg_attribute) GETSTRUCT(tuple)))
7776 /* Tell Phase 3 it needs to test the constraint */
7777 tab->verify_new_notnull = true;
7780 ObjectAddressSubSet(address, RelationRelationId,
7781 RelationGetRelid(rel), attnum);
7783 else
7784 address = InvalidObjectAddress;
7786 InvokeObjectPostAlterHook(RelationRelationId,
7787 RelationGetRelid(rel), attnum);
7789 table_close(attr_rel, RowExclusiveLock);
7791 return address;
7795 * ALTER TABLE ALTER COLUMN CHECK NOT NULL
7797 * This doesn't exist in the grammar, but we generate AT_CheckNotNull
7798 * commands against the partitions of a partitioned table if the user
7799 * writes ALTER TABLE ONLY ... SET NOT NULL on the partitioned table,
7800 * or tries to create a primary key on it (which internally creates
7801 * AT_SetNotNull on the partitioned table). Such a command doesn't
7802 * allow us to actually modify any partition, but we want to let it
7803 * go through if the partitions are already properly marked.
7805 * In future, this might need to adjust the child table's state, likely
7806 * by incrementing an inheritance count for the attnotnull constraint.
7807 * For now we need only check for the presence of the flag.
7809 static void
7810 ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel,
7811 const char *colName, LOCKMODE lockmode)
7813 HeapTuple tuple;
7815 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7817 if (!HeapTupleIsValid(tuple))
7818 ereport(ERROR,
7819 errcode(ERRCODE_UNDEFINED_COLUMN),
7820 errmsg("column \"%s\" of relation \"%s\" does not exist",
7821 colName, RelationGetRelationName(rel)));
7823 if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7824 ereport(ERROR,
7825 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7826 errmsg("constraint must be added to child tables too"),
7827 errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7828 colName, RelationGetRelationName(rel)),
7829 errhint("Do not specify the ONLY keyword.")));
7831 ReleaseSysCache(tuple);
7835 * NotNullImpliedByRelConstraints
7836 * Does rel's existing constraints imply NOT NULL for the given attribute?
7838 static bool
7839 NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
7841 NullTest *nnulltest = makeNode(NullTest);
7843 nnulltest->arg = (Expr *) makeVar(1,
7844 attr->attnum,
7845 attr->atttypid,
7846 attr->atttypmod,
7847 attr->attcollation,
7849 nnulltest->nulltesttype = IS_NOT_NULL;
7852 * argisrow = false is correct even for a composite column, because
7853 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7854 * case, just IS DISTINCT FROM NULL.
7856 nnulltest->argisrow = false;
7857 nnulltest->location = -1;
7859 if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7861 ereport(DEBUG1,
7862 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7863 RelationGetRelationName(rel), NameStr(attr->attname))));
7864 return true;
7867 return false;
7871 * ALTER TABLE ALTER COLUMN SET/DROP DEFAULT
7873 * Return the address of the affected column.
7875 static ObjectAddress
7876 ATExecColumnDefault(Relation rel, const char *colName,
7877 Node *newDefault, LOCKMODE lockmode)
7879 TupleDesc tupdesc = RelationGetDescr(rel);
7880 AttrNumber attnum;
7881 ObjectAddress address;
7884 * get the number of the attribute
7886 attnum = get_attnum(RelationGetRelid(rel), colName);
7887 if (attnum == InvalidAttrNumber)
7888 ereport(ERROR,
7889 (errcode(ERRCODE_UNDEFINED_COLUMN),
7890 errmsg("column \"%s\" of relation \"%s\" does not exist",
7891 colName, RelationGetRelationName(rel))));
7893 /* Prevent them from altering a system attribute */
7894 if (attnum <= 0)
7895 ereport(ERROR,
7896 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7897 errmsg("cannot alter system column \"%s\"",
7898 colName)));
7900 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7901 ereport(ERROR,
7902 (errcode(ERRCODE_SYNTAX_ERROR),
7903 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7904 colName, RelationGetRelationName(rel)),
7905 /* translator: %s is an SQL ALTER command */
7906 newDefault ? 0 : errhint("Use %s instead.",
7907 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7909 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7910 ereport(ERROR,
7911 (errcode(ERRCODE_SYNTAX_ERROR),
7912 errmsg("column \"%s\" of relation \"%s\" is a generated column",
7913 colName, RelationGetRelationName(rel)),
7914 newDefault ?
7915 /* translator: %s is an SQL ALTER command */
7916 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7917 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7918 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7921 * Remove any old default for the column. We use RESTRICT here for
7922 * safety, but at present we do not expect anything to depend on the
7923 * default.
7925 * We treat removing the existing default as an internal operation when it
7926 * is preparatory to adding a new default, but as a user-initiated
7927 * operation when the user asked for a drop.
7929 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7930 newDefault != NULL);
7932 if (newDefault)
7934 /* SET DEFAULT */
7935 RawColumnDefault *rawEnt;
7937 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7938 rawEnt->attnum = attnum;
7939 rawEnt->raw_default = newDefault;
7940 rawEnt->missingMode = false;
7941 rawEnt->generated = '\0';
7944 * This function is intended for CREATE TABLE, so it processes a
7945 * _list_ of defaults, but we just do one.
7947 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
7948 false, true, false, NULL);
7951 ObjectAddressSubSet(address, RelationRelationId,
7952 RelationGetRelid(rel), attnum);
7953 return address;
7957 * Add a pre-cooked default expression.
7959 * Return the address of the affected column.
7961 static ObjectAddress
7962 ATExecCookedColumnDefault(Relation rel, AttrNumber attnum,
7963 Node *newDefault)
7965 ObjectAddress address;
7967 /* We assume no checking is required */
7970 * Remove any old default for the column. We use RESTRICT here for
7971 * safety, but at present we do not expect anything to depend on the
7972 * default. (In ordinary cases, there could not be a default in place
7973 * anyway, but it's possible when combining LIKE with inheritance.)
7975 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, false,
7976 true);
7978 (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7980 ObjectAddressSubSet(address, RelationRelationId,
7981 RelationGetRelid(rel), attnum);
7982 return address;
7986 * ALTER TABLE ALTER COLUMN ADD IDENTITY
7988 * Return the address of the affected column.
7990 static ObjectAddress
7991 ATExecAddIdentity(Relation rel, const char *colName,
7992 Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
7994 Relation attrelation;
7995 HeapTuple tuple;
7996 Form_pg_attribute attTup;
7997 AttrNumber attnum;
7998 ObjectAddress address;
7999 ColumnDef *cdef = castNode(ColumnDef, def);
8000 bool ispartitioned;
8002 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8003 if (ispartitioned && !recurse)
8004 ereport(ERROR,
8005 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8006 errmsg("cannot add identity to a column of only the partitioned table"),
8007 errhint("Do not specify the ONLY keyword.")));
8009 if (rel->rd_rel->relispartition && !recursing)
8010 ereport(ERROR,
8011 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8012 errmsg("cannot add identity to a column of a partition"));
8014 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8016 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8017 if (!HeapTupleIsValid(tuple))
8018 ereport(ERROR,
8019 (errcode(ERRCODE_UNDEFINED_COLUMN),
8020 errmsg("column \"%s\" of relation \"%s\" does not exist",
8021 colName, RelationGetRelationName(rel))));
8022 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8023 attnum = attTup->attnum;
8025 /* Can't alter a system attribute */
8026 if (attnum <= 0)
8027 ereport(ERROR,
8028 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8029 errmsg("cannot alter system column \"%s\"",
8030 colName)));
8033 * Creating a column as identity implies NOT NULL, so adding the identity
8034 * to an existing column that is not NOT NULL would create a state that
8035 * cannot be reproduced without contortions.
8037 if (!attTup->attnotnull)
8038 ereport(ERROR,
8039 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8040 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8041 colName, RelationGetRelationName(rel))));
8043 if (attTup->attidentity)
8044 ereport(ERROR,
8045 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8046 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8047 colName, RelationGetRelationName(rel))));
8049 if (attTup->atthasdef)
8050 ereport(ERROR,
8051 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8052 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8053 colName, RelationGetRelationName(rel))));
8055 attTup->attidentity = cdef->identity;
8056 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8058 InvokeObjectPostAlterHook(RelationRelationId,
8059 RelationGetRelid(rel),
8060 attTup->attnum);
8061 ObjectAddressSubSet(address, RelationRelationId,
8062 RelationGetRelid(rel), attnum);
8063 heap_freetuple(tuple);
8065 table_close(attrelation, RowExclusiveLock);
8068 * Recurse to propagate the identity column to partitions. Identity is
8069 * not inherited in regular inheritance children.
8071 if (recurse && ispartitioned)
8073 List *children;
8074 ListCell *lc;
8076 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8078 foreach(lc, children)
8080 Relation childrel;
8082 childrel = table_open(lfirst_oid(lc), NoLock);
8083 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8084 table_close(childrel, NoLock);
8088 return address;
8092 * ALTER TABLE ALTER COLUMN SET { GENERATED or sequence options }
8094 * Return the address of the affected column.
8096 static ObjectAddress
8097 ATExecSetIdentity(Relation rel, const char *colName, Node *def,
8098 LOCKMODE lockmode, bool recurse, bool recursing)
8100 ListCell *option;
8101 DefElem *generatedEl = NULL;
8102 HeapTuple tuple;
8103 Form_pg_attribute attTup;
8104 AttrNumber attnum;
8105 Relation attrelation;
8106 ObjectAddress address;
8107 bool ispartitioned;
8109 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8110 if (ispartitioned && !recurse)
8111 ereport(ERROR,
8112 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8113 errmsg("cannot change identity column of only the partitioned table"),
8114 errhint("Do not specify the ONLY keyword.")));
8116 if (rel->rd_rel->relispartition && !recursing)
8117 ereport(ERROR,
8118 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8119 errmsg("cannot change identity column of a partition"));
8121 foreach(option, castNode(List, def))
8123 DefElem *defel = lfirst_node(DefElem, option);
8125 if (strcmp(defel->defname, "generated") == 0)
8127 if (generatedEl)
8128 ereport(ERROR,
8129 (errcode(ERRCODE_SYNTAX_ERROR),
8130 errmsg("conflicting or redundant options")));
8131 generatedEl = defel;
8133 else
8134 elog(ERROR, "option \"%s\" not recognized",
8135 defel->defname);
8139 * Even if there is nothing to change here, we run all the checks. There
8140 * will be a subsequent ALTER SEQUENCE that relies on everything being
8141 * there.
8144 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8145 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8146 if (!HeapTupleIsValid(tuple))
8147 ereport(ERROR,
8148 (errcode(ERRCODE_UNDEFINED_COLUMN),
8149 errmsg("column \"%s\" of relation \"%s\" does not exist",
8150 colName, RelationGetRelationName(rel))));
8152 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8153 attnum = attTup->attnum;
8155 if (attnum <= 0)
8156 ereport(ERROR,
8157 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8158 errmsg("cannot alter system column \"%s\"",
8159 colName)));
8161 if (!attTup->attidentity)
8162 ereport(ERROR,
8163 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8164 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8165 colName, RelationGetRelationName(rel))));
8167 if (generatedEl)
8169 attTup->attidentity = defGetInt32(generatedEl);
8170 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8172 InvokeObjectPostAlterHook(RelationRelationId,
8173 RelationGetRelid(rel),
8174 attTup->attnum);
8175 ObjectAddressSubSet(address, RelationRelationId,
8176 RelationGetRelid(rel), attnum);
8178 else
8179 address = InvalidObjectAddress;
8181 heap_freetuple(tuple);
8182 table_close(attrelation, RowExclusiveLock);
8185 * Recurse to propagate the identity change to partitions. Identity is not
8186 * inherited in regular inheritance children.
8188 if (generatedEl && recurse && ispartitioned)
8190 List *children;
8191 ListCell *lc;
8193 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8195 foreach(lc, children)
8197 Relation childrel;
8199 childrel = table_open(lfirst_oid(lc), NoLock);
8200 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8201 table_close(childrel, NoLock);
8205 return address;
8209 * ALTER TABLE ALTER COLUMN DROP IDENTITY
8211 * Return the address of the affected column.
8213 static ObjectAddress
8214 ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode,
8215 bool recurse, bool recursing)
8217 HeapTuple tuple;
8218 Form_pg_attribute attTup;
8219 AttrNumber attnum;
8220 Relation attrelation;
8221 ObjectAddress address;
8222 Oid seqid;
8223 ObjectAddress seqaddress;
8224 bool ispartitioned;
8226 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8227 if (ispartitioned && !recurse)
8228 ereport(ERROR,
8229 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8230 errmsg("cannot drop identity from a column of only the partitioned table"),
8231 errhint("Do not specify the ONLY keyword.")));
8233 if (rel->rd_rel->relispartition && !recursing)
8234 ereport(ERROR,
8235 errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8236 errmsg("cannot drop identity from a column of a partition"));
8238 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8239 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8240 if (!HeapTupleIsValid(tuple))
8241 ereport(ERROR,
8242 (errcode(ERRCODE_UNDEFINED_COLUMN),
8243 errmsg("column \"%s\" of relation \"%s\" does not exist",
8244 colName, RelationGetRelationName(rel))));
8246 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8247 attnum = attTup->attnum;
8249 if (attnum <= 0)
8250 ereport(ERROR,
8251 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8252 errmsg("cannot alter system column \"%s\"",
8253 colName)));
8255 if (!attTup->attidentity)
8257 if (!missing_ok)
8258 ereport(ERROR,
8259 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8260 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8261 colName, RelationGetRelationName(rel))));
8262 else
8264 ereport(NOTICE,
8265 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8266 colName, RelationGetRelationName(rel))));
8267 heap_freetuple(tuple);
8268 table_close(attrelation, RowExclusiveLock);
8269 return InvalidObjectAddress;
8273 attTup->attidentity = '\0';
8274 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8276 InvokeObjectPostAlterHook(RelationRelationId,
8277 RelationGetRelid(rel),
8278 attTup->attnum);
8279 ObjectAddressSubSet(address, RelationRelationId,
8280 RelationGetRelid(rel), attnum);
8281 heap_freetuple(tuple);
8283 table_close(attrelation, RowExclusiveLock);
8286 * Recurse to drop the identity from column in partitions. Identity is
8287 * not inherited in regular inheritance children so ignore them.
8289 if (recurse && ispartitioned)
8291 List *children;
8292 ListCell *lc;
8294 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8296 foreach(lc, children)
8298 Relation childrel;
8300 childrel = table_open(lfirst_oid(lc), NoLock);
8301 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8302 table_close(childrel, NoLock);
8306 if (!recursing)
8308 /* drop the internal sequence */
8309 seqid = getIdentitySequence(rel, attnum, false);
8310 deleteDependencyRecordsForClass(RelationRelationId, seqid,
8311 RelationRelationId, DEPENDENCY_INTERNAL);
8312 CommandCounterIncrement();
8313 seqaddress.classId = RelationRelationId;
8314 seqaddress.objectId = seqid;
8315 seqaddress.objectSubId = 0;
8316 performDeletion(&seqaddress, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
8319 return address;
8323 * ALTER TABLE ALTER COLUMN SET EXPRESSION
8325 * Return the address of the affected column.
8327 static ObjectAddress
8328 ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName,
8329 Node *newExpr, LOCKMODE lockmode)
8331 HeapTuple tuple;
8332 Form_pg_attribute attTup;
8333 AttrNumber attnum;
8334 Oid attrdefoid;
8335 ObjectAddress address;
8336 Expr *defval;
8337 NewColumnValue *newval;
8338 RawColumnDefault *rawEnt;
8340 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8341 if (!HeapTupleIsValid(tuple))
8342 ereport(ERROR,
8343 (errcode(ERRCODE_UNDEFINED_COLUMN),
8344 errmsg("column \"%s\" of relation \"%s\" does not exist",
8345 colName, RelationGetRelationName(rel))));
8347 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8348 attnum = attTup->attnum;
8350 if (attnum <= 0)
8351 ereport(ERROR,
8352 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8353 errmsg("cannot alter system column \"%s\"",
8354 colName)));
8356 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8357 ereport(ERROR,
8358 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8359 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8360 colName, RelationGetRelationName(rel))));
8361 ReleaseSysCache(tuple);
8364 * Clear all the missing values if we're rewriting the table, since this
8365 * renders them pointless.
8367 RelationClearMissing(rel);
8369 /* make sure we don't conflict with later attribute modifications */
8370 CommandCounterIncrement();
8373 * Find everything that depends on the column (constraints, indexes, etc),
8374 * and record enough information to let us recreate the objects after
8375 * rewrite.
8377 RememberAllDependentForRebuilding(tab, AT_SetExpression, rel, attnum, colName);
8380 * Drop the dependency records of the GENERATED expression, in particular
8381 * its INTERNAL dependency on the column, which would otherwise cause
8382 * dependency.c to refuse to perform the deletion.
8384 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8385 if (!OidIsValid(attrdefoid))
8386 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8387 RelationGetRelid(rel), attnum);
8388 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8390 /* Make above changes visible */
8391 CommandCounterIncrement();
8394 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8395 * safety, but at present we do not expect anything to depend on the
8396 * expression.
8398 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8399 false, false);
8401 /* Prepare to store the new expression, in the catalogs */
8402 rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8403 rawEnt->attnum = attnum;
8404 rawEnt->raw_default = newExpr;
8405 rawEnt->missingMode = false;
8406 rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8408 /* Store the generated expression */
8409 AddRelationNewConstraints(rel, list_make1(rawEnt), NIL,
8410 false, true, false, NULL);
8412 /* Make above new expression visible */
8413 CommandCounterIncrement();
8415 /* Prepare for table rewrite */
8416 defval = (Expr *) build_column_default(rel, attnum);
8418 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
8419 newval->attnum = attnum;
8420 newval->expr = expression_planner(defval);
8421 newval->is_generated = true;
8423 tab->newvals = lappend(tab->newvals, newval);
8424 tab->rewrite |= AT_REWRITE_DEFAULT_VAL;
8426 /* Drop any pg_statistic entry for the column */
8427 RemoveStatistics(RelationGetRelid(rel), attnum);
8429 InvokeObjectPostAlterHook(RelationRelationId,
8430 RelationGetRelid(rel), attnum);
8432 ObjectAddressSubSet(address, RelationRelationId,
8433 RelationGetRelid(rel), attnum);
8434 return address;
8438 * ALTER TABLE ALTER COLUMN DROP EXPRESSION
8440 static void
8441 ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
8444 * Reject ONLY if there are child tables. We could implement this, but it
8445 * is a bit complicated. GENERATED clauses must be attached to the column
8446 * definition and cannot be added later like DEFAULT, so if a child table
8447 * has a generation expression that the parent does not have, the child
8448 * column will necessarily be an attislocal column. So to implement ONLY
8449 * here, we'd need extra code to update attislocal of the direct child
8450 * tables, somewhat similar to how DROP COLUMN does it, so that the
8451 * resulting state can be properly dumped and restored.
8453 if (!recurse &&
8454 find_inheritance_children(RelationGetRelid(rel), lockmode))
8455 ereport(ERROR,
8456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8457 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8460 * Cannot drop generation expression from inherited columns.
8462 if (!recursing)
8464 HeapTuple tuple;
8465 Form_pg_attribute attTup;
8467 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8468 if (!HeapTupleIsValid(tuple))
8469 ereport(ERROR,
8470 (errcode(ERRCODE_UNDEFINED_COLUMN),
8471 errmsg("column \"%s\" of relation \"%s\" does not exist",
8472 cmd->name, RelationGetRelationName(rel))));
8474 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8476 if (attTup->attinhcount > 0)
8477 ereport(ERROR,
8478 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8479 errmsg("cannot drop generation expression from inherited column")));
8484 * Return the address of the affected column.
8486 static ObjectAddress
8487 ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
8489 HeapTuple tuple;
8490 Form_pg_attribute attTup;
8491 AttrNumber attnum;
8492 Relation attrelation;
8493 Oid attrdefoid;
8494 ObjectAddress address;
8496 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8497 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8498 if (!HeapTupleIsValid(tuple))
8499 ereport(ERROR,
8500 (errcode(ERRCODE_UNDEFINED_COLUMN),
8501 errmsg("column \"%s\" of relation \"%s\" does not exist",
8502 colName, RelationGetRelationName(rel))));
8504 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8505 attnum = attTup->attnum;
8507 if (attnum <= 0)
8508 ereport(ERROR,
8509 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8510 errmsg("cannot alter system column \"%s\"",
8511 colName)));
8513 if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8515 if (!missing_ok)
8516 ereport(ERROR,
8517 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8518 errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8519 colName, RelationGetRelationName(rel))));
8520 else
8522 ereport(NOTICE,
8523 (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8524 colName, RelationGetRelationName(rel))));
8525 heap_freetuple(tuple);
8526 table_close(attrelation, RowExclusiveLock);
8527 return InvalidObjectAddress;
8532 * Mark the column as no longer generated. (The atthasdef flag needs to
8533 * get cleared too, but RemoveAttrDefault will handle that.)
8535 attTup->attgenerated = '\0';
8536 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8538 InvokeObjectPostAlterHook(RelationRelationId,
8539 RelationGetRelid(rel),
8540 attnum);
8541 heap_freetuple(tuple);
8543 table_close(attrelation, RowExclusiveLock);
8546 * Drop the dependency records of the GENERATED expression, in particular
8547 * its INTERNAL dependency on the column, which would otherwise cause
8548 * dependency.c to refuse to perform the deletion.
8550 attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8551 if (!OidIsValid(attrdefoid))
8552 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8553 RelationGetRelid(rel), attnum);
8554 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8556 /* Make above changes visible */
8557 CommandCounterIncrement();
8560 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8561 * safety, but at present we do not expect anything to depend on the
8562 * default.
8564 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT,
8565 false, false);
8567 ObjectAddressSubSet(address, RelationRelationId,
8568 RelationGetRelid(rel), attnum);
8569 return address;
8573 * ALTER TABLE ALTER COLUMN SET STATISTICS
8575 * Return value is the address of the modified column
8577 static ObjectAddress
8578 ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
8580 int newtarget = 0;
8581 bool newtarget_default;
8582 Relation attrelation;
8583 HeapTuple tuple,
8584 newtuple;
8585 Form_pg_attribute attrtuple;
8586 AttrNumber attnum;
8587 ObjectAddress address;
8588 Datum repl_val[Natts_pg_attribute];
8589 bool repl_null[Natts_pg_attribute];
8590 bool repl_repl[Natts_pg_attribute];
8593 * We allow referencing columns by numbers only for indexes, since table
8594 * column numbers could contain gaps if columns are later dropped.
8596 if (rel->rd_rel->relkind != RELKIND_INDEX &&
8597 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8598 !colName)
8599 ereport(ERROR,
8600 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8601 errmsg("cannot refer to non-index column by number")));
8603 /* -1 was used in previous versions for the default setting */
8604 if (newValue && intVal(newValue) != -1)
8606 newtarget = intVal(newValue);
8607 newtarget_default = false;
8609 else
8610 newtarget_default = true;
8612 if (!newtarget_default)
8615 * Limit target to a sane range
8617 if (newtarget < 0)
8619 ereport(ERROR,
8620 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8621 errmsg("statistics target %d is too low",
8622 newtarget)));
8624 else if (newtarget > MAX_STATISTICS_TARGET)
8626 newtarget = MAX_STATISTICS_TARGET;
8627 ereport(WARNING,
8628 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8629 errmsg("lowering statistics target to %d",
8630 newtarget)));
8634 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8636 if (colName)
8638 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8640 if (!HeapTupleIsValid(tuple))
8641 ereport(ERROR,
8642 (errcode(ERRCODE_UNDEFINED_COLUMN),
8643 errmsg("column \"%s\" of relation \"%s\" does not exist",
8644 colName, RelationGetRelationName(rel))));
8646 else
8648 tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8650 if (!HeapTupleIsValid(tuple))
8651 ereport(ERROR,
8652 (errcode(ERRCODE_UNDEFINED_COLUMN),
8653 errmsg("column number %d of relation \"%s\" does not exist",
8654 colNum, RelationGetRelationName(rel))));
8657 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8659 attnum = attrtuple->attnum;
8660 if (attnum <= 0)
8661 ereport(ERROR,
8662 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8663 errmsg("cannot alter system column \"%s\"",
8664 colName)));
8666 if (rel->rd_rel->relkind == RELKIND_INDEX ||
8667 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8669 if (attnum > rel->rd_index->indnkeyatts)
8670 ereport(ERROR,
8671 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8672 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8673 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8674 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8675 ereport(ERROR,
8676 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8677 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8678 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8679 errhint("Alter statistics on table column instead.")));
8682 /* Build new tuple. */
8683 memset(repl_null, false, sizeof(repl_null));
8684 memset(repl_repl, false, sizeof(repl_repl));
8685 if (!newtarget_default)
8686 repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8687 else
8688 repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8689 repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8690 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8691 repl_val, repl_null, repl_repl);
8692 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8694 InvokeObjectPostAlterHook(RelationRelationId,
8695 RelationGetRelid(rel),
8696 attrtuple->attnum);
8697 ObjectAddressSubSet(address, RelationRelationId,
8698 RelationGetRelid(rel), attnum);
8700 heap_freetuple(newtuple);
8702 ReleaseSysCache(tuple);
8704 table_close(attrelation, RowExclusiveLock);
8706 return address;
8710 * Return value is the address of the modified column
8712 static ObjectAddress
8713 ATExecSetOptions(Relation rel, const char *colName, Node *options,
8714 bool isReset, LOCKMODE lockmode)
8716 Relation attrelation;
8717 HeapTuple tuple,
8718 newtuple;
8719 Form_pg_attribute attrtuple;
8720 AttrNumber attnum;
8721 Datum datum,
8722 newOptions;
8723 bool isnull;
8724 ObjectAddress address;
8725 Datum repl_val[Natts_pg_attribute];
8726 bool repl_null[Natts_pg_attribute];
8727 bool repl_repl[Natts_pg_attribute];
8729 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8731 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8733 if (!HeapTupleIsValid(tuple))
8734 ereport(ERROR,
8735 (errcode(ERRCODE_UNDEFINED_COLUMN),
8736 errmsg("column \"%s\" of relation \"%s\" does not exist",
8737 colName, RelationGetRelationName(rel))));
8738 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8740 attnum = attrtuple->attnum;
8741 if (attnum <= 0)
8742 ereport(ERROR,
8743 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8744 errmsg("cannot alter system column \"%s\"",
8745 colName)));
8747 /* Generate new proposed attoptions (text array) */
8748 datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8749 &isnull);
8750 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8751 castNode(List, options), NULL, NULL,
8752 false, isReset);
8753 /* Validate new options */
8754 (void) attribute_reloptions(newOptions, true);
8756 /* Build new tuple. */
8757 memset(repl_null, false, sizeof(repl_null));
8758 memset(repl_repl, false, sizeof(repl_repl));
8759 if (newOptions != (Datum) 0)
8760 repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8761 else
8762 repl_null[Anum_pg_attribute_attoptions - 1] = true;
8763 repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8764 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8765 repl_val, repl_null, repl_repl);
8767 /* Update system catalog. */
8768 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8770 InvokeObjectPostAlterHook(RelationRelationId,
8771 RelationGetRelid(rel),
8772 attrtuple->attnum);
8773 ObjectAddressSubSet(address, RelationRelationId,
8774 RelationGetRelid(rel), attnum);
8776 heap_freetuple(newtuple);
8778 ReleaseSysCache(tuple);
8780 table_close(attrelation, RowExclusiveLock);
8782 return address;
8786 * Helper function for ATExecSetStorage and ATExecSetCompression
8788 * Set the attstorage and/or attcompression fields for index columns
8789 * associated with the specified table column.
8791 static void
8792 SetIndexStorageProperties(Relation rel, Relation attrelation,
8793 AttrNumber attnum,
8794 bool setstorage, char newstorage,
8795 bool setcompression, char newcompression,
8796 LOCKMODE lockmode)
8798 ListCell *lc;
8800 foreach(lc, RelationGetIndexList(rel))
8802 Oid indexoid = lfirst_oid(lc);
8803 Relation indrel;
8804 AttrNumber indattnum = 0;
8805 HeapTuple tuple;
8807 indrel = index_open(indexoid, lockmode);
8809 for (int i = 0; i < indrel->rd_index->indnatts; i++)
8811 if (indrel->rd_index->indkey.values[i] == attnum)
8813 indattnum = i + 1;
8814 break;
8818 if (indattnum == 0)
8820 index_close(indrel, lockmode);
8821 continue;
8824 tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8826 if (HeapTupleIsValid(tuple))
8828 Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8830 if (setstorage)
8831 attrtuple->attstorage = newstorage;
8833 if (setcompression)
8834 attrtuple->attcompression = newcompression;
8836 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8838 InvokeObjectPostAlterHook(RelationRelationId,
8839 RelationGetRelid(rel),
8840 attrtuple->attnum);
8842 heap_freetuple(tuple);
8845 index_close(indrel, lockmode);
8850 * ALTER TABLE ALTER COLUMN SET STORAGE
8852 * Return value is the address of the modified column
8854 static ObjectAddress
8855 ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
8857 Relation attrelation;
8858 HeapTuple tuple;
8859 Form_pg_attribute attrtuple;
8860 AttrNumber attnum;
8861 ObjectAddress address;
8863 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8865 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8867 if (!HeapTupleIsValid(tuple))
8868 ereport(ERROR,
8869 (errcode(ERRCODE_UNDEFINED_COLUMN),
8870 errmsg("column \"%s\" of relation \"%s\" does not exist",
8871 colName, RelationGetRelationName(rel))));
8872 attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8874 attnum = attrtuple->attnum;
8875 if (attnum <= 0)
8876 ereport(ERROR,
8877 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8878 errmsg("cannot alter system column \"%s\"",
8879 colName)));
8881 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8883 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8885 InvokeObjectPostAlterHook(RelationRelationId,
8886 RelationGetRelid(rel),
8887 attrtuple->attnum);
8890 * Apply the change to indexes as well (only for simple index columns,
8891 * matching behavior of index.c ConstructTupleDescriptor()).
8893 SetIndexStorageProperties(rel, attrelation, attnum,
8894 true, attrtuple->attstorage,
8895 false, 0,
8896 lockmode);
8898 heap_freetuple(tuple);
8900 table_close(attrelation, RowExclusiveLock);
8902 ObjectAddressSubSet(address, RelationRelationId,
8903 RelationGetRelid(rel), attnum);
8904 return address;
8909 * ALTER TABLE DROP COLUMN
8911 * DROP COLUMN cannot use the normal ALTER TABLE recursion mechanism,
8912 * because we have to decide at runtime whether to recurse or not depending
8913 * on whether attinhcount goes to zero or not. (We can't check this in a
8914 * static pre-pass because it won't handle multiple inheritance situations
8915 * correctly.)
8917 static void
8918 ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
8919 AlterTableCmd *cmd, LOCKMODE lockmode,
8920 AlterTableUtilityContext *context)
8922 if (rel->rd_rel->reloftype && !recursing)
8923 ereport(ERROR,
8924 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8925 errmsg("cannot drop column from typed table")));
8927 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8928 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8930 if (recurse)
8931 cmd->recurse = true;
8935 * Drops column 'colName' from relation 'rel' and returns the address of the
8936 * dropped column. The column is also dropped (or marked as no longer
8937 * inherited from relation) from the relation's inheritance children, if any.
8939 * In the recursive invocations for inheritance child relations, instead of
8940 * dropping the column directly (if to be dropped at all), its object address
8941 * is added to 'addrs', which must be non-NULL in such invocations. All
8942 * columns are dropped at the same time after all the children have been
8943 * checked recursively.
8945 static ObjectAddress
8946 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
8947 DropBehavior behavior,
8948 bool recurse, bool recursing,
8949 bool missing_ok, LOCKMODE lockmode,
8950 ObjectAddresses *addrs)
8952 HeapTuple tuple;
8953 Form_pg_attribute targetatt;
8954 AttrNumber attnum;
8955 List *children;
8956 ObjectAddress object;
8957 bool is_expr;
8959 /* At top level, permission check was done in ATPrepCmd, else do it */
8960 if (recursing)
8961 ATSimplePermissions(AT_DropColumn, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
8963 /* Initialize addrs on the first invocation */
8964 Assert(!recursing || addrs != NULL);
8966 /* since this function recurses, it could be driven to stack overflow */
8967 check_stack_depth();
8969 if (!recursing)
8970 addrs = new_object_addresses();
8973 * get the number of the attribute
8975 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8976 if (!HeapTupleIsValid(tuple))
8978 if (!missing_ok)
8980 ereport(ERROR,
8981 (errcode(ERRCODE_UNDEFINED_COLUMN),
8982 errmsg("column \"%s\" of relation \"%s\" does not exist",
8983 colName, RelationGetRelationName(rel))));
8985 else
8987 ereport(NOTICE,
8988 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8989 colName, RelationGetRelationName(rel))));
8990 return InvalidObjectAddress;
8993 targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
8995 attnum = targetatt->attnum;
8997 /* Can't drop a system attribute */
8998 if (attnum <= 0)
8999 ereport(ERROR,
9000 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9001 errmsg("cannot drop system column \"%s\"",
9002 colName)));
9005 * Don't drop inherited columns, unless recursing (presumably from a drop
9006 * of the parent column)
9008 if (targetatt->attinhcount > 0 && !recursing)
9009 ereport(ERROR,
9010 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9011 errmsg("cannot drop inherited column \"%s\"",
9012 colName)));
9015 * Don't drop columns used in the partition key, either. (If we let this
9016 * go through, the key column's dependencies would cause a cascaded drop
9017 * of the whole table, which is surely not what the user expected.)
9019 if (has_partition_attrs(rel,
9020 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
9021 &is_expr))
9022 ereport(ERROR,
9023 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9024 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9025 colName, RelationGetRelationName(rel))));
9027 ReleaseSysCache(tuple);
9030 * Propagate to children as appropriate. Unlike most other ALTER
9031 * routines, we have to do this one level of recursion at a time; we can't
9032 * use find_all_inheritors to do it in one pass.
9034 children =
9035 find_inheritance_children(RelationGetRelid(rel), lockmode);
9037 if (children)
9039 Relation attr_rel;
9040 ListCell *child;
9043 * In case of a partitioned table, the column must be dropped from the
9044 * partitions as well.
9046 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9047 ereport(ERROR,
9048 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9049 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9050 errhint("Do not specify the ONLY keyword.")));
9052 attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9053 foreach(child, children)
9055 Oid childrelid = lfirst_oid(child);
9056 Relation childrel;
9057 Form_pg_attribute childatt;
9059 /* find_inheritance_children already got lock */
9060 childrel = table_open(childrelid, NoLock);
9061 CheckAlterTableIsSafe(childrel);
9063 tuple = SearchSysCacheCopyAttName(childrelid, colName);
9064 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9065 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9066 colName, childrelid);
9067 childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9069 if (childatt->attinhcount <= 0) /* shouldn't happen */
9070 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9071 childrelid, colName);
9073 if (recurse)
9076 * If the child column has other definition sources, just
9077 * decrement its inheritance count; if not, recurse to delete
9078 * it.
9080 if (childatt->attinhcount == 1 && !childatt->attislocal)
9082 /* Time to delete this child column, too */
9083 ATExecDropColumn(wqueue, childrel, colName,
9084 behavior, true, true,
9085 false, lockmode, addrs);
9087 else
9089 /* Child column must survive my deletion */
9090 childatt->attinhcount--;
9092 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9094 /* Make update visible */
9095 CommandCounterIncrement();
9098 else
9101 * If we were told to drop ONLY in this table (no recursion),
9102 * we need to mark the inheritors' attributes as locally
9103 * defined rather than inherited.
9105 childatt->attinhcount--;
9106 childatt->attislocal = true;
9108 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9110 /* Make update visible */
9111 CommandCounterIncrement();
9114 heap_freetuple(tuple);
9116 table_close(childrel, NoLock);
9118 table_close(attr_rel, RowExclusiveLock);
9121 /* Add object to delete */
9122 object.classId = RelationRelationId;
9123 object.objectId = RelationGetRelid(rel);
9124 object.objectSubId = attnum;
9125 add_exact_object_address(&object, addrs);
9127 if (!recursing)
9129 /* Recursion has ended, drop everything that was collected */
9130 performMultipleDeletions(addrs, behavior, 0);
9131 free_object_addresses(addrs);
9134 return object;
9138 * ALTER TABLE ADD INDEX
9140 * There is no such command in the grammar, but parse_utilcmd.c converts
9141 * UNIQUE and PRIMARY KEY constraints into AT_AddIndex subcommands. This lets
9142 * us schedule creation of the index at the appropriate time during ALTER.
9144 * Return value is the address of the new index.
9146 static ObjectAddress
9147 ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
9148 IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9150 bool check_rights;
9151 bool skip_build;
9152 bool quiet;
9153 ObjectAddress address;
9155 Assert(IsA(stmt, IndexStmt));
9156 Assert(!stmt->concurrent);
9158 /* The IndexStmt has already been through transformIndexStmt */
9159 Assert(stmt->transformed);
9161 /* suppress schema rights check when rebuilding existing index */
9162 check_rights = !is_rebuild;
9163 /* skip index build if phase 3 will do it or we're reusing an old one */
9164 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9165 /* suppress notices when rebuilding existing index */
9166 quiet = is_rebuild;
9168 address = DefineIndex(RelationGetRelid(rel),
9169 stmt,
9170 InvalidOid, /* no predefined OID */
9171 InvalidOid, /* no parent index */
9172 InvalidOid, /* no parent constraint */
9173 -1, /* total_parts unknown */
9174 true, /* is_alter_table */
9175 check_rights,
9176 false, /* check_not_in_use - we did it already */
9177 skip_build,
9178 quiet);
9181 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9182 * new index instead of building from scratch. Restore associated fields.
9183 * This may store InvalidSubTransactionId in both fields, in which case
9184 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9185 * this after the CCI that made catalog rows visible to any rebuild. The
9186 * DROP of the old edition of this index will have scheduled the storage
9187 * for deletion at commit, so cancel that pending deletion.
9189 if (RelFileNumberIsValid(stmt->oldNumber))
9191 Relation irel = index_open(address.objectId, NoLock);
9193 irel->rd_createSubid = stmt->oldCreateSubid;
9194 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9195 RelationPreserveStorage(irel->rd_locator, true);
9196 index_close(irel, NoLock);
9199 return address;
9203 * ALTER TABLE ADD STATISTICS
9205 * This is no such command in the grammar, but we use this internally to add
9206 * AT_ReAddStatistics subcommands to rebuild extended statistics after a table
9207 * column type change.
9209 static ObjectAddress
9210 ATExecAddStatistics(AlteredTableInfo *tab, Relation rel,
9211 CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
9213 ObjectAddress address;
9215 Assert(IsA(stmt, CreateStatsStmt));
9217 /* The CreateStatsStmt has already been through transformStatsStmt */
9218 Assert(stmt->transformed);
9220 address = CreateStatistics(stmt);
9222 return address;
9226 * ALTER TABLE ADD CONSTRAINT USING INDEX
9228 * Returns the address of the new constraint.
9230 static ObjectAddress
9231 ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel,
9232 IndexStmt *stmt, LOCKMODE lockmode)
9234 Oid index_oid = stmt->indexOid;
9235 Relation indexRel;
9236 char *indexName;
9237 IndexInfo *indexInfo;
9238 char *constraintName;
9239 char constraintType;
9240 ObjectAddress address;
9241 bits16 flags;
9243 Assert(IsA(stmt, IndexStmt));
9244 Assert(OidIsValid(index_oid));
9245 Assert(stmt->isconstraint);
9248 * Doing this on partitioned tables is not a simple feature to implement,
9249 * so let's punt for now.
9251 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9252 ereport(ERROR,
9253 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9254 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9256 indexRel = index_open(index_oid, AccessShareLock);
9258 indexName = pstrdup(RelationGetRelationName(indexRel));
9260 indexInfo = BuildIndexInfo(indexRel);
9262 /* this should have been checked at parse time */
9263 if (!indexInfo->ii_Unique)
9264 elog(ERROR, "index \"%s\" is not unique", indexName);
9267 * Determine name to assign to constraint. We require a constraint to
9268 * have the same name as the underlying index; therefore, use the index's
9269 * existing name as the default constraint name, and if the user
9270 * explicitly gives some other name for the constraint, rename the index
9271 * to match.
9273 constraintName = stmt->idxname;
9274 if (constraintName == NULL)
9275 constraintName = indexName;
9276 else if (strcmp(constraintName, indexName) != 0)
9278 ereport(NOTICE,
9279 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9280 indexName, constraintName)));
9281 RenameRelationInternal(index_oid, constraintName, false, true);
9284 /* Extra checks needed if making primary key */
9285 if (stmt->primary)
9286 index_check_primary_key(rel, indexInfo, true, stmt);
9288 /* Note we currently don't support EXCLUSION constraints here */
9289 if (stmt->primary)
9290 constraintType = CONSTRAINT_PRIMARY;
9291 else
9292 constraintType = CONSTRAINT_UNIQUE;
9294 /* Create the catalog entries for the constraint */
9295 flags = INDEX_CONSTR_CREATE_UPDATE_INDEX |
9296 INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS |
9297 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9298 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9299 (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9301 address = index_constraint_create(rel,
9302 index_oid,
9303 InvalidOid,
9304 indexInfo,
9305 constraintName,
9306 constraintType,
9307 flags,
9308 allowSystemTableMods,
9309 false); /* is_internal */
9311 index_close(indexRel, NoLock);
9313 return address;
9317 * ALTER TABLE ADD CONSTRAINT
9319 * Return value is the address of the new constraint; if no constraint was
9320 * added, InvalidObjectAddress is returned.
9322 static ObjectAddress
9323 ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9324 Constraint *newConstraint, bool recurse, bool is_readd,
9325 LOCKMODE lockmode)
9327 ObjectAddress address = InvalidObjectAddress;
9329 Assert(IsA(newConstraint, Constraint));
9332 * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
9333 * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
9334 * switch anyway to make it easier to add more code later.
9336 switch (newConstraint->contype)
9338 case CONSTR_CHECK:
9339 address =
9340 ATAddCheckConstraint(wqueue, tab, rel,
9341 newConstraint, recurse, false, is_readd,
9342 lockmode);
9343 break;
9345 case CONSTR_FOREIGN:
9348 * Assign or validate constraint name
9350 if (newConstraint->conname)
9352 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
9353 RelationGetRelid(rel),
9354 newConstraint->conname))
9355 ereport(ERROR,
9356 (errcode(ERRCODE_DUPLICATE_OBJECT),
9357 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9358 newConstraint->conname,
9359 RelationGetRelationName(rel))));
9361 else
9362 newConstraint->conname =
9363 ChooseConstraintName(RelationGetRelationName(rel),
9364 ChooseForeignKeyConstraintNameAddition(newConstraint->fk_attrs),
9365 "fkey",
9366 RelationGetNamespace(rel),
9367 NIL);
9369 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9370 newConstraint,
9371 recurse, false,
9372 lockmode);
9373 break;
9375 default:
9376 elog(ERROR, "unrecognized constraint type: %d",
9377 (int) newConstraint->contype);
9380 return address;
9384 * Generate the column-name portion of the constraint name for a new foreign
9385 * key given the list of column names that reference the referenced
9386 * table. This will be passed to ChooseConstraintName along with the parent
9387 * table name and the "fkey" suffix.
9389 * We know that less than NAMEDATALEN characters will actually be used, so we
9390 * can truncate the result once we've generated that many.
9392 * XXX see also ChooseExtendedStatisticNameAddition and
9393 * ChooseIndexNameAddition.
9395 static char *
9396 ChooseForeignKeyConstraintNameAddition(List *colnames)
9398 char buf[NAMEDATALEN * 2];
9399 int buflen = 0;
9400 ListCell *lc;
9402 buf[0] = '\0';
9403 foreach(lc, colnames)
9405 const char *name = strVal(lfirst(lc));
9407 if (buflen > 0)
9408 buf[buflen++] = '_'; /* insert _ between names */
9411 * At this point we have buflen <= NAMEDATALEN. name should be less
9412 * than NAMEDATALEN already, but use strlcpy for paranoia.
9414 strlcpy(buf + buflen, name, NAMEDATALEN);
9415 buflen += strlen(buf + buflen);
9416 if (buflen >= NAMEDATALEN)
9417 break;
9419 return pstrdup(buf);
9423 * Add a check constraint to a single table and its children. Returns the
9424 * address of the constraint added to the parent relation, if one gets added,
9425 * or InvalidObjectAddress otherwise.
9427 * Subroutine for ATExecAddConstraint.
9429 * We must recurse to child tables during execution, rather than using
9430 * ALTER TABLE's normal prep-time recursion. The reason is that all the
9431 * constraints *must* be given the same name, else they won't be seen as
9432 * related later. If the user didn't explicitly specify a name, then
9433 * AddRelationNewConstraints would normally assign different names to the
9434 * child constraints. To fix that, we must capture the name assigned at
9435 * the parent table and pass that down.
9437 static ObjectAddress
9438 ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9439 Constraint *constr, bool recurse, bool recursing,
9440 bool is_readd, LOCKMODE lockmode)
9442 List *newcons;
9443 ListCell *lcon;
9444 List *children;
9445 ListCell *child;
9446 ObjectAddress address = InvalidObjectAddress;
9448 /* At top level, permission check was done in ATPrepCmd, else do it */
9449 if (recursing)
9450 ATSimplePermissions(AT_AddConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
9453 * Call AddRelationNewConstraints to do the work, making sure it works on
9454 * a copy of the Constraint so transformExpr can't modify the original. It
9455 * returns a list of cooked constraints.
9457 * If the constraint ends up getting merged with a pre-existing one, it's
9458 * omitted from the returned list, which is what we want: we do not need
9459 * to do any validation work. That can only happen at child tables,
9460 * though, since we disallow merging at the top level.
9462 newcons = AddRelationNewConstraints(rel, NIL,
9463 list_make1(copyObject(constr)),
9464 recursing || is_readd, /* allow_merge */
9465 !recursing, /* is_local */
9466 is_readd, /* is_internal */
9467 NULL); /* queryString not available
9468 * here */
9470 /* we don't expect more than one constraint here */
9471 Assert(list_length(newcons) <= 1);
9473 /* Add each to-be-validated constraint to Phase 3's queue */
9474 foreach(lcon, newcons)
9476 CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9478 if (!ccon->skip_validation)
9480 NewConstraint *newcon;
9482 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9483 newcon->name = ccon->name;
9484 newcon->contype = ccon->contype;
9485 newcon->qual = ccon->expr;
9487 tab->constraints = lappend(tab->constraints, newcon);
9490 /* Save the actually assigned name if it was defaulted */
9491 if (constr->conname == NULL)
9492 constr->conname = ccon->name;
9494 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9497 /* At this point we must have a locked-down name to use */
9498 Assert(constr->conname != NULL);
9500 /* Advance command counter in case same table is visited multiple times */
9501 CommandCounterIncrement();
9504 * If the constraint got merged with an existing constraint, we're done.
9505 * We mustn't recurse to child tables in this case, because they've
9506 * already got the constraint, and visiting them again would lead to an
9507 * incorrect value for coninhcount.
9509 if (newcons == NIL)
9510 return address;
9513 * If adding a NO INHERIT constraint, no need to find our children.
9515 if (constr->is_no_inherit)
9516 return address;
9519 * Propagate to children as appropriate. Unlike most other ALTER
9520 * routines, we have to do this one level of recursion at a time; we can't
9521 * use find_all_inheritors to do it in one pass.
9523 children =
9524 find_inheritance_children(RelationGetRelid(rel), lockmode);
9527 * Check if ONLY was specified with ALTER TABLE. If so, allow the
9528 * constraint creation only if there are no children currently. Error out
9529 * otherwise.
9531 if (!recurse && children != NIL)
9532 ereport(ERROR,
9533 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9534 errmsg("constraint must be added to child tables too")));
9536 foreach(child, children)
9538 Oid childrelid = lfirst_oid(child);
9539 Relation childrel;
9540 AlteredTableInfo *childtab;
9542 /* find_inheritance_children already got lock */
9543 childrel = table_open(childrelid, NoLock);
9544 CheckAlterTableIsSafe(childrel);
9546 /* Find or create work queue entry for this table */
9547 childtab = ATGetQueueEntry(wqueue, childrel);
9549 /* Recurse to child */
9550 ATAddCheckConstraint(wqueue, childtab, childrel,
9551 constr, recurse, true, is_readd, lockmode);
9553 table_close(childrel, NoLock);
9556 return address;
9560 * Add a foreign-key constraint to a single table; return the new constraint's
9561 * address.
9563 * Subroutine for ATExecAddConstraint. Must already hold exclusive
9564 * lock on the rel, and have done appropriate validity checks for it.
9565 * We do permissions checks here, however.
9567 * When the referenced or referencing tables (or both) are partitioned,
9568 * multiple pg_constraint rows are required -- one for each partitioned table
9569 * and each partition on each side (fortunately, not one for every combination
9570 * thereof). We also need action triggers on each leaf partition on the
9571 * referenced side, and check triggers on each leaf partition on the
9572 * referencing side.
9574 static ObjectAddress
9575 ATAddForeignKeyConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
9576 Constraint *fkconstraint,
9577 bool recurse, bool recursing, LOCKMODE lockmode)
9579 Relation pkrel;
9580 int16 pkattnum[INDEX_MAX_KEYS] = {0};
9581 int16 fkattnum[INDEX_MAX_KEYS] = {0};
9582 Oid pktypoid[INDEX_MAX_KEYS] = {0};
9583 Oid fktypoid[INDEX_MAX_KEYS] = {0};
9584 Oid opclasses[INDEX_MAX_KEYS] = {0};
9585 Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9586 Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9587 Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9588 int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9589 int i;
9590 int numfks,
9591 numpks,
9592 numfkdelsetcols;
9593 Oid indexOid;
9594 bool old_check_ok;
9595 ObjectAddress address;
9596 ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9599 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9600 * delete rows out from under us.
9602 if (OidIsValid(fkconstraint->old_pktable_oid))
9603 pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9604 else
9605 pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9608 * Validity checks (permission checks wait till we have the column
9609 * numbers)
9611 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9613 if (!recurse)
9614 ereport(ERROR,
9615 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9616 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9617 RelationGetRelationName(rel),
9618 RelationGetRelationName(pkrel))));
9619 if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9620 ereport(ERROR,
9621 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9622 errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9623 RelationGetRelationName(rel),
9624 RelationGetRelationName(pkrel)),
9625 errdetail("This feature is not yet supported on partitioned tables.")));
9628 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9629 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9630 ereport(ERROR,
9631 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9632 errmsg("referenced relation \"%s\" is not a table",
9633 RelationGetRelationName(pkrel))));
9635 if (!allowSystemTableMods && IsSystemRelation(pkrel))
9636 ereport(ERROR,
9637 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9638 errmsg("permission denied: \"%s\" is a system catalog",
9639 RelationGetRelationName(pkrel))));
9642 * References from permanent or unlogged tables to temp tables, and from
9643 * permanent tables to unlogged tables, are disallowed because the
9644 * referenced data can vanish out from under us. References from temp
9645 * tables to any other table type are also disallowed, because other
9646 * backends might need to run the RI triggers on the perm table, but they
9647 * can't reliably see tuples in the local buffers of other backends.
9649 switch (rel->rd_rel->relpersistence)
9651 case RELPERSISTENCE_PERMANENT:
9652 if (!RelationIsPermanent(pkrel))
9653 ereport(ERROR,
9654 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9655 errmsg("constraints on permanent tables may reference only permanent tables")));
9656 break;
9657 case RELPERSISTENCE_UNLOGGED:
9658 if (!RelationIsPermanent(pkrel)
9659 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9660 ereport(ERROR,
9661 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9662 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9663 break;
9664 case RELPERSISTENCE_TEMP:
9665 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9666 ereport(ERROR,
9667 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9668 errmsg("constraints on temporary tables may reference only temporary tables")));
9669 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9670 ereport(ERROR,
9671 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9672 errmsg("constraints on temporary tables must involve temporary tables of this session")));
9673 break;
9677 * Look up the referencing attributes to make sure they exist, and record
9678 * their attnums and type OIDs.
9680 numfks = transformColumnNameList(RelationGetRelid(rel),
9681 fkconstraint->fk_attrs,
9682 fkattnum, fktypoid);
9684 numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9685 fkconstraint->fk_del_set_cols,
9686 fkdelsetcols, NULL);
9687 validateFkOnDeleteSetColumns(numfks, fkattnum,
9688 numfkdelsetcols, fkdelsetcols,
9689 fkconstraint->fk_del_set_cols);
9692 * If the attribute list for the referenced table was omitted, lookup the
9693 * definition of the primary key and use it. Otherwise, validate the
9694 * supplied attribute list. In either case, discover the index OID and
9695 * index opclasses, and the attnums and type OIDs of the attributes.
9697 if (fkconstraint->pk_attrs == NIL)
9699 numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9700 &fkconstraint->pk_attrs,
9701 pkattnum, pktypoid,
9702 opclasses);
9704 else
9706 numpks = transformColumnNameList(RelationGetRelid(pkrel),
9707 fkconstraint->pk_attrs,
9708 pkattnum, pktypoid);
9709 /* Look for an index matching the column list */
9710 indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9711 opclasses);
9715 * Now we can check permissions.
9717 checkFkeyPermissions(pkrel, pkattnum, numpks);
9720 * Check some things for generated columns.
9722 for (i = 0; i < numfks; i++)
9724 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9726 if (attgenerated)
9729 * Check restrictions on UPDATE/DELETE actions, per SQL standard
9731 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9732 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9733 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9734 ereport(ERROR,
9735 (errcode(ERRCODE_SYNTAX_ERROR),
9736 errmsg("invalid %s action for foreign key constraint containing generated column",
9737 "ON UPDATE")));
9738 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9739 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9740 ereport(ERROR,
9741 (errcode(ERRCODE_SYNTAX_ERROR),
9742 errmsg("invalid %s action for foreign key constraint containing generated column",
9743 "ON DELETE")));
9748 * Look up the equality operators to use in the constraint.
9750 * Note that we have to be careful about the difference between the actual
9751 * PK column type and the opclass' declared input type, which might be
9752 * only binary-compatible with it. The declared opcintype is the right
9753 * thing to probe pg_amop with.
9755 if (numfks != numpks)
9756 ereport(ERROR,
9757 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9758 errmsg("number of referencing and referenced columns for foreign key disagree")));
9761 * On the strength of a previous constraint, we might avoid scanning
9762 * tables to validate this one. See below.
9764 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9765 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9767 for (i = 0; i < numpks; i++)
9769 Oid pktype = pktypoid[i];
9770 Oid fktype = fktypoid[i];
9771 Oid fktyped;
9772 HeapTuple cla_ht;
9773 Form_pg_opclass cla_tup;
9774 Oid amid;
9775 Oid opfamily;
9776 Oid opcintype;
9777 Oid pfeqop;
9778 Oid ppeqop;
9779 Oid ffeqop;
9780 int16 eqstrategy;
9781 Oid pfeqop_right;
9783 /* We need several fields out of the pg_opclass entry */
9784 cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9785 if (!HeapTupleIsValid(cla_ht))
9786 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9787 cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9788 amid = cla_tup->opcmethod;
9789 opfamily = cla_tup->opcfamily;
9790 opcintype = cla_tup->opcintype;
9791 ReleaseSysCache(cla_ht);
9794 * Check it's a btree; currently this can never fail since no other
9795 * index AMs support unique indexes. If we ever did have other types
9796 * of unique indexes, we'd need a way to determine which operator
9797 * strategy number is equality. (Is it reasonable to insist that
9798 * every such index AM use btree's number for equality?)
9800 if (amid != BTREE_AM_OID)
9801 elog(ERROR, "only b-tree indexes are supported for foreign keys");
9802 eqstrategy = BTEqualStrategyNumber;
9805 * There had better be a primary equality operator for the index.
9806 * We'll use it for PK = PK comparisons.
9808 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9809 eqstrategy);
9811 if (!OidIsValid(ppeqop))
9812 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9813 eqstrategy, opcintype, opcintype, opfamily);
9816 * Are there equality operators that take exactly the FK type? Assume
9817 * we should look through any domain here.
9819 fktyped = getBaseType(fktype);
9821 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9822 eqstrategy);
9823 if (OidIsValid(pfeqop))
9825 pfeqop_right = fktyped;
9826 ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9827 eqstrategy);
9829 else
9831 /* keep compiler quiet */
9832 pfeqop_right = InvalidOid;
9833 ffeqop = InvalidOid;
9836 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9839 * Otherwise, look for an implicit cast from the FK type to the
9840 * opcintype, and if found, use the primary equality operator.
9841 * This is a bit tricky because opcintype might be a polymorphic
9842 * type such as ANYARRAY or ANYENUM; so what we have to test is
9843 * whether the two actual column types can be concurrently cast to
9844 * that type. (Otherwise, we'd fail to reject combinations such
9845 * as int[] and point[].)
9847 Oid input_typeids[2];
9848 Oid target_typeids[2];
9850 input_typeids[0] = pktype;
9851 input_typeids[1] = fktype;
9852 target_typeids[0] = opcintype;
9853 target_typeids[1] = opcintype;
9854 if (can_coerce_type(2, input_typeids, target_typeids,
9855 COERCION_IMPLICIT))
9857 pfeqop = ffeqop = ppeqop;
9858 pfeqop_right = opcintype;
9862 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9863 ereport(ERROR,
9864 (errcode(ERRCODE_DATATYPE_MISMATCH),
9865 errmsg("foreign key constraint \"%s\" cannot be implemented",
9866 fkconstraint->conname),
9867 errdetail("Key columns \"%s\" and \"%s\" "
9868 "are of incompatible types: %s and %s.",
9869 strVal(list_nth(fkconstraint->fk_attrs, i)),
9870 strVal(list_nth(fkconstraint->pk_attrs, i)),
9871 format_type_be(fktype),
9872 format_type_be(pktype))));
9874 if (old_check_ok)
9877 * When a pfeqop changes, revalidate the constraint. We could
9878 * permit intra-opfamily changes, but that adds subtle complexity
9879 * without any concrete benefit for core types. We need not
9880 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9882 old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9883 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9884 old_pfeqop_item);
9886 if (old_check_ok)
9888 Oid old_fktype;
9889 Oid new_fktype;
9890 CoercionPathType old_pathtype;
9891 CoercionPathType new_pathtype;
9892 Oid old_castfunc;
9893 Oid new_castfunc;
9894 Form_pg_attribute attr = TupleDescAttr(tab->oldDesc,
9895 fkattnum[i] - 1);
9898 * Identify coercion pathways from each of the old and new FK-side
9899 * column types to the right (foreign) operand type of the pfeqop.
9900 * We may assume that pg_constraint.conkey is not changing.
9902 old_fktype = attr->atttypid;
9903 new_fktype = fktype;
9904 old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9905 &old_castfunc);
9906 new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9907 &new_castfunc);
9910 * Upon a change to the cast from the FK column to its pfeqop
9911 * operand, revalidate the constraint. For this evaluation, a
9912 * binary coercion cast is equivalent to no cast at all. While
9913 * type implementors should design implicit casts with an eye
9914 * toward consistency of operations like equality, we cannot
9915 * assume here that they have done so.
9917 * A function with a polymorphic argument could change behavior
9918 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9919 * when the cast destination is polymorphic, we only avoid
9920 * revalidation if the input type has not changed at all. Given
9921 * just the core data types and operator classes, this requirement
9922 * prevents no would-be optimizations.
9924 * If the cast converts from a base type to a domain thereon, then
9925 * that domain type must be the opcintype of the unique index.
9926 * Necessarily, the primary key column must then be of the domain
9927 * type. Since the constraint was previously valid, all values on
9928 * the foreign side necessarily exist on the primary side and in
9929 * turn conform to the domain. Consequently, we need not treat
9930 * domains specially here.
9932 * Since we require that all collations share the same notion of
9933 * equality (which they do, because texteq reduces to bitwise
9934 * equality), we don't compare collation here.
9936 * We need not directly consider the PK type. It's necessarily
9937 * binary coercible to the opcintype of the unique index column,
9938 * and ri_triggers.c will only deal with PK datums in terms of
9939 * that opcintype. Changing the opcintype also changes pfeqop.
9941 old_check_ok = (new_pathtype == old_pathtype &&
9942 new_castfunc == old_castfunc &&
9943 (!IsPolymorphicType(pfeqop_right) ||
9944 new_fktype == old_fktype));
9947 pfeqoperators[i] = pfeqop;
9948 ppeqoperators[i] = ppeqop;
9949 ffeqoperators[i] = ffeqop;
9953 * Create all the constraint and trigger objects, recursing to partitions
9954 * as necessary. First handle the referenced side.
9956 address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9957 indexOid,
9958 InvalidOid, /* no parent constraint */
9959 numfks,
9960 pkattnum,
9961 fkattnum,
9962 pfeqoperators,
9963 ppeqoperators,
9964 ffeqoperators,
9965 numfkdelsetcols,
9966 fkdelsetcols,
9967 old_check_ok,
9968 InvalidOid, InvalidOid);
9970 /* Now handle the referencing side. */
9971 addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9972 indexOid,
9973 address.objectId,
9974 numfks,
9975 pkattnum,
9976 fkattnum,
9977 pfeqoperators,
9978 ppeqoperators,
9979 ffeqoperators,
9980 numfkdelsetcols,
9981 fkdelsetcols,
9982 old_check_ok,
9983 lockmode,
9984 InvalidOid, InvalidOid);
9987 * Done. Close pk table, but keep lock until we've committed.
9989 table_close(pkrel, NoLock);
9991 return address;
9995 * validateFkOnDeleteSetColumns
9996 * Verifies that columns used in ON DELETE SET NULL/DEFAULT (...)
9997 * column lists are valid.
9999 void
10000 validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums,
10001 int numfksetcols, const int16 *fksetcolsattnums,
10002 List *fksetcols)
10004 for (int i = 0; i < numfksetcols; i++)
10006 int16 setcol_attnum = fksetcolsattnums[i];
10007 bool seen = false;
10009 for (int j = 0; j < numfks; j++)
10011 if (fkattnums[j] == setcol_attnum)
10013 seen = true;
10014 break;
10018 if (!seen)
10020 char *col = strVal(list_nth(fksetcols, i));
10022 ereport(ERROR,
10023 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10024 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10030 * addFkRecurseReferenced
10031 * subroutine for ATAddForeignKeyConstraint; recurses on the referenced
10032 * side of the constraint
10034 * Create pg_constraint rows for the referenced side of the constraint,
10035 * referencing the parent of the referencing side; also create action triggers
10036 * on leaf partitions. If the table is partitioned, recurse to handle each
10037 * partition.
10039 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10040 * of an ALTER TABLE sequence.
10041 * fkconstraint is the constraint being added.
10042 * rel is the root referencing relation.
10043 * pkrel is the referenced relation; might be a partition, if recursing.
10044 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10045 * parentConstr is the OID of a parent constraint; InvalidOid if this is a
10046 * top-level constraint.
10047 * numfks is the number of columns in the foreign key
10048 * pkattnum is the attnum array of referenced attributes.
10049 * fkattnum is the attnum array of referencing attributes.
10050 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10051 * (...) clause
10052 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10053 * NULL/DEFAULT clause
10054 * pf/pp/ffeqoperators are OID array of operators between columns.
10055 * old_check_ok signals that this constraint replaces an existing one that
10056 * was already validated (thus this one doesn't need validation).
10057 * parentDelTrigger and parentUpdTrigger, when being recursively called on
10058 * a partition, are the OIDs of the parent action triggers for DELETE and
10059 * UPDATE respectively.
10061 static ObjectAddress
10062 addFkRecurseReferenced(List **wqueue, Constraint *fkconstraint, Relation rel,
10063 Relation pkrel, Oid indexOid, Oid parentConstr,
10064 int numfks,
10065 int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators,
10066 Oid *ppeqoperators, Oid *ffeqoperators,
10067 int numfkdelsetcols, int16 *fkdelsetcols,
10068 bool old_check_ok,
10069 Oid parentDelTrigger, Oid parentUpdTrigger)
10071 ObjectAddress address;
10072 Oid constrOid;
10073 char *conname;
10074 bool conislocal;
10075 int coninhcount;
10076 bool connoinherit;
10077 Oid deleteTriggerOid,
10078 updateTriggerOid;
10081 * Verify relkind for each referenced partition. At the top level, this
10082 * is redundant with a previous check, but we need it when recursing.
10084 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10085 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10086 ereport(ERROR,
10087 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10088 errmsg("referenced relation \"%s\" is not a table",
10089 RelationGetRelationName(pkrel))));
10092 * Caller supplies us with a constraint name; however, it may be used in
10093 * this partition, so come up with a different one in that case.
10095 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10096 RelationGetRelid(rel),
10097 fkconstraint->conname))
10098 conname = ChooseConstraintName(RelationGetRelationName(rel),
10099 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10100 "fkey",
10101 RelationGetNamespace(rel), NIL);
10102 else
10103 conname = fkconstraint->conname;
10105 if (OidIsValid(parentConstr))
10107 conislocal = false;
10108 coninhcount = 1;
10109 connoinherit = false;
10111 else
10113 conislocal = true;
10114 coninhcount = 0;
10117 * always inherit for partitioned tables, never for legacy inheritance
10119 connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10123 * Record the FK constraint in pg_constraint.
10125 constrOid = CreateConstraintEntry(conname,
10126 RelationGetNamespace(rel),
10127 CONSTRAINT_FOREIGN,
10128 fkconstraint->deferrable,
10129 fkconstraint->initdeferred,
10130 fkconstraint->initially_valid,
10131 parentConstr,
10132 RelationGetRelid(rel),
10133 fkattnum,
10134 numfks,
10135 numfks,
10136 InvalidOid, /* not a domain constraint */
10137 indexOid,
10138 RelationGetRelid(pkrel),
10139 pkattnum,
10140 pfeqoperators,
10141 ppeqoperators,
10142 ffeqoperators,
10143 numfks,
10144 fkconstraint->fk_upd_action,
10145 fkconstraint->fk_del_action,
10146 fkdelsetcols,
10147 numfkdelsetcols,
10148 fkconstraint->fk_matchtype,
10149 NULL, /* no exclusion constraint */
10150 NULL, /* no check constraint */
10151 NULL,
10152 conislocal, /* islocal */
10153 coninhcount, /* inhcount */
10154 connoinherit, /* conNoInherit */
10155 false); /* is_internal */
10157 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10160 * Mark the child constraint as part of the parent constraint; it must not
10161 * be dropped on its own. (This constraint is deleted when the partition
10162 * is detached, but a special check needs to occur that the partition
10163 * contains no referenced values.)
10165 if (OidIsValid(parentConstr))
10167 ObjectAddress referenced;
10169 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10170 recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10173 /* make new constraint visible, in case we add more */
10174 CommandCounterIncrement();
10177 * Create the action triggers that enforce the constraint.
10179 createForeignKeyActionTriggers(rel, RelationGetRelid(pkrel),
10180 fkconstraint,
10181 constrOid, indexOid,
10182 parentDelTrigger, parentUpdTrigger,
10183 &deleteTriggerOid, &updateTriggerOid);
10186 * If the referenced table is partitioned, recurse on ourselves to handle
10187 * each partition. We need one pg_constraint row created for each
10188 * partition in addition to the pg_constraint row for the parent table.
10190 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10192 PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10194 for (int i = 0; i < pd->nparts; i++)
10196 Relation partRel;
10197 AttrMap *map;
10198 AttrNumber *mapped_pkattnum;
10199 Oid partIndexId;
10201 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10204 * Map the attribute numbers in the referenced side of the FK
10205 * definition to match the partition's column layout.
10207 map = build_attrmap_by_name_if_req(RelationGetDescr(partRel),
10208 RelationGetDescr(pkrel),
10209 false);
10210 if (map)
10212 mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10213 for (int j = 0; j < numfks; j++)
10214 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10216 else
10217 mapped_pkattnum = pkattnum;
10219 /* do the deed */
10220 partIndexId = index_get_partition(partRel, indexOid);
10221 if (!OidIsValid(partIndexId))
10222 elog(ERROR, "index for %u not found in partition %s",
10223 indexOid, RelationGetRelationName(partRel));
10224 addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10225 partIndexId, constrOid, numfks,
10226 mapped_pkattnum, fkattnum,
10227 pfeqoperators, ppeqoperators, ffeqoperators,
10228 numfkdelsetcols, fkdelsetcols,
10229 old_check_ok,
10230 deleteTriggerOid, updateTriggerOid);
10232 /* Done -- clean up (but keep the lock) */
10233 table_close(partRel, NoLock);
10234 if (map)
10236 pfree(mapped_pkattnum);
10237 free_attrmap(map);
10242 return address;
10246 * addFkRecurseReferencing
10247 * subroutine for ATAddForeignKeyConstraint and CloneFkReferencing
10249 * If the referencing relation is a plain relation, create the necessary check
10250 * triggers that implement the constraint, and set up for Phase 3 constraint
10251 * verification. If the referencing relation is a partitioned table, then
10252 * we create a pg_constraint row for it and recurse on this routine for each
10253 * partition.
10255 * We assume that the referenced relation is locked against concurrent
10256 * deletions. If it's a partitioned relation, every partition must be so
10257 * locked.
10259 * wqueue is the ALTER TABLE work queue; can be NULL when not running as part
10260 * of an ALTER TABLE sequence.
10261 * fkconstraint is the constraint being added.
10262 * rel is the referencing relation; might be a partition, if recursing.
10263 * pkrel is the root referenced relation.
10264 * indexOid is the OID of the index (on pkrel) implementing this constraint.
10265 * parentConstr is the OID of the parent constraint (there is always one).
10266 * numfks is the number of columns in the foreign key
10267 * pkattnum is the attnum array of referenced attributes.
10268 * fkattnum is the attnum array of referencing attributes.
10269 * pf/pp/ffeqoperators are OID array of operators between columns.
10270 * numfkdelsetcols is the number of columns in the ON DELETE SET NULL/DEFAULT
10271 * (...) clause
10272 * fkdelsetcols is the attnum array of the columns in the ON DELETE SET
10273 * NULL/DEFAULT clause
10274 * old_check_ok signals that this constraint replaces an existing one that
10275 * was already validated (thus this one doesn't need validation).
10276 * lockmode is the lockmode to acquire on partitions when recursing.
10277 * parentInsTrigger and parentUpdTrigger, when being recursively called on
10278 * a partition, are the OIDs of the parent check triggers for INSERT and
10279 * UPDATE respectively.
10281 static void
10282 addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel,
10283 Relation pkrel, Oid indexOid, Oid parentConstr,
10284 int numfks, int16 *pkattnum, int16 *fkattnum,
10285 Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators,
10286 int numfkdelsetcols, int16 *fkdelsetcols,
10287 bool old_check_ok, LOCKMODE lockmode,
10288 Oid parentInsTrigger, Oid parentUpdTrigger)
10290 Oid insertTriggerOid,
10291 updateTriggerOid;
10293 Assert(OidIsValid(parentConstr));
10295 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10296 ereport(ERROR,
10297 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10298 errmsg("foreign key constraints are not supported on foreign tables")));
10301 * Add the check triggers to it and, if necessary, schedule it to be
10302 * checked in Phase 3.
10304 * If the relation is partitioned, drill down to do it to its partitions.
10306 createForeignKeyCheckTriggers(RelationGetRelid(rel),
10307 RelationGetRelid(pkrel),
10308 fkconstraint,
10309 parentConstr,
10310 indexOid,
10311 parentInsTrigger, parentUpdTrigger,
10312 &insertTriggerOid, &updateTriggerOid);
10314 if (rel->rd_rel->relkind == RELKIND_RELATION)
10317 * Tell Phase 3 to check that the constraint is satisfied by existing
10318 * rows. We can skip this during table creation, when requested
10319 * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10320 * and when we're recreating a constraint following a SET DATA TYPE
10321 * operation that did not impugn its validity.
10323 if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10325 NewConstraint *newcon;
10326 AlteredTableInfo *tab;
10328 tab = ATGetQueueEntry(wqueue, rel);
10330 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10331 newcon->name = get_constraint_name(parentConstr);
10332 newcon->contype = CONSTR_FOREIGN;
10333 newcon->refrelid = RelationGetRelid(pkrel);
10334 newcon->refindid = indexOid;
10335 newcon->conid = parentConstr;
10336 newcon->qual = (Node *) fkconstraint;
10338 tab->constraints = lappend(tab->constraints, newcon);
10341 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10343 PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10344 Relation trigrel;
10347 * Triggers of the foreign keys will be manipulated a bunch of times
10348 * in the loop below. To avoid repeatedly opening/closing the trigger
10349 * catalog relation, we open it here and pass it to the subroutines
10350 * called below.
10352 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10355 * Recurse to take appropriate action on each partition; either we
10356 * find an existing constraint to reparent to ours, or we create a new
10357 * one.
10359 for (int i = 0; i < pd->nparts; i++)
10361 Oid partitionId = pd->oids[i];
10362 Relation partition = table_open(partitionId, lockmode);
10363 List *partFKs;
10364 AttrMap *attmap;
10365 AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10366 bool attached;
10367 char *conname;
10368 Oid constrOid;
10369 ObjectAddress address,
10370 referenced;
10371 ListCell *cell;
10373 CheckAlterTableIsSafe(partition);
10375 attmap = build_attrmap_by_name(RelationGetDescr(partition),
10376 RelationGetDescr(rel),
10377 false);
10378 for (int j = 0; j < numfks; j++)
10379 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10381 /* Check whether an existing constraint can be repurposed */
10382 partFKs = copyObject(RelationGetFKeyList(partition));
10383 attached = false;
10384 foreach(cell, partFKs)
10386 ForeignKeyCacheInfo *fk;
10388 fk = lfirst_node(ForeignKeyCacheInfo, cell);
10389 if (tryAttachPartitionForeignKey(fk,
10390 partitionId,
10391 parentConstr,
10392 numfks,
10393 mapped_fkattnum,
10394 pkattnum,
10395 pfeqoperators,
10396 insertTriggerOid,
10397 updateTriggerOid,
10398 trigrel))
10400 attached = true;
10401 break;
10404 if (attached)
10406 table_close(partition, NoLock);
10407 continue;
10411 * No luck finding a good constraint to reuse; create our own.
10413 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10414 RelationGetRelid(partition),
10415 fkconstraint->conname))
10416 conname = ChooseConstraintName(RelationGetRelationName(partition),
10417 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10418 "fkey",
10419 RelationGetNamespace(partition), NIL);
10420 else
10421 conname = fkconstraint->conname;
10422 constrOid =
10423 CreateConstraintEntry(conname,
10424 RelationGetNamespace(partition),
10425 CONSTRAINT_FOREIGN,
10426 fkconstraint->deferrable,
10427 fkconstraint->initdeferred,
10428 fkconstraint->initially_valid,
10429 parentConstr,
10430 partitionId,
10431 mapped_fkattnum,
10432 numfks,
10433 numfks,
10434 InvalidOid,
10435 indexOid,
10436 RelationGetRelid(pkrel),
10437 pkattnum,
10438 pfeqoperators,
10439 ppeqoperators,
10440 ffeqoperators,
10441 numfks,
10442 fkconstraint->fk_upd_action,
10443 fkconstraint->fk_del_action,
10444 fkdelsetcols,
10445 numfkdelsetcols,
10446 fkconstraint->fk_matchtype,
10447 NULL,
10448 NULL,
10449 NULL,
10450 false,
10452 false,
10453 false);
10456 * Give this constraint partition-type dependencies on the parent
10457 * constraint as well as the table.
10459 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10460 ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10461 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10462 ObjectAddressSet(referenced, RelationRelationId, partitionId);
10463 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10465 /* Make all this visible before recursing */
10466 CommandCounterIncrement();
10468 /* call ourselves to finalize the creation and we're done */
10469 addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10470 indexOid,
10471 constrOid,
10472 numfks,
10473 pkattnum,
10474 mapped_fkattnum,
10475 pfeqoperators,
10476 ppeqoperators,
10477 ffeqoperators,
10478 numfkdelsetcols,
10479 fkdelsetcols,
10480 old_check_ok,
10481 lockmode,
10482 insertTriggerOid,
10483 updateTriggerOid);
10485 table_close(partition, NoLock);
10488 table_close(trigrel, RowExclusiveLock);
10493 * CloneForeignKeyConstraints
10494 * Clone foreign keys from a partitioned table to a newly acquired
10495 * partition.
10497 * partitionRel is a partition of parentRel, so we can be certain that it has
10498 * the same columns with the same datatypes. The columns may be in different
10499 * order, though.
10501 * wqueue must be passed to set up phase 3 constraint checking, unless the
10502 * referencing-side partition is known to be empty (such as in CREATE TABLE /
10503 * PARTITION OF).
10505 static void
10506 CloneForeignKeyConstraints(List **wqueue, Relation parentRel,
10507 Relation partitionRel)
10509 /* This only works for declarative partitioning */
10510 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10513 * Clone constraints for which the parent is on the referenced side.
10515 CloneFkReferenced(parentRel, partitionRel);
10518 * Now clone constraints where the parent is on the referencing side.
10520 CloneFkReferencing(wqueue, parentRel, partitionRel);
10524 * CloneFkReferenced
10525 * Subroutine for CloneForeignKeyConstraints
10527 * Find all the FKs that have the parent relation on the referenced side;
10528 * clone those constraints to the given partition. This is to be called
10529 * when the partition is being created or attached.
10531 * This ignores self-referencing FKs; those are handled by CloneFkReferencing.
10533 * This recurses to partitions, if the relation being attached is partitioned.
10534 * Recursion is done by calling addFkRecurseReferenced.
10536 static void
10537 CloneFkReferenced(Relation parentRel, Relation partitionRel)
10539 Relation pg_constraint;
10540 AttrMap *attmap;
10541 ListCell *cell;
10542 SysScanDesc scan;
10543 ScanKeyData key[2];
10544 HeapTuple tuple;
10545 List *clone = NIL;
10546 Relation trigrel;
10549 * Search for any constraints where this partition's parent is in the
10550 * referenced side. However, we must not clone any constraint whose
10551 * parent constraint is also going to be cloned, to avoid duplicates. So
10552 * do it in two steps: first construct the list of constraints to clone,
10553 * then go over that list cloning those whose parents are not in the list.
10554 * (We must not rely on the parent being seen first, since the catalog
10555 * scan could return children first.)
10557 pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10558 ScanKeyInit(&key[0],
10559 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10560 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10561 ScanKeyInit(&key[1],
10562 Anum_pg_constraint_contype, BTEqualStrategyNumber,
10563 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10564 /* This is a seqscan, as we don't have a usable index ... */
10565 scan = systable_beginscan(pg_constraint, InvalidOid, true,
10566 NULL, 2, key);
10567 while ((tuple = systable_getnext(scan)) != NULL)
10569 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10571 clone = lappend_oid(clone, constrForm->oid);
10573 systable_endscan(scan);
10574 table_close(pg_constraint, RowShareLock);
10577 * Triggers of the foreign keys will be manipulated a bunch of times in
10578 * the loop below. To avoid repeatedly opening/closing the trigger
10579 * catalog relation, we open it here and pass it to the subroutines called
10580 * below.
10582 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10584 attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10585 RelationGetDescr(parentRel),
10586 false);
10587 foreach(cell, clone)
10589 Oid constrOid = lfirst_oid(cell);
10590 Form_pg_constraint constrForm;
10591 Relation fkRel;
10592 Oid indexOid;
10593 Oid partIndexId;
10594 int numfks;
10595 AttrNumber conkey[INDEX_MAX_KEYS];
10596 AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10597 AttrNumber confkey[INDEX_MAX_KEYS];
10598 Oid conpfeqop[INDEX_MAX_KEYS];
10599 Oid conppeqop[INDEX_MAX_KEYS];
10600 Oid conffeqop[INDEX_MAX_KEYS];
10601 int numfkdelsetcols;
10602 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10603 Constraint *fkconstraint;
10604 Oid deleteTriggerOid,
10605 updateTriggerOid;
10607 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10608 if (!HeapTupleIsValid(tuple))
10609 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10610 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10613 * As explained above: don't try to clone a constraint for which we're
10614 * going to clone the parent.
10616 if (list_member_oid(clone, constrForm->conparentid))
10618 ReleaseSysCache(tuple);
10619 continue;
10623 * Don't clone self-referencing foreign keys, which can be in the
10624 * partitioned table or in the partition-to-be.
10626 if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10627 constrForm->conrelid == RelationGetRelid(partitionRel))
10629 ReleaseSysCache(tuple);
10630 continue;
10634 * Because we're only expanding the key space at the referenced side,
10635 * we don't need to prevent any operation in the referencing table, so
10636 * AccessShareLock suffices (assumes that dropping the constraint
10637 * acquires AEL).
10639 fkRel = table_open(constrForm->conrelid, AccessShareLock);
10641 indexOid = constrForm->conindid;
10642 DeconstructFkConstraintRow(tuple,
10643 &numfks,
10644 conkey,
10645 confkey,
10646 conpfeqop,
10647 conppeqop,
10648 conffeqop,
10649 &numfkdelsetcols,
10650 confdelsetcols);
10652 for (int i = 0; i < numfks; i++)
10653 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10655 fkconstraint = makeNode(Constraint);
10656 fkconstraint->contype = CONSTRAINT_FOREIGN;
10657 fkconstraint->conname = NameStr(constrForm->conname);
10658 fkconstraint->deferrable = constrForm->condeferrable;
10659 fkconstraint->initdeferred = constrForm->condeferred;
10660 fkconstraint->location = -1;
10661 fkconstraint->pktable = NULL;
10662 /* ->fk_attrs determined below */
10663 fkconstraint->pk_attrs = NIL;
10664 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10665 fkconstraint->fk_upd_action = constrForm->confupdtype;
10666 fkconstraint->fk_del_action = constrForm->confdeltype;
10667 fkconstraint->fk_del_set_cols = NIL;
10668 fkconstraint->old_conpfeqop = NIL;
10669 fkconstraint->old_pktable_oid = InvalidOid;
10670 fkconstraint->skip_validation = false;
10671 fkconstraint->initially_valid = true;
10673 /* set up colnames that are used to generate the constraint name */
10674 for (int i = 0; i < numfks; i++)
10676 Form_pg_attribute att;
10678 att = TupleDescAttr(RelationGetDescr(fkRel),
10679 conkey[i] - 1);
10680 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10681 makeString(NameStr(att->attname)));
10685 * Add the new foreign key constraint pointing to the new partition.
10686 * Because this new partition appears in the referenced side of the
10687 * constraint, we don't need to set up for Phase 3 check.
10689 partIndexId = index_get_partition(partitionRel, indexOid);
10690 if (!OidIsValid(partIndexId))
10691 elog(ERROR, "index for %u not found in partition %s",
10692 indexOid, RelationGetRelationName(partitionRel));
10695 * Get the "action" triggers belonging to the constraint to pass as
10696 * parent OIDs for similar triggers that will be created on the
10697 * partition in addFkRecurseReferenced().
10699 GetForeignKeyActionTriggers(trigrel, constrOid,
10700 constrForm->confrelid, constrForm->conrelid,
10701 &deleteTriggerOid, &updateTriggerOid);
10703 addFkRecurseReferenced(NULL,
10704 fkconstraint,
10705 fkRel,
10706 partitionRel,
10707 partIndexId,
10708 constrOid,
10709 numfks,
10710 mapped_confkey,
10711 conkey,
10712 conpfeqop,
10713 conppeqop,
10714 conffeqop,
10715 numfkdelsetcols,
10716 confdelsetcols,
10717 true,
10718 deleteTriggerOid,
10719 updateTriggerOid);
10721 table_close(fkRel, NoLock);
10722 ReleaseSysCache(tuple);
10725 table_close(trigrel, RowExclusiveLock);
10729 * CloneFkReferencing
10730 * Subroutine for CloneForeignKeyConstraints
10732 * For each FK constraint of the parent relation in the given list, find an
10733 * equivalent constraint in its partition relation that can be reparented;
10734 * if one cannot be found, create a new constraint in the partition as its
10735 * child.
10737 * If wqueue is given, it is used to set up phase-3 verification for each
10738 * cloned constraint; if omitted, we assume that such verification is not
10739 * needed (example: the partition is being created anew).
10741 static void
10742 CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
10744 AttrMap *attmap;
10745 List *partFKs;
10746 List *clone = NIL;
10747 ListCell *cell;
10748 Relation trigrel;
10750 /* obtain a list of constraints that we need to clone */
10751 foreach(cell, RelationGetFKeyList(parentRel))
10753 ForeignKeyCacheInfo *fk = lfirst(cell);
10755 clone = lappend_oid(clone, fk->conoid);
10759 * Silently do nothing if there's nothing to do. In particular, this
10760 * avoids throwing a spurious error for foreign tables.
10762 if (clone == NIL)
10763 return;
10765 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10766 ereport(ERROR,
10767 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10768 errmsg("foreign key constraints are not supported on foreign tables")));
10771 * Triggers of the foreign keys will be manipulated a bunch of times in
10772 * the loop below. To avoid repeatedly opening/closing the trigger
10773 * catalog relation, we open it here and pass it to the subroutines called
10774 * below.
10776 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10779 * The constraint key may differ, if the columns in the partition are
10780 * different. This map is used to convert them.
10782 attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10783 RelationGetDescr(parentRel),
10784 false);
10786 partFKs = copyObject(RelationGetFKeyList(partRel));
10788 foreach(cell, clone)
10790 Oid parentConstrOid = lfirst_oid(cell);
10791 Form_pg_constraint constrForm;
10792 Relation pkrel;
10793 HeapTuple tuple;
10794 int numfks;
10795 AttrNumber conkey[INDEX_MAX_KEYS];
10796 AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10797 AttrNumber confkey[INDEX_MAX_KEYS];
10798 Oid conpfeqop[INDEX_MAX_KEYS];
10799 Oid conppeqop[INDEX_MAX_KEYS];
10800 Oid conffeqop[INDEX_MAX_KEYS];
10801 int numfkdelsetcols;
10802 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10803 Constraint *fkconstraint;
10804 bool attached;
10805 Oid indexOid;
10806 Oid constrOid;
10807 ObjectAddress address,
10808 referenced;
10809 ListCell *lc;
10810 Oid insertTriggerOid,
10811 updateTriggerOid;
10813 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
10814 if (!HeapTupleIsValid(tuple))
10815 elog(ERROR, "cache lookup failed for constraint %u",
10816 parentConstrOid);
10817 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10819 /* Don't clone constraints whose parents are being cloned */
10820 if (list_member_oid(clone, constrForm->conparentid))
10822 ReleaseSysCache(tuple);
10823 continue;
10827 * Need to prevent concurrent deletions. If pkrel is a partitioned
10828 * relation, that means to lock all partitions.
10830 pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10831 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10832 (void) find_all_inheritors(RelationGetRelid(pkrel),
10833 ShareRowExclusiveLock, NULL);
10835 DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
10836 conpfeqop, conppeqop, conffeqop,
10837 &numfkdelsetcols, confdelsetcols);
10838 for (int i = 0; i < numfks; i++)
10839 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
10842 * Get the "check" triggers belonging to the constraint to pass as
10843 * parent OIDs for similar triggers that will be created on the
10844 * partition in addFkRecurseReferencing(). They are also passed to
10845 * tryAttachPartitionForeignKey() below to simply assign as parents to
10846 * the partition's existing "check" triggers, that is, if the
10847 * corresponding constraints is deemed attachable to the parent
10848 * constraint.
10850 GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10851 constrForm->confrelid, constrForm->conrelid,
10852 &insertTriggerOid, &updateTriggerOid);
10855 * Before creating a new constraint, see whether any existing FKs are
10856 * fit for the purpose. If one is, attach the parent constraint to
10857 * it, and don't clone anything. This way we avoid the expensive
10858 * verification step and don't end up with a duplicate FK, and we
10859 * don't need to recurse to partitions for this constraint.
10861 attached = false;
10862 foreach(lc, partFKs)
10864 ForeignKeyCacheInfo *fk = lfirst_node(ForeignKeyCacheInfo, lc);
10866 if (tryAttachPartitionForeignKey(fk,
10867 RelationGetRelid(partRel),
10868 parentConstrOid,
10869 numfks,
10870 mapped_conkey,
10871 confkey,
10872 conpfeqop,
10873 insertTriggerOid,
10874 updateTriggerOid,
10875 trigrel))
10877 attached = true;
10878 table_close(pkrel, NoLock);
10879 break;
10882 if (attached)
10884 ReleaseSysCache(tuple);
10885 continue;
10888 /* No dice. Set up to create our own constraint */
10889 fkconstraint = makeNode(Constraint);
10890 fkconstraint->contype = CONSTRAINT_FOREIGN;
10891 /* ->conname determined below */
10892 fkconstraint->deferrable = constrForm->condeferrable;
10893 fkconstraint->initdeferred = constrForm->condeferred;
10894 fkconstraint->location = -1;
10895 fkconstraint->pktable = NULL;
10896 /* ->fk_attrs determined below */
10897 fkconstraint->pk_attrs = NIL;
10898 fkconstraint->fk_matchtype = constrForm->confmatchtype;
10899 fkconstraint->fk_upd_action = constrForm->confupdtype;
10900 fkconstraint->fk_del_action = constrForm->confdeltype;
10901 fkconstraint->fk_del_set_cols = NIL;
10902 fkconstraint->old_conpfeqop = NIL;
10903 fkconstraint->old_pktable_oid = InvalidOid;
10904 fkconstraint->skip_validation = false;
10905 fkconstraint->initially_valid = true;
10906 for (int i = 0; i < numfks; i++)
10908 Form_pg_attribute att;
10910 att = TupleDescAttr(RelationGetDescr(partRel),
10911 mapped_conkey[i] - 1);
10912 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10913 makeString(NameStr(att->attname)));
10915 if (ConstraintNameIsUsed(CONSTRAINT_RELATION,
10916 RelationGetRelid(partRel),
10917 NameStr(constrForm->conname)))
10918 fkconstraint->conname =
10919 ChooseConstraintName(RelationGetRelationName(partRel),
10920 ChooseForeignKeyConstraintNameAddition(fkconstraint->fk_attrs),
10921 "fkey",
10922 RelationGetNamespace(partRel), NIL);
10923 else
10924 fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10926 indexOid = constrForm->conindid;
10927 constrOid =
10928 CreateConstraintEntry(fkconstraint->conname,
10929 constrForm->connamespace,
10930 CONSTRAINT_FOREIGN,
10931 fkconstraint->deferrable,
10932 fkconstraint->initdeferred,
10933 constrForm->convalidated,
10934 parentConstrOid,
10935 RelationGetRelid(partRel),
10936 mapped_conkey,
10937 numfks,
10938 numfks,
10939 InvalidOid, /* not a domain constraint */
10940 indexOid,
10941 constrForm->confrelid, /* same foreign rel */
10942 confkey,
10943 conpfeqop,
10944 conppeqop,
10945 conffeqop,
10946 numfks,
10947 fkconstraint->fk_upd_action,
10948 fkconstraint->fk_del_action,
10949 confdelsetcols,
10950 numfkdelsetcols,
10951 fkconstraint->fk_matchtype,
10952 NULL,
10953 NULL,
10954 NULL,
10955 false, /* islocal */
10956 1, /* inhcount */
10957 false, /* conNoInherit */
10958 true);
10960 /* Set up partition dependencies for the new constraint */
10961 ObjectAddressSet(address, ConstraintRelationId, constrOid);
10962 ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10963 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10964 ObjectAddressSet(referenced, RelationRelationId,
10965 RelationGetRelid(partRel));
10966 recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10968 /* Done with the cloned constraint's tuple */
10969 ReleaseSysCache(tuple);
10971 /* Make all this visible before recursing */
10972 CommandCounterIncrement();
10974 addFkRecurseReferencing(wqueue,
10975 fkconstraint,
10976 partRel,
10977 pkrel,
10978 indexOid,
10979 constrOid,
10980 numfks,
10981 confkey,
10982 mapped_conkey,
10983 conpfeqop,
10984 conppeqop,
10985 conffeqop,
10986 numfkdelsetcols,
10987 confdelsetcols,
10988 false, /* no old check exists */
10989 AccessExclusiveLock,
10990 insertTriggerOid,
10991 updateTriggerOid);
10992 table_close(pkrel, NoLock);
10995 table_close(trigrel, RowExclusiveLock);
10999 * When the parent of a partition receives [the referencing side of] a foreign
11000 * key, we must propagate that foreign key to the partition. However, the
11001 * partition might already have an equivalent foreign key; this routine
11002 * compares the given ForeignKeyCacheInfo (in the partition) to the FK defined
11003 * by the other parameters. If they are equivalent, create the link between
11004 * the two constraints and return true.
11006 * If the given FK does not match the one defined by rest of the params,
11007 * return false.
11009 static bool
11010 tryAttachPartitionForeignKey(ForeignKeyCacheInfo *fk,
11011 Oid partRelid,
11012 Oid parentConstrOid,
11013 int numfks,
11014 AttrNumber *mapped_conkey,
11015 AttrNumber *confkey,
11016 Oid *conpfeqop,
11017 Oid parentInsTrigger,
11018 Oid parentUpdTrigger,
11019 Relation trigrel)
11021 HeapTuple parentConstrTup;
11022 Form_pg_constraint parentConstr;
11023 HeapTuple partcontup;
11024 Form_pg_constraint partConstr;
11025 ScanKeyData key;
11026 SysScanDesc scan;
11027 HeapTuple trigtup;
11028 Oid insertTriggerOid,
11029 updateTriggerOid;
11031 parentConstrTup = SearchSysCache1(CONSTROID,
11032 ObjectIdGetDatum(parentConstrOid));
11033 if (!HeapTupleIsValid(parentConstrTup))
11034 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11035 parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11038 * Do some quick & easy initial checks. If any of these fail, we cannot
11039 * use this constraint.
11041 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11043 ReleaseSysCache(parentConstrTup);
11044 return false;
11046 for (int i = 0; i < numfks; i++)
11048 if (fk->conkey[i] != mapped_conkey[i] ||
11049 fk->confkey[i] != confkey[i] ||
11050 fk->conpfeqop[i] != conpfeqop[i])
11052 ReleaseSysCache(parentConstrTup);
11053 return false;
11058 * Looks good so far; do some more extensive checks. Presumably the check
11059 * for 'convalidated' could be dropped, since we don't really care about
11060 * that, but let's be careful for now.
11062 partcontup = SearchSysCache1(CONSTROID,
11063 ObjectIdGetDatum(fk->conoid));
11064 if (!HeapTupleIsValid(partcontup))
11065 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11066 partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11067 if (OidIsValid(partConstr->conparentid) ||
11068 !partConstr->convalidated ||
11069 partConstr->condeferrable != parentConstr->condeferrable ||
11070 partConstr->condeferred != parentConstr->condeferred ||
11071 partConstr->confupdtype != parentConstr->confupdtype ||
11072 partConstr->confdeltype != parentConstr->confdeltype ||
11073 partConstr->confmatchtype != parentConstr->confmatchtype)
11075 ReleaseSysCache(parentConstrTup);
11076 ReleaseSysCache(partcontup);
11077 return false;
11080 ReleaseSysCache(partcontup);
11081 ReleaseSysCache(parentConstrTup);
11084 * Looks good! Attach this constraint. The action triggers in the new
11085 * partition become redundant -- the parent table already has equivalent
11086 * ones, and those will be able to reach the partition. Remove the ones
11087 * in the partition. We identify them because they have our constraint
11088 * OID, as well as being on the referenced rel.
11090 ScanKeyInit(&key,
11091 Anum_pg_trigger_tgconstraint,
11092 BTEqualStrategyNumber, F_OIDEQ,
11093 ObjectIdGetDatum(fk->conoid));
11094 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11095 NULL, 1, &key);
11096 while ((trigtup = systable_getnext(scan)) != NULL)
11098 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11099 ObjectAddress trigger;
11101 if (trgform->tgconstrrelid != fk->conrelid)
11102 continue;
11103 if (trgform->tgrelid != fk->confrelid)
11104 continue;
11107 * The constraint is originally set up to contain this trigger as an
11108 * implementation object, so there's a dependency record that links
11109 * the two; however, since the trigger is no longer needed, we remove
11110 * the dependency link in order to be able to drop the trigger while
11111 * keeping the constraint intact.
11113 deleteDependencyRecordsFor(TriggerRelationId,
11114 trgform->oid,
11115 false);
11116 /* make dependency deletion visible to performDeletion */
11117 CommandCounterIncrement();
11118 ObjectAddressSet(trigger, TriggerRelationId,
11119 trgform->oid);
11120 performDeletion(&trigger, DROP_RESTRICT, 0);
11121 /* make trigger drop visible, in case the loop iterates */
11122 CommandCounterIncrement();
11125 systable_endscan(scan);
11127 ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11130 * Like the constraint, attach partition's "check" triggers to the
11131 * corresponding parent triggers.
11133 GetForeignKeyCheckTriggers(trigrel,
11134 fk->conoid, fk->confrelid, fk->conrelid,
11135 &insertTriggerOid, &updateTriggerOid);
11136 Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11137 TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11138 partRelid);
11139 Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11140 TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11141 partRelid);
11143 CommandCounterIncrement();
11144 return true;
11148 * GetForeignKeyActionTriggers
11149 * Returns delete and update "action" triggers of the given relation
11150 * belonging to the given constraint
11152 static void
11153 GetForeignKeyActionTriggers(Relation trigrel,
11154 Oid conoid, Oid confrelid, Oid conrelid,
11155 Oid *deleteTriggerOid,
11156 Oid *updateTriggerOid)
11158 ScanKeyData key;
11159 SysScanDesc scan;
11160 HeapTuple trigtup;
11162 *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11163 ScanKeyInit(&key,
11164 Anum_pg_trigger_tgconstraint,
11165 BTEqualStrategyNumber, F_OIDEQ,
11166 ObjectIdGetDatum(conoid));
11168 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11169 NULL, 1, &key);
11170 while ((trigtup = systable_getnext(scan)) != NULL)
11172 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11174 if (trgform->tgconstrrelid != conrelid)
11175 continue;
11176 if (trgform->tgrelid != confrelid)
11177 continue;
11178 /* Only ever look at "action" triggers on the PK side. */
11179 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11180 continue;
11181 if (TRIGGER_FOR_DELETE(trgform->tgtype))
11183 Assert(*deleteTriggerOid == InvalidOid);
11184 *deleteTriggerOid = trgform->oid;
11186 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11188 Assert(*updateTriggerOid == InvalidOid);
11189 *updateTriggerOid = trgform->oid;
11191 #ifndef USE_ASSERT_CHECKING
11192 /* In an assert-enabled build, continue looking to find duplicates */
11193 if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11194 break;
11195 #endif
11198 if (!OidIsValid(*deleteTriggerOid))
11199 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11200 conoid);
11201 if (!OidIsValid(*updateTriggerOid))
11202 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11203 conoid);
11205 systable_endscan(scan);
11209 * GetForeignKeyCheckTriggers
11210 * Returns insert and update "check" triggers of the given relation
11211 * belonging to the given constraint
11213 static void
11214 GetForeignKeyCheckTriggers(Relation trigrel,
11215 Oid conoid, Oid confrelid, Oid conrelid,
11216 Oid *insertTriggerOid,
11217 Oid *updateTriggerOid)
11219 ScanKeyData key;
11220 SysScanDesc scan;
11221 HeapTuple trigtup;
11223 *insertTriggerOid = *updateTriggerOid = InvalidOid;
11224 ScanKeyInit(&key,
11225 Anum_pg_trigger_tgconstraint,
11226 BTEqualStrategyNumber, F_OIDEQ,
11227 ObjectIdGetDatum(conoid));
11229 scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11230 NULL, 1, &key);
11231 while ((trigtup = systable_getnext(scan)) != NULL)
11233 Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11235 if (trgform->tgconstrrelid != confrelid)
11236 continue;
11237 if (trgform->tgrelid != conrelid)
11238 continue;
11239 /* Only ever look at "check" triggers on the FK side. */
11240 if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11241 continue;
11242 if (TRIGGER_FOR_INSERT(trgform->tgtype))
11244 Assert(*insertTriggerOid == InvalidOid);
11245 *insertTriggerOid = trgform->oid;
11247 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11249 Assert(*updateTriggerOid == InvalidOid);
11250 *updateTriggerOid = trgform->oid;
11252 #ifndef USE_ASSERT_CHECKING
11253 /* In an assert-enabled build, continue looking to find duplicates. */
11254 if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11255 break;
11256 #endif
11259 if (!OidIsValid(*insertTriggerOid))
11260 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11261 conoid);
11262 if (!OidIsValid(*updateTriggerOid))
11263 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11264 conoid);
11266 systable_endscan(scan);
11270 * ALTER TABLE ALTER CONSTRAINT
11272 * Update the attributes of a constraint.
11274 * Currently only works for Foreign Key constraints.
11276 * If the constraint is modified, returns its address; otherwise, return
11277 * InvalidObjectAddress.
11279 static ObjectAddress
11280 ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse,
11281 bool recursing, LOCKMODE lockmode)
11283 Constraint *cmdcon;
11284 Relation conrel;
11285 Relation tgrel;
11286 SysScanDesc scan;
11287 ScanKeyData skey[3];
11288 HeapTuple contuple;
11289 Form_pg_constraint currcon;
11290 ObjectAddress address;
11291 List *otherrelids = NIL;
11292 ListCell *lc;
11294 cmdcon = castNode(Constraint, cmd->def);
11296 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11297 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11300 * Find and check the target constraint
11302 ScanKeyInit(&skey[0],
11303 Anum_pg_constraint_conrelid,
11304 BTEqualStrategyNumber, F_OIDEQ,
11305 ObjectIdGetDatum(RelationGetRelid(rel)));
11306 ScanKeyInit(&skey[1],
11307 Anum_pg_constraint_contypid,
11308 BTEqualStrategyNumber, F_OIDEQ,
11309 ObjectIdGetDatum(InvalidOid));
11310 ScanKeyInit(&skey[2],
11311 Anum_pg_constraint_conname,
11312 BTEqualStrategyNumber, F_NAMEEQ,
11313 CStringGetDatum(cmdcon->conname));
11314 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11315 true, NULL, 3, skey);
11317 /* There can be at most one matching row */
11318 if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11319 ereport(ERROR,
11320 (errcode(ERRCODE_UNDEFINED_OBJECT),
11321 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11322 cmdcon->conname, RelationGetRelationName(rel))));
11324 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11325 if (currcon->contype != CONSTRAINT_FOREIGN)
11326 ereport(ERROR,
11327 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11328 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11329 cmdcon->conname, RelationGetRelationName(rel))));
11332 * If it's not the topmost constraint, raise an error.
11334 * Altering a non-topmost constraint leaves some triggers untouched, since
11335 * they are not directly connected to this constraint; also, pg_dump would
11336 * ignore the deferrability status of the individual constraint, since it
11337 * only dumps topmost constraints. Avoid these problems by refusing this
11338 * operation and telling the user to alter the parent constraint instead.
11340 if (OidIsValid(currcon->conparentid))
11342 HeapTuple tp;
11343 Oid parent = currcon->conparentid;
11344 char *ancestorname = NULL;
11345 char *ancestortable = NULL;
11347 /* Loop to find the topmost constraint */
11348 while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11350 Form_pg_constraint contup = (Form_pg_constraint) GETSTRUCT(tp);
11352 /* If no parent, this is the constraint we want */
11353 if (!OidIsValid(contup->conparentid))
11355 ancestorname = pstrdup(NameStr(contup->conname));
11356 ancestortable = get_rel_name(contup->conrelid);
11357 ReleaseSysCache(tp);
11358 break;
11361 parent = contup->conparentid;
11362 ReleaseSysCache(tp);
11365 ereport(ERROR,
11366 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11367 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11368 cmdcon->conname, RelationGetRelationName(rel)),
11369 ancestorname && ancestortable ?
11370 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11371 cmdcon->conname, ancestorname, ancestortable) : 0,
11372 errhint("You may alter the constraint it derives from instead.")));
11376 * Do the actual catalog work. We can skip changing if already in the
11377 * desired state, but not if a partitioned table: partitions need to be
11378 * processed regardless, in case they had the constraint locally changed.
11380 address = InvalidObjectAddress;
11381 if (currcon->condeferrable != cmdcon->deferrable ||
11382 currcon->condeferred != cmdcon->initdeferred ||
11383 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11385 if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11386 &otherrelids, lockmode))
11387 ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11391 * ATExecAlterConstrRecurse already invalidated relcache for the relations
11392 * having the constraint itself; here we also invalidate for relations
11393 * that have any triggers that are part of the constraint.
11395 foreach(lc, otherrelids)
11396 CacheInvalidateRelcacheByRelid(lfirst_oid(lc));
11398 systable_endscan(scan);
11400 table_close(tgrel, RowExclusiveLock);
11401 table_close(conrel, RowExclusiveLock);
11403 return address;
11407 * Recursive subroutine of ATExecAlterConstraint. Returns true if the
11408 * constraint is altered.
11410 * *otherrelids is appended OIDs of relations containing affected triggers.
11412 * Note that we must recurse even when the values are correct, in case
11413 * indirect descendants have had their constraints altered locally.
11414 * (This could be avoided if we forbade altering constraints in partitions
11415 * but existing releases don't do that.)
11417 static bool
11418 ATExecAlterConstrRecurse(Constraint *cmdcon, Relation conrel, Relation tgrel,
11419 Relation rel, HeapTuple contuple, List **otherrelids,
11420 LOCKMODE lockmode)
11422 Form_pg_constraint currcon;
11423 Oid conoid;
11424 Oid refrelid;
11425 bool changed = false;
11427 /* since this function recurses, it could be driven to stack overflow */
11428 check_stack_depth();
11430 currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11431 conoid = currcon->oid;
11432 refrelid = currcon->confrelid;
11435 * Update pg_constraint with the flags from cmdcon.
11437 * If called to modify a constraint that's already in the desired state,
11438 * silently do nothing.
11440 if (currcon->condeferrable != cmdcon->deferrable ||
11441 currcon->condeferred != cmdcon->initdeferred)
11443 HeapTuple copyTuple;
11444 Form_pg_constraint copy_con;
11445 HeapTuple tgtuple;
11446 ScanKeyData tgkey;
11447 SysScanDesc tgscan;
11449 copyTuple = heap_copytuple(contuple);
11450 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11451 copy_con->condeferrable = cmdcon->deferrable;
11452 copy_con->condeferred = cmdcon->initdeferred;
11453 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11455 InvokeObjectPostAlterHook(ConstraintRelationId,
11456 conoid, 0);
11458 heap_freetuple(copyTuple);
11459 changed = true;
11461 /* Make new constraint flags visible to others */
11462 CacheInvalidateRelcache(rel);
11465 * Now we need to update the multiple entries in pg_trigger that
11466 * implement the constraint.
11468 ScanKeyInit(&tgkey,
11469 Anum_pg_trigger_tgconstraint,
11470 BTEqualStrategyNumber, F_OIDEQ,
11471 ObjectIdGetDatum(conoid));
11472 tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11473 NULL, 1, &tgkey);
11474 while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11476 Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11477 Form_pg_trigger copy_tg;
11478 HeapTuple tgCopyTuple;
11481 * Remember OIDs of other relation(s) involved in FK constraint.
11482 * (Note: it's likely that we could skip forcing a relcache inval
11483 * for other rels that don't have a trigger whose properties
11484 * change, but let's be conservative.)
11486 if (tgform->tgrelid != RelationGetRelid(rel))
11487 *otherrelids = list_append_unique_oid(*otherrelids,
11488 tgform->tgrelid);
11491 * Update deferrability of RI_FKey_noaction_del,
11492 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11493 * triggers, but not others; see createForeignKeyActionTriggers
11494 * and CreateFKCheckTrigger.
11496 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11497 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11498 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11499 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11500 continue;
11502 tgCopyTuple = heap_copytuple(tgtuple);
11503 copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11505 copy_tg->tgdeferrable = cmdcon->deferrable;
11506 copy_tg->tginitdeferred = cmdcon->initdeferred;
11507 CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11509 InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11511 heap_freetuple(tgCopyTuple);
11514 systable_endscan(tgscan);
11518 * If the table at either end of the constraint is partitioned, we need to
11519 * recurse and handle every constraint that is a child of this one.
11521 * (This assumes that the recurse flag is forcibly set for partitioned
11522 * tables, and not set for legacy inheritance, though we don't check for
11523 * that here.)
11525 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11526 get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11528 ScanKeyData pkey;
11529 SysScanDesc pscan;
11530 HeapTuple childtup;
11532 ScanKeyInit(&pkey,
11533 Anum_pg_constraint_conparentid,
11534 BTEqualStrategyNumber, F_OIDEQ,
11535 ObjectIdGetDatum(conoid));
11537 pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11538 true, NULL, 1, &pkey);
11540 while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11542 Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11543 Relation childrel;
11545 childrel = table_open(childcon->conrelid, lockmode);
11546 ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11547 otherrelids, lockmode);
11548 table_close(childrel, NoLock);
11551 systable_endscan(pscan);
11554 return changed;
11558 * ALTER TABLE VALIDATE CONSTRAINT
11560 * XXX The reason we handle recursion here rather than at Phase 1 is because
11561 * there's no good way to skip recursing when handling foreign keys: there is
11562 * no need to lock children in that case, yet we wouldn't be able to avoid
11563 * doing so at that level.
11565 * Return value is the address of the validated constraint. If the constraint
11566 * was already validated, InvalidObjectAddress is returned.
11568 static ObjectAddress
11569 ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName,
11570 bool recurse, bool recursing, LOCKMODE lockmode)
11572 Relation conrel;
11573 SysScanDesc scan;
11574 ScanKeyData skey[3];
11575 HeapTuple tuple;
11576 Form_pg_constraint con;
11577 ObjectAddress address;
11579 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11582 * Find and check the target constraint
11584 ScanKeyInit(&skey[0],
11585 Anum_pg_constraint_conrelid,
11586 BTEqualStrategyNumber, F_OIDEQ,
11587 ObjectIdGetDatum(RelationGetRelid(rel)));
11588 ScanKeyInit(&skey[1],
11589 Anum_pg_constraint_contypid,
11590 BTEqualStrategyNumber, F_OIDEQ,
11591 ObjectIdGetDatum(InvalidOid));
11592 ScanKeyInit(&skey[2],
11593 Anum_pg_constraint_conname,
11594 BTEqualStrategyNumber, F_NAMEEQ,
11595 CStringGetDatum(constrName));
11596 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11597 true, NULL, 3, skey);
11599 /* There can be at most one matching row */
11600 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11601 ereport(ERROR,
11602 (errcode(ERRCODE_UNDEFINED_OBJECT),
11603 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11604 constrName, RelationGetRelationName(rel))));
11606 con = (Form_pg_constraint) GETSTRUCT(tuple);
11607 if (con->contype != CONSTRAINT_FOREIGN &&
11608 con->contype != CONSTRAINT_CHECK)
11609 ereport(ERROR,
11610 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11611 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11612 constrName, RelationGetRelationName(rel))));
11614 if (!con->convalidated)
11616 AlteredTableInfo *tab;
11617 HeapTuple copyTuple;
11618 Form_pg_constraint copy_con;
11620 if (con->contype == CONSTRAINT_FOREIGN)
11622 NewConstraint *newcon;
11623 Constraint *fkconstraint;
11625 /* Queue validation for phase 3 */
11626 fkconstraint = makeNode(Constraint);
11627 /* for now this is all we need */
11628 fkconstraint->conname = constrName;
11630 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11631 newcon->name = constrName;
11632 newcon->contype = CONSTR_FOREIGN;
11633 newcon->refrelid = con->confrelid;
11634 newcon->refindid = con->conindid;
11635 newcon->conid = con->oid;
11636 newcon->qual = (Node *) fkconstraint;
11638 /* Find or create work queue entry for this table */
11639 tab = ATGetQueueEntry(wqueue, rel);
11640 tab->constraints = lappend(tab->constraints, newcon);
11643 * We disallow creating invalid foreign keys to or from
11644 * partitioned tables, so ignoring the recursion bit is okay.
11647 else if (con->contype == CONSTRAINT_CHECK)
11649 List *children = NIL;
11650 ListCell *child;
11651 NewConstraint *newcon;
11652 Datum val;
11653 char *conbin;
11656 * If we're recursing, the parent has already done this, so skip
11657 * it. Also, if the constraint is a NO INHERIT constraint, we
11658 * shouldn't try to look for it in the children.
11660 if (!recursing && !con->connoinherit)
11661 children = find_all_inheritors(RelationGetRelid(rel),
11662 lockmode, NULL);
11665 * For CHECK constraints, we must ensure that we only mark the
11666 * constraint as validated on the parent if it's already validated
11667 * on the children.
11669 * We recurse before validating on the parent, to reduce risk of
11670 * deadlocks.
11672 foreach(child, children)
11674 Oid childoid = lfirst_oid(child);
11675 Relation childrel;
11677 if (childoid == RelationGetRelid(rel))
11678 continue;
11681 * If we are told not to recurse, there had better not be any
11682 * child tables, because we can't mark the constraint on the
11683 * parent valid unless it is valid for all child tables.
11685 if (!recurse)
11686 ereport(ERROR,
11687 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11688 errmsg("constraint must be validated on child tables too")));
11690 /* find_all_inheritors already got lock */
11691 childrel = table_open(childoid, NoLock);
11693 ATExecValidateConstraint(wqueue, childrel, constrName, false,
11694 true, lockmode);
11695 table_close(childrel, NoLock);
11698 /* Queue validation for phase 3 */
11699 newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11700 newcon->name = constrName;
11701 newcon->contype = CONSTR_CHECK;
11702 newcon->refrelid = InvalidOid;
11703 newcon->refindid = InvalidOid;
11704 newcon->conid = con->oid;
11706 val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11707 Anum_pg_constraint_conbin);
11708 conbin = TextDatumGetCString(val);
11709 newcon->qual = (Node *) stringToNode(conbin);
11711 /* Find or create work queue entry for this table */
11712 tab = ATGetQueueEntry(wqueue, rel);
11713 tab->constraints = lappend(tab->constraints, newcon);
11716 * Invalidate relcache so that others see the new validated
11717 * constraint.
11719 CacheInvalidateRelcache(rel);
11723 * Now update the catalog, while we have the door open.
11725 copyTuple = heap_copytuple(tuple);
11726 copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11727 copy_con->convalidated = true;
11728 CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11730 InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11732 heap_freetuple(copyTuple);
11734 ObjectAddressSet(address, ConstraintRelationId, con->oid);
11736 else
11737 address = InvalidObjectAddress; /* already validated */
11739 systable_endscan(scan);
11741 table_close(conrel, RowExclusiveLock);
11743 return address;
11748 * transformColumnNameList - transform list of column names
11750 * Lookup each name and return its attnum and, optionally, type OID
11752 * Note: the name of this function suggests that it's general-purpose,
11753 * but actually it's only used to look up names appearing in foreign-key
11754 * clauses. The error messages would need work to use it in other cases,
11755 * and perhaps the validity checks as well.
11757 static int
11758 transformColumnNameList(Oid relId, List *colList,
11759 int16 *attnums, Oid *atttypids)
11761 ListCell *l;
11762 int attnum;
11764 attnum = 0;
11765 foreach(l, colList)
11767 char *attname = strVal(lfirst(l));
11768 HeapTuple atttuple;
11769 Form_pg_attribute attform;
11771 atttuple = SearchSysCacheAttName(relId, attname);
11772 if (!HeapTupleIsValid(atttuple))
11773 ereport(ERROR,
11774 (errcode(ERRCODE_UNDEFINED_COLUMN),
11775 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11776 attname)));
11777 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
11778 if (attform->attnum < 0)
11779 ereport(ERROR,
11780 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11781 errmsg("system columns cannot be used in foreign keys")));
11782 if (attnum >= INDEX_MAX_KEYS)
11783 ereport(ERROR,
11784 (errcode(ERRCODE_TOO_MANY_COLUMNS),
11785 errmsg("cannot have more than %d keys in a foreign key",
11786 INDEX_MAX_KEYS)));
11787 attnums[attnum] = attform->attnum;
11788 if (atttypids != NULL)
11789 atttypids[attnum] = attform->atttypid;
11790 ReleaseSysCache(atttuple);
11791 attnum++;
11794 return attnum;
11798 * transformFkeyGetPrimaryKey -
11800 * Look up the names, attnums, and types of the primary key attributes
11801 * for the pkrel. Also return the index OID and index opclasses of the
11802 * index supporting the primary key.
11804 * All parameters except pkrel are output parameters. Also, the function
11805 * return value is the number of attributes in the primary key.
11807 * Used when the column list in the REFERENCES specification is omitted.
11809 static int
11810 transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid,
11811 List **attnamelist,
11812 int16 *attnums, Oid *atttypids,
11813 Oid *opclasses)
11815 List *indexoidlist;
11816 ListCell *indexoidscan;
11817 HeapTuple indexTuple = NULL;
11818 Form_pg_index indexStruct = NULL;
11819 Datum indclassDatum;
11820 oidvector *indclass;
11821 int i;
11824 * Get the list of index OIDs for the table from the relcache, and look up
11825 * each one in the pg_index syscache until we find one marked primary key
11826 * (hopefully there isn't more than one such). Insist it's valid, too.
11828 *indexOid = InvalidOid;
11830 indexoidlist = RelationGetIndexList(pkrel);
11832 foreach(indexoidscan, indexoidlist)
11834 Oid indexoid = lfirst_oid(indexoidscan);
11836 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11837 if (!HeapTupleIsValid(indexTuple))
11838 elog(ERROR, "cache lookup failed for index %u", indexoid);
11839 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11840 if (indexStruct->indisprimary && indexStruct->indisvalid)
11843 * Refuse to use a deferrable primary key. This is per SQL spec,
11844 * and there would be a lot of interesting semantic problems if we
11845 * tried to allow it.
11847 if (!indexStruct->indimmediate)
11848 ereport(ERROR,
11849 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11850 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11851 RelationGetRelationName(pkrel))));
11853 *indexOid = indexoid;
11854 break;
11856 ReleaseSysCache(indexTuple);
11859 list_free(indexoidlist);
11862 * Check that we found it
11864 if (!OidIsValid(*indexOid))
11865 ereport(ERROR,
11866 (errcode(ERRCODE_UNDEFINED_OBJECT),
11867 errmsg("there is no primary key for referenced table \"%s\"",
11868 RelationGetRelationName(pkrel))));
11870 /* Must get indclass the hard way */
11871 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11872 Anum_pg_index_indclass);
11873 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11876 * Now build the list of PK attributes from the indkey definition (we
11877 * assume a primary key cannot have expressional elements)
11879 *attnamelist = NIL;
11880 for (i = 0; i < indexStruct->indnkeyatts; i++)
11882 int pkattno = indexStruct->indkey.values[i];
11884 attnums[i] = pkattno;
11885 atttypids[i] = attnumTypeId(pkrel, pkattno);
11886 opclasses[i] = indclass->values[i];
11887 *attnamelist = lappend(*attnamelist,
11888 makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11891 ReleaseSysCache(indexTuple);
11893 return i;
11897 * transformFkeyCheckAttrs -
11899 * Validate that the 'attnums' columns in the 'pkrel' relation are valid to
11900 * reference as part of a foreign key constraint.
11902 * Returns the OID of the unique index supporting the constraint and
11903 * populates the caller-provided 'opclasses' array with the opclasses
11904 * associated with the index columns.
11906 * Raises an ERROR on validation failure.
11908 static Oid
11909 transformFkeyCheckAttrs(Relation pkrel,
11910 int numattrs, int16 *attnums,
11911 Oid *opclasses)
11913 Oid indexoid = InvalidOid;
11914 bool found = false;
11915 bool found_deferrable = false;
11916 List *indexoidlist;
11917 ListCell *indexoidscan;
11918 int i,
11922 * Reject duplicate appearances of columns in the referenced-columns list.
11923 * Such a case is forbidden by the SQL standard, and even if we thought it
11924 * useful to allow it, there would be ambiguity about how to match the
11925 * list to unique indexes (in particular, it'd be unclear which index
11926 * opclass goes with which FK column).
11928 for (i = 0; i < numattrs; i++)
11930 for (j = i + 1; j < numattrs; j++)
11932 if (attnums[i] == attnums[j])
11933 ereport(ERROR,
11934 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11935 errmsg("foreign key referenced-columns list must not contain duplicates")));
11940 * Get the list of index OIDs for the table from the relcache, and look up
11941 * each one in the pg_index syscache, and match unique indexes to the list
11942 * of attnums we are given.
11944 indexoidlist = RelationGetIndexList(pkrel);
11946 foreach(indexoidscan, indexoidlist)
11948 HeapTuple indexTuple;
11949 Form_pg_index indexStruct;
11951 indexoid = lfirst_oid(indexoidscan);
11952 indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11953 if (!HeapTupleIsValid(indexTuple))
11954 elog(ERROR, "cache lookup failed for index %u", indexoid);
11955 indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11958 * Must have the right number of columns; must be unique and not a
11959 * partial index; forget it if there are any expressions, too. Invalid
11960 * indexes are out as well.
11962 if (indexStruct->indnkeyatts == numattrs &&
11963 indexStruct->indisunique &&
11964 indexStruct->indisvalid &&
11965 heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11966 heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
11968 Datum indclassDatum;
11969 oidvector *indclass;
11971 /* Must get indclass the hard way */
11972 indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11973 Anum_pg_index_indclass);
11974 indclass = (oidvector *) DatumGetPointer(indclassDatum);
11977 * The given attnum list may match the index columns in any order.
11978 * Check for a match, and extract the appropriate opclasses while
11979 * we're at it.
11981 * We know that attnums[] is duplicate-free per the test at the
11982 * start of this function, and we checked above that the number of
11983 * index columns agrees, so if we find a match for each attnums[]
11984 * entry then we must have a one-to-one match in some order.
11986 for (i = 0; i < numattrs; i++)
11988 found = false;
11989 for (j = 0; j < numattrs; j++)
11991 if (attnums[i] == indexStruct->indkey.values[j])
11993 opclasses[i] = indclass->values[j];
11994 found = true;
11995 break;
11998 if (!found)
11999 break;
12003 * Refuse to use a deferrable unique/primary key. This is per SQL
12004 * spec, and there would be a lot of interesting semantic problems
12005 * if we tried to allow it.
12007 if (found && !indexStruct->indimmediate)
12010 * Remember that we found an otherwise matching index, so that
12011 * we can generate a more appropriate error message.
12013 found_deferrable = true;
12014 found = false;
12017 ReleaseSysCache(indexTuple);
12018 if (found)
12019 break;
12022 if (!found)
12024 if (found_deferrable)
12025 ereport(ERROR,
12026 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12027 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12028 RelationGetRelationName(pkrel))));
12029 else
12030 ereport(ERROR,
12031 (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12032 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12033 RelationGetRelationName(pkrel))));
12036 list_free(indexoidlist);
12038 return indexoid;
12042 * findFkeyCast -
12044 * Wrapper around find_coercion_pathway() for ATAddForeignKeyConstraint().
12045 * Caller has equal regard for binary coercibility and for an exact match.
12047 static CoercionPathType
12048 findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
12050 CoercionPathType ret;
12052 if (targetTypeId == sourceTypeId)
12054 ret = COERCION_PATH_RELABELTYPE;
12055 *funcid = InvalidOid;
12057 else
12059 ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12060 COERCION_IMPLICIT, funcid);
12061 if (ret == COERCION_PATH_NONE)
12062 /* A previously-relied-upon cast is now gone. */
12063 elog(ERROR, "could not find cast from %u to %u",
12064 sourceTypeId, targetTypeId);
12067 return ret;
12071 * Permissions checks on the referenced table for ADD FOREIGN KEY
12073 * Note: we have already checked that the user owns the referencing table,
12074 * else we'd have failed much earlier; no additional checks are needed for it.
12076 static void
12077 checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
12079 Oid roleid = GetUserId();
12080 AclResult aclresult;
12081 int i;
12083 /* Okay if we have relation-level REFERENCES permission */
12084 aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12085 ACL_REFERENCES);
12086 if (aclresult == ACLCHECK_OK)
12087 return;
12088 /* Else we must have REFERENCES on each column */
12089 for (i = 0; i < natts; i++)
12091 aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12092 roleid, ACL_REFERENCES);
12093 if (aclresult != ACLCHECK_OK)
12094 aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12095 RelationGetRelationName(rel));
12100 * Scan the existing rows in a table to verify they meet a proposed FK
12101 * constraint.
12103 * Caller must have opened and locked both relations appropriately.
12105 static void
12106 validateForeignKeyConstraint(char *conname,
12107 Relation rel,
12108 Relation pkrel,
12109 Oid pkindOid,
12110 Oid constraintOid)
12112 TupleTableSlot *slot;
12113 TableScanDesc scan;
12114 Trigger trig = {0};
12115 Snapshot snapshot;
12116 MemoryContext oldcxt;
12117 MemoryContext perTupCxt;
12119 ereport(DEBUG1,
12120 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12123 * Build a trigger call structure; we'll need it either way.
12125 trig.tgoid = InvalidOid;
12126 trig.tgname = conname;
12127 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
12128 trig.tgisinternal = true;
12129 trig.tgconstrrelid = RelationGetRelid(pkrel);
12130 trig.tgconstrindid = pkindOid;
12131 trig.tgconstraint = constraintOid;
12132 trig.tgdeferrable = false;
12133 trig.tginitdeferred = false;
12134 /* we needn't fill in remaining fields */
12137 * See if we can do it with a single LEFT JOIN query. A false result
12138 * indicates we must proceed with the fire-the-trigger method.
12140 if (RI_Initial_Check(&trig, rel, pkrel))
12141 return;
12144 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12145 * if that tuple had just been inserted. If any of those fail, it should
12146 * ereport(ERROR) and that's that.
12148 snapshot = RegisterSnapshot(GetLatestSnapshot());
12149 slot = table_slot_create(rel, NULL);
12150 scan = table_beginscan(rel, snapshot, 0, NULL);
12152 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
12153 "validateForeignKeyConstraint",
12154 ALLOCSET_SMALL_SIZES);
12155 oldcxt = MemoryContextSwitchTo(perTupCxt);
12157 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12159 LOCAL_FCINFO(fcinfo, 0);
12160 TriggerData trigdata = {0};
12162 CHECK_FOR_INTERRUPTS();
12165 * Make a call to the trigger function
12167 * No parameters are passed, but we do set a context
12169 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12172 * We assume RI_FKey_check_ins won't look at flinfo...
12174 trigdata.type = T_TriggerData;
12175 trigdata.tg_event = TRIGGER_EVENT_INSERT | TRIGGER_EVENT_ROW;
12176 trigdata.tg_relation = rel;
12177 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12178 trigdata.tg_trigslot = slot;
12179 trigdata.tg_trigger = &trig;
12181 fcinfo->context = (Node *) &trigdata;
12183 RI_FKey_check_ins(fcinfo);
12185 MemoryContextReset(perTupCxt);
12188 MemoryContextSwitchTo(oldcxt);
12189 MemoryContextDelete(perTupCxt);
12190 table_endscan(scan);
12191 UnregisterSnapshot(snapshot);
12192 ExecDropSingleTupleTableSlot(slot);
12196 * CreateFKCheckTrigger
12197 * Creates the insert (on_insert=true) or update "check" trigger that
12198 * implements a given foreign key
12200 * Returns the OID of the so created trigger.
12202 static Oid
12203 CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint,
12204 Oid constraintOid, Oid indexOid, Oid parentTrigOid,
12205 bool on_insert)
12207 ObjectAddress trigAddress;
12208 CreateTrigStmt *fk_trigger;
12211 * Note: for a self-referential FK (referencing and referenced tables are
12212 * the same), it is important that the ON UPDATE action fires before the
12213 * CHECK action, since both triggers will fire on the same row during an
12214 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12215 * state of the row. Triggers fire in name order, so we ensure this by
12216 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12217 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12219 fk_trigger = makeNode(CreateTrigStmt);
12220 fk_trigger->replace = false;
12221 fk_trigger->isconstraint = true;
12222 fk_trigger->trigname = "RI_ConstraintTrigger_c";
12223 fk_trigger->relation = NULL;
12225 /* Either ON INSERT or ON UPDATE */
12226 if (on_insert)
12228 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12229 fk_trigger->events = TRIGGER_TYPE_INSERT;
12231 else
12233 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12234 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12237 fk_trigger->args = NIL;
12238 fk_trigger->row = true;
12239 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12240 fk_trigger->columns = NIL;
12241 fk_trigger->whenClause = NULL;
12242 fk_trigger->transitionRels = NIL;
12243 fk_trigger->deferrable = fkconstraint->deferrable;
12244 fk_trigger->initdeferred = fkconstraint->initdeferred;
12245 fk_trigger->constrrel = NULL;
12247 trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12248 constraintOid, indexOid, InvalidOid,
12249 parentTrigOid, NULL, true, false);
12251 /* Make changes-so-far visible */
12252 CommandCounterIncrement();
12254 return trigAddress.objectId;
12258 * createForeignKeyActionTriggers
12259 * Create the referenced-side "action" triggers that implement a foreign
12260 * key.
12262 * Returns the OIDs of the so created triggers in *deleteTrigOid and
12263 * *updateTrigOid.
12265 static void
12266 createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint,
12267 Oid constraintOid, Oid indexOid,
12268 Oid parentDelTrigger, Oid parentUpdTrigger,
12269 Oid *deleteTrigOid, Oid *updateTrigOid)
12271 CreateTrigStmt *fk_trigger;
12272 ObjectAddress trigAddress;
12275 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12276 * DELETE action on the referenced table.
12278 fk_trigger = makeNode(CreateTrigStmt);
12279 fk_trigger->replace = false;
12280 fk_trigger->isconstraint = true;
12281 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12282 fk_trigger->relation = NULL;
12283 fk_trigger->args = NIL;
12284 fk_trigger->row = true;
12285 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12286 fk_trigger->events = TRIGGER_TYPE_DELETE;
12287 fk_trigger->columns = NIL;
12288 fk_trigger->whenClause = NULL;
12289 fk_trigger->transitionRels = NIL;
12290 fk_trigger->constrrel = NULL;
12291 switch (fkconstraint->fk_del_action)
12293 case FKCONSTR_ACTION_NOACTION:
12294 fk_trigger->deferrable = fkconstraint->deferrable;
12295 fk_trigger->initdeferred = fkconstraint->initdeferred;
12296 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12297 break;
12298 case FKCONSTR_ACTION_RESTRICT:
12299 fk_trigger->deferrable = false;
12300 fk_trigger->initdeferred = false;
12301 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12302 break;
12303 case FKCONSTR_ACTION_CASCADE:
12304 fk_trigger->deferrable = false;
12305 fk_trigger->initdeferred = false;
12306 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12307 break;
12308 case FKCONSTR_ACTION_SETNULL:
12309 fk_trigger->deferrable = false;
12310 fk_trigger->initdeferred = false;
12311 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12312 break;
12313 case FKCONSTR_ACTION_SETDEFAULT:
12314 fk_trigger->deferrable = false;
12315 fk_trigger->initdeferred = false;
12316 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12317 break;
12318 default:
12319 elog(ERROR, "unrecognized FK action type: %d",
12320 (int) fkconstraint->fk_del_action);
12321 break;
12324 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12325 RelationGetRelid(rel),
12326 constraintOid, indexOid, InvalidOid,
12327 parentDelTrigger, NULL, true, false);
12328 if (deleteTrigOid)
12329 *deleteTrigOid = trigAddress.objectId;
12331 /* Make changes-so-far visible */
12332 CommandCounterIncrement();
12335 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12336 * UPDATE action on the referenced table.
12338 fk_trigger = makeNode(CreateTrigStmt);
12339 fk_trigger->replace = false;
12340 fk_trigger->isconstraint = true;
12341 fk_trigger->trigname = "RI_ConstraintTrigger_a";
12342 fk_trigger->relation = NULL;
12343 fk_trigger->args = NIL;
12344 fk_trigger->row = true;
12345 fk_trigger->timing = TRIGGER_TYPE_AFTER;
12346 fk_trigger->events = TRIGGER_TYPE_UPDATE;
12347 fk_trigger->columns = NIL;
12348 fk_trigger->whenClause = NULL;
12349 fk_trigger->transitionRels = NIL;
12350 fk_trigger->constrrel = NULL;
12351 switch (fkconstraint->fk_upd_action)
12353 case FKCONSTR_ACTION_NOACTION:
12354 fk_trigger->deferrable = fkconstraint->deferrable;
12355 fk_trigger->initdeferred = fkconstraint->initdeferred;
12356 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12357 break;
12358 case FKCONSTR_ACTION_RESTRICT:
12359 fk_trigger->deferrable = false;
12360 fk_trigger->initdeferred = false;
12361 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12362 break;
12363 case FKCONSTR_ACTION_CASCADE:
12364 fk_trigger->deferrable = false;
12365 fk_trigger->initdeferred = false;
12366 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12367 break;
12368 case FKCONSTR_ACTION_SETNULL:
12369 fk_trigger->deferrable = false;
12370 fk_trigger->initdeferred = false;
12371 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12372 break;
12373 case FKCONSTR_ACTION_SETDEFAULT:
12374 fk_trigger->deferrable = false;
12375 fk_trigger->initdeferred = false;
12376 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12377 break;
12378 default:
12379 elog(ERROR, "unrecognized FK action type: %d",
12380 (int) fkconstraint->fk_upd_action);
12381 break;
12384 trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12385 RelationGetRelid(rel),
12386 constraintOid, indexOid, InvalidOid,
12387 parentUpdTrigger, NULL, true, false);
12388 if (updateTrigOid)
12389 *updateTrigOid = trigAddress.objectId;
12393 * createForeignKeyCheckTriggers
12394 * Create the referencing-side "check" triggers that implement a foreign
12395 * key.
12397 * Returns the OIDs of the so created triggers in *insertTrigOid and
12398 * *updateTrigOid.
12400 static void
12401 createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid,
12402 Constraint *fkconstraint, Oid constraintOid,
12403 Oid indexOid,
12404 Oid parentInsTrigger, Oid parentUpdTrigger,
12405 Oid *insertTrigOid, Oid *updateTrigOid)
12407 *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12408 constraintOid, indexOid,
12409 parentInsTrigger, true);
12410 *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12411 constraintOid, indexOid,
12412 parentUpdTrigger, false);
12416 * ALTER TABLE DROP CONSTRAINT
12418 * Like DROP COLUMN, we can't use the normal ALTER TABLE recursion mechanism.
12420 static void
12421 ATExecDropConstraint(Relation rel, const char *constrName,
12422 DropBehavior behavior,
12423 bool recurse, bool recursing,
12424 bool missing_ok, LOCKMODE lockmode)
12426 List *children;
12427 Relation conrel;
12428 Form_pg_constraint con;
12429 SysScanDesc scan;
12430 ScanKeyData skey[3];
12431 HeapTuple tuple;
12432 bool found = false;
12433 bool is_no_inherit_constraint = false;
12434 char contype;
12436 /* At top level, permission check was done in ATPrepCmd, else do it */
12437 if (recursing)
12438 ATSimplePermissions(AT_DropConstraint, rel, ATT_TABLE | ATT_FOREIGN_TABLE);
12440 conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12443 * Find and drop the target constraint
12445 ScanKeyInit(&skey[0],
12446 Anum_pg_constraint_conrelid,
12447 BTEqualStrategyNumber, F_OIDEQ,
12448 ObjectIdGetDatum(RelationGetRelid(rel)));
12449 ScanKeyInit(&skey[1],
12450 Anum_pg_constraint_contypid,
12451 BTEqualStrategyNumber, F_OIDEQ,
12452 ObjectIdGetDatum(InvalidOid));
12453 ScanKeyInit(&skey[2],
12454 Anum_pg_constraint_conname,
12455 BTEqualStrategyNumber, F_NAMEEQ,
12456 CStringGetDatum(constrName));
12457 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12458 true, NULL, 3, skey);
12460 /* There can be at most one matching row */
12461 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12463 ObjectAddress conobj;
12465 con = (Form_pg_constraint) GETSTRUCT(tuple);
12467 /* Don't drop inherited constraints */
12468 if (con->coninhcount > 0 && !recursing)
12469 ereport(ERROR,
12470 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12471 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12472 constrName, RelationGetRelationName(rel))));
12474 is_no_inherit_constraint = con->connoinherit;
12475 contype = con->contype;
12478 * If it's a foreign-key constraint, we'd better lock the referenced
12479 * table and check that that's not in use, just as we've already done
12480 * for the constrained table (else we might, eg, be dropping a trigger
12481 * that has unfired events). But we can/must skip that in the
12482 * self-referential case.
12484 if (contype == CONSTRAINT_FOREIGN &&
12485 con->confrelid != RelationGetRelid(rel))
12487 Relation frel;
12489 /* Must match lock taken by RemoveTriggerById: */
12490 frel = table_open(con->confrelid, AccessExclusiveLock);
12491 CheckAlterTableIsSafe(frel);
12492 table_close(frel, NoLock);
12496 * Perform the actual constraint deletion
12498 conobj.classId = ConstraintRelationId;
12499 conobj.objectId = con->oid;
12500 conobj.objectSubId = 0;
12502 performDeletion(&conobj, behavior, 0);
12504 found = true;
12507 systable_endscan(scan);
12509 if (!found)
12511 if (!missing_ok)
12513 ereport(ERROR,
12514 (errcode(ERRCODE_UNDEFINED_OBJECT),
12515 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12516 constrName, RelationGetRelationName(rel))));
12518 else
12520 ereport(NOTICE,
12521 (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12522 constrName, RelationGetRelationName(rel))));
12523 table_close(conrel, RowExclusiveLock);
12524 return;
12529 * For partitioned tables, non-CHECK inherited constraints are dropped via
12530 * the dependency mechanism, so we're done here.
12532 if (contype != CONSTRAINT_CHECK &&
12533 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12535 table_close(conrel, RowExclusiveLock);
12536 return;
12540 * Propagate to children as appropriate. Unlike most other ALTER
12541 * routines, we have to do this one level of recursion at a time; we can't
12542 * use find_all_inheritors to do it in one pass.
12544 if (!is_no_inherit_constraint)
12545 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12546 else
12547 children = NIL;
12550 * For a partitioned table, if partitions exist and we are told not to
12551 * recurse, it's a user error. It doesn't make sense to have a constraint
12552 * be defined only on the parent, especially if it's a partitioned table.
12554 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
12555 children != NIL && !recurse)
12556 ereport(ERROR,
12557 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12558 errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12559 errhint("Do not specify the ONLY keyword.")));
12561 foreach_oid(childrelid, children)
12563 Relation childrel;
12564 HeapTuple copy_tuple;
12566 /* find_inheritance_children already got lock */
12567 childrel = table_open(childrelid, NoLock);
12568 CheckAlterTableIsSafe(childrel);
12570 ScanKeyInit(&skey[0],
12571 Anum_pg_constraint_conrelid,
12572 BTEqualStrategyNumber, F_OIDEQ,
12573 ObjectIdGetDatum(childrelid));
12574 ScanKeyInit(&skey[1],
12575 Anum_pg_constraint_contypid,
12576 BTEqualStrategyNumber, F_OIDEQ,
12577 ObjectIdGetDatum(InvalidOid));
12578 ScanKeyInit(&skey[2],
12579 Anum_pg_constraint_conname,
12580 BTEqualStrategyNumber, F_NAMEEQ,
12581 CStringGetDatum(constrName));
12582 scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12583 true, NULL, 3, skey);
12585 /* There can be at most one matching row */
12586 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12587 ereport(ERROR,
12588 (errcode(ERRCODE_UNDEFINED_OBJECT),
12589 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12590 constrName,
12591 RelationGetRelationName(childrel))));
12593 copy_tuple = heap_copytuple(tuple);
12595 systable_endscan(scan);
12597 con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
12599 /* Right now only CHECK constraints can be inherited */
12600 if (con->contype != CONSTRAINT_CHECK)
12601 elog(ERROR, "inherited constraint is not a CHECK constraint");
12603 if (con->coninhcount <= 0) /* shouldn't happen */
12604 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
12605 childrelid, constrName);
12607 if (recurse)
12610 * If the child constraint has other definition sources, just
12611 * decrement its inheritance count; if not, recurse to delete it.
12613 if (con->coninhcount == 1 && !con->conislocal)
12615 /* Time to delete this child constraint, too */
12616 ATExecDropConstraint(childrel, constrName, behavior,
12617 true, true,
12618 false, lockmode);
12620 else
12622 /* Child constraint must survive my deletion */
12623 con->coninhcount--;
12624 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12626 /* Make update visible */
12627 CommandCounterIncrement();
12630 else
12633 * If we were told to drop ONLY in this table (no recursion), we
12634 * need to mark the inheritors' constraints as locally defined
12635 * rather than inherited.
12637 con->coninhcount--;
12638 con->conislocal = true;
12640 CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12642 /* Make update visible */
12643 CommandCounterIncrement();
12646 heap_freetuple(copy_tuple);
12648 table_close(childrel, NoLock);
12651 table_close(conrel, RowExclusiveLock);
12655 * ALTER COLUMN TYPE
12657 * Unlike other subcommand types, we do parse transformation for ALTER COLUMN
12658 * TYPE during phase 1 --- the AlterTableCmd passed in here is already
12659 * transformed (and must be, because we rely on some transformed fields).
12661 * The point of this is that the execution of all ALTER COLUMN TYPEs for a
12662 * table will be done "in parallel" during phase 3, so all the USING
12663 * expressions should be parsed assuming the original column types. Also,
12664 * this allows a USING expression to refer to a field that will be dropped.
12666 * To make this work safely, AT_PASS_DROP then AT_PASS_ALTER_TYPE must be
12667 * the first two execution steps in phase 2; they must not see the effects
12668 * of any other subcommand types, since the USING expressions are parsed
12669 * against the unmodified table's state.
12671 static void
12672 ATPrepAlterColumnType(List **wqueue,
12673 AlteredTableInfo *tab, Relation rel,
12674 bool recurse, bool recursing,
12675 AlterTableCmd *cmd, LOCKMODE lockmode,
12676 AlterTableUtilityContext *context)
12678 char *colName = cmd->name;
12679 ColumnDef *def = (ColumnDef *) cmd->def;
12680 TypeName *typeName = def->typeName;
12681 Node *transform = def->cooked_default;
12682 HeapTuple tuple;
12683 Form_pg_attribute attTup;
12684 AttrNumber attnum;
12685 Oid targettype;
12686 int32 targettypmod;
12687 Oid targetcollid;
12688 NewColumnValue *newval;
12689 ParseState *pstate = make_parsestate(NULL);
12690 AclResult aclresult;
12691 bool is_expr;
12693 if (rel->rd_rel->reloftype && !recursing)
12694 ereport(ERROR,
12695 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12696 errmsg("cannot alter column type of typed table")));
12698 /* lookup the attribute so we can check inheritance status */
12699 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12700 if (!HeapTupleIsValid(tuple))
12701 ereport(ERROR,
12702 (errcode(ERRCODE_UNDEFINED_COLUMN),
12703 errmsg("column \"%s\" of relation \"%s\" does not exist",
12704 colName, RelationGetRelationName(rel))));
12705 attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12706 attnum = attTup->attnum;
12708 /* Can't alter a system attribute */
12709 if (attnum <= 0)
12710 ereport(ERROR,
12711 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12712 errmsg("cannot alter system column \"%s\"",
12713 colName)));
12716 * Don't alter inherited columns. At outer level, there had better not be
12717 * any inherited definition; when recursing, we assume this was checked at
12718 * the parent level (see below).
12720 if (attTup->attinhcount > 0 && !recursing)
12721 ereport(ERROR,
12722 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12723 errmsg("cannot alter inherited column \"%s\"",
12724 colName)));
12726 /* Don't alter columns used in the partition key */
12727 if (has_partition_attrs(rel,
12728 bms_make_singleton(attnum - FirstLowInvalidHeapAttributeNumber),
12729 &is_expr))
12730 ereport(ERROR,
12731 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12732 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12733 colName, RelationGetRelationName(rel))));
12735 /* Look up the target type */
12736 typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
12738 aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
12739 if (aclresult != ACLCHECK_OK)
12740 aclcheck_error_type(aclresult, targettype);
12742 /* And the collation */
12743 targetcollid = GetColumnDefCollation(NULL, def, targettype);
12745 /* make sure datatype is legal for a column */
12746 CheckAttributeType(colName, targettype, targetcollid,
12747 list_make1_oid(rel->rd_rel->reltype),
12750 if (tab->relkind == RELKIND_RELATION ||
12751 tab->relkind == RELKIND_PARTITIONED_TABLE)
12754 * Set up an expression to transform the old data value to the new
12755 * type. If a USING option was given, use the expression as
12756 * transformed by transformAlterTableStmt, else just take the old
12757 * value and try to coerce it. We do this first so that type
12758 * incompatibility can be detected before we waste effort, and because
12759 * we need the expression to be parsed against the original table row
12760 * type.
12762 if (!transform)
12764 transform = (Node *) makeVar(1, attnum,
12765 attTup->atttypid, attTup->atttypmod,
12766 attTup->attcollation,
12770 transform = coerce_to_target_type(pstate,
12771 transform, exprType(transform),
12772 targettype, targettypmod,
12773 COERCION_ASSIGNMENT,
12774 COERCE_IMPLICIT_CAST,
12775 -1);
12776 if (transform == NULL)
12778 /* error text depends on whether USING was specified or not */
12779 if (def->cooked_default != NULL)
12780 ereport(ERROR,
12781 (errcode(ERRCODE_DATATYPE_MISMATCH),
12782 errmsg("result of USING clause for column \"%s\""
12783 " cannot be cast automatically to type %s",
12784 colName, format_type_be(targettype)),
12785 errhint("You might need to add an explicit cast.")));
12786 else
12787 ereport(ERROR,
12788 (errcode(ERRCODE_DATATYPE_MISMATCH),
12789 errmsg("column \"%s\" cannot be cast automatically to type %s",
12790 colName, format_type_be(targettype)),
12791 /* translator: USING is SQL, don't translate it */
12792 errhint("You might need to specify \"USING %s::%s\".",
12793 quote_identifier(colName),
12794 format_type_with_typemod(targettype,
12795 targettypmod))));
12798 /* Fix collations after all else */
12799 assign_expr_collations(pstate, transform);
12801 /* Plan the expr now so we can accurately assess the need to rewrite. */
12802 transform = (Node *) expression_planner((Expr *) transform);
12805 * Add a work queue item to make ATRewriteTable update the column
12806 * contents.
12808 newval = (NewColumnValue *) palloc0(sizeof(NewColumnValue));
12809 newval->attnum = attnum;
12810 newval->expr = (Expr *) transform;
12811 newval->is_generated = false;
12813 tab->newvals = lappend(tab->newvals, newval);
12814 if (ATColumnChangeRequiresRewrite(transform, attnum))
12815 tab->rewrite |= AT_REWRITE_COLUMN_REWRITE;
12817 else if (transform)
12818 ereport(ERROR,
12819 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12820 errmsg("\"%s\" is not a table",
12821 RelationGetRelationName(rel))));
12823 if (!RELKIND_HAS_STORAGE(tab->relkind))
12826 * For relations without storage, do this check now. Regular tables
12827 * will check it later when the table is being rewritten.
12829 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
12832 ReleaseSysCache(tuple);
12835 * Recurse manually by queueing a new command for each child, if
12836 * necessary. We cannot apply ATSimpleRecursion here because we need to
12837 * remap attribute numbers in the USING expression, if any.
12839 * If we are told not to recurse, there had better not be any child
12840 * tables; else the alter would put them out of step.
12842 if (recurse)
12844 Oid relid = RelationGetRelid(rel);
12845 List *child_oids,
12846 *child_numparents;
12847 ListCell *lo,
12848 *li;
12850 child_oids = find_all_inheritors(relid, lockmode,
12851 &child_numparents);
12854 * find_all_inheritors does the recursive search of the inheritance
12855 * hierarchy, so all we have to do is process all of the relids in the
12856 * list that it returns.
12858 forboth(lo, child_oids, li, child_numparents)
12860 Oid childrelid = lfirst_oid(lo);
12861 int numparents = lfirst_int(li);
12862 Relation childrel;
12863 HeapTuple childtuple;
12864 Form_pg_attribute childattTup;
12866 if (childrelid == relid)
12867 continue;
12869 /* find_all_inheritors already got lock */
12870 childrel = relation_open(childrelid, NoLock);
12871 CheckAlterTableIsSafe(childrel);
12874 * Verify that the child doesn't have any inherited definitions of
12875 * this column that came from outside this inheritance hierarchy.
12876 * (renameatt makes a similar test, though in a different way
12877 * because of its different recursion mechanism.)
12879 childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
12880 colName);
12881 if (!HeapTupleIsValid(childtuple))
12882 ereport(ERROR,
12883 (errcode(ERRCODE_UNDEFINED_COLUMN),
12884 errmsg("column \"%s\" of relation \"%s\" does not exist",
12885 colName, RelationGetRelationName(childrel))));
12886 childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
12888 if (childattTup->attinhcount > numparents)
12889 ereport(ERROR,
12890 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12891 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12892 colName, RelationGetRelationName(childrel))));
12894 ReleaseSysCache(childtuple);
12897 * Remap the attribute numbers. If no USING expression was
12898 * specified, there is no need for this step.
12900 if (def->cooked_default)
12902 AttrMap *attmap;
12903 bool found_whole_row;
12905 /* create a copy to scribble on */
12906 cmd = copyObject(cmd);
12908 attmap = build_attrmap_by_name(RelationGetDescr(childrel),
12909 RelationGetDescr(rel),
12910 false);
12911 ((ColumnDef *) cmd->def)->cooked_default =
12912 map_variable_attnos(def->cooked_default,
12913 1, 0,
12914 attmap,
12915 InvalidOid, &found_whole_row);
12916 if (found_whole_row)
12917 ereport(ERROR,
12918 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12919 errmsg("cannot convert whole-row table reference"),
12920 errdetail("USING expression contains a whole-row table reference.")));
12921 pfree(attmap);
12923 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
12924 relation_close(childrel, NoLock);
12927 else if (!recursing &&
12928 find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
12929 ereport(ERROR,
12930 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12931 errmsg("type of inherited column \"%s\" must be changed in child tables too",
12932 colName)));
12934 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
12935 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
12939 * When the data type of a column is changed, a rewrite might not be required
12940 * if the new type is sufficiently identical to the old one, and the USING
12941 * clause isn't trying to insert some other value. It's safe to skip the
12942 * rewrite in these cases:
12944 * - the old type is binary coercible to the new type
12945 * - the new type is an unconstrained domain over the old type
12946 * - {NEW,OLD} or {OLD,NEW} is {timestamptz,timestamp} and the timezone is UTC
12948 * In the case of a constrained domain, we could get by with scanning the
12949 * table and checking the constraint rather than actually rewriting it, but we
12950 * don't currently try to do that.
12952 static bool
12953 ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
12955 Assert(expr != NULL);
12957 for (;;)
12959 /* only one varno, so no need to check that */
12960 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
12961 return false;
12962 else if (IsA(expr, RelabelType))
12963 expr = (Node *) ((RelabelType *) expr)->arg;
12964 else if (IsA(expr, CoerceToDomain))
12966 CoerceToDomain *d = (CoerceToDomain *) expr;
12968 if (DomainHasConstraints(d->resulttype))
12969 return true;
12970 expr = (Node *) d->arg;
12972 else if (IsA(expr, FuncExpr))
12974 FuncExpr *f = (FuncExpr *) expr;
12976 switch (f->funcid)
12978 case F_TIMESTAMPTZ_TIMESTAMP:
12979 case F_TIMESTAMP_TIMESTAMPTZ:
12980 if (TimestampTimestampTzRequiresRewrite())
12981 return true;
12982 else
12983 expr = linitial(f->args);
12984 break;
12985 default:
12986 return true;
12989 else
12990 return true;
12995 * ALTER COLUMN .. SET DATA TYPE
12997 * Return the address of the modified column.
12999 static ObjectAddress
13000 ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
13001 AlterTableCmd *cmd, LOCKMODE lockmode)
13003 char *colName = cmd->name;
13004 ColumnDef *def = (ColumnDef *) cmd->def;
13005 TypeName *typeName = def->typeName;
13006 HeapTuple heapTup;
13007 Form_pg_attribute attTup,
13008 attOldTup;
13009 AttrNumber attnum;
13010 HeapTuple typeTuple;
13011 Form_pg_type tform;
13012 Oid targettype;
13013 int32 targettypmod;
13014 Oid targetcollid;
13015 Node *defaultexpr;
13016 Relation attrelation;
13017 Relation depRel;
13018 ScanKeyData key[3];
13019 SysScanDesc scan;
13020 HeapTuple depTup;
13021 ObjectAddress address;
13024 * Clear all the missing values if we're rewriting the table, since this
13025 * renders them pointless.
13027 if (tab->rewrite)
13029 Relation newrel;
13031 newrel = table_open(RelationGetRelid(rel), NoLock);
13032 RelationClearMissing(newrel);
13033 relation_close(newrel, NoLock);
13034 /* make sure we don't conflict with later attribute modifications */
13035 CommandCounterIncrement();
13038 attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13040 /* Look up the target column */
13041 heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13042 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13043 ereport(ERROR,
13044 (errcode(ERRCODE_UNDEFINED_COLUMN),
13045 errmsg("column \"%s\" of relation \"%s\" does not exist",
13046 colName, RelationGetRelationName(rel))));
13047 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13048 attnum = attTup->attnum;
13049 attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13051 /* Check for multiple ALTER TYPE on same column --- can't cope */
13052 if (attTup->atttypid != attOldTup->atttypid ||
13053 attTup->atttypmod != attOldTup->atttypmod)
13054 ereport(ERROR,
13055 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13056 errmsg("cannot alter type of column \"%s\" twice",
13057 colName)));
13059 /* Look up the target type (should not fail, since prep found it) */
13060 typeTuple = typenameType(NULL, typeName, &targettypmod);
13061 tform = (Form_pg_type) GETSTRUCT(typeTuple);
13062 targettype = tform->oid;
13063 /* And the collation */
13064 targetcollid = GetColumnDefCollation(NULL, def, targettype);
13067 * If there is a default expression for the column, get it and ensure we
13068 * can coerce it to the new datatype. (We must do this before changing
13069 * the column type, because build_column_default itself will try to
13070 * coerce, and will not issue the error message we want if it fails.)
13072 * We remove any implicit coercion steps at the top level of the old
13073 * default expression; this has been agreed to satisfy the principle of
13074 * least surprise. (The conversion to the new column type should act like
13075 * it started from what the user sees as the stored expression, and the
13076 * implicit coercions aren't going to be shown.)
13078 if (attTup->atthasdef)
13080 defaultexpr = build_column_default(rel, attnum);
13081 Assert(defaultexpr);
13082 defaultexpr = strip_implicit_coercions(defaultexpr);
13083 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13084 defaultexpr, exprType(defaultexpr),
13085 targettype, targettypmod,
13086 COERCION_ASSIGNMENT,
13087 COERCE_IMPLICIT_CAST,
13088 -1);
13089 if (defaultexpr == NULL)
13091 if (attTup->attgenerated)
13092 ereport(ERROR,
13093 (errcode(ERRCODE_DATATYPE_MISMATCH),
13094 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13095 colName, format_type_be(targettype))));
13096 else
13097 ereport(ERROR,
13098 (errcode(ERRCODE_DATATYPE_MISMATCH),
13099 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13100 colName, format_type_be(targettype))));
13103 else
13104 defaultexpr = NULL;
13107 * Find everything that depends on the column (constraints, indexes, etc),
13108 * and record enough information to let us recreate the objects.
13110 * The actual recreation does not happen here, but only after we have
13111 * performed all the individual ALTER TYPE operations. We have to save
13112 * the info before executing ALTER TYPE, though, else the deparser will
13113 * get confused.
13115 RememberAllDependentForRebuilding(tab, AT_AlterColumnType, rel, attnum, colName);
13118 * Now scan for dependencies of this column on other things. The only
13119 * things we should find are the dependency on the column datatype and
13120 * possibly a collation dependency. Those can be removed.
13122 depRel = table_open(DependRelationId, RowExclusiveLock);
13124 ScanKeyInit(&key[0],
13125 Anum_pg_depend_classid,
13126 BTEqualStrategyNumber, F_OIDEQ,
13127 ObjectIdGetDatum(RelationRelationId));
13128 ScanKeyInit(&key[1],
13129 Anum_pg_depend_objid,
13130 BTEqualStrategyNumber, F_OIDEQ,
13131 ObjectIdGetDatum(RelationGetRelid(rel)));
13132 ScanKeyInit(&key[2],
13133 Anum_pg_depend_objsubid,
13134 BTEqualStrategyNumber, F_INT4EQ,
13135 Int32GetDatum((int32) attnum));
13137 scan = systable_beginscan(depRel, DependDependerIndexId, true,
13138 NULL, 3, key);
13140 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13142 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13143 ObjectAddress foundObject;
13145 foundObject.classId = foundDep->refclassid;
13146 foundObject.objectId = foundDep->refobjid;
13147 foundObject.objectSubId = foundDep->refobjsubid;
13149 if (foundDep->deptype != DEPENDENCY_NORMAL)
13150 elog(ERROR, "found unexpected dependency type '%c'",
13151 foundDep->deptype);
13152 if (!(foundDep->refclassid == TypeRelationId &&
13153 foundDep->refobjid == attTup->atttypid) &&
13154 !(foundDep->refclassid == CollationRelationId &&
13155 foundDep->refobjid == attTup->attcollation))
13156 elog(ERROR, "found unexpected dependency for column: %s",
13157 getObjectDescription(&foundObject, false));
13159 CatalogTupleDelete(depRel, &depTup->t_self);
13162 systable_endscan(scan);
13164 table_close(depRel, RowExclusiveLock);
13167 * Here we go --- change the recorded column type and collation. (Note
13168 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13169 * fix up the missing value if any.
13171 if (attTup->atthasmissing)
13173 Datum missingval;
13174 bool missingNull;
13176 /* if rewrite is true the missing value should already be cleared */
13177 Assert(tab->rewrite == 0);
13179 /* Get the missing value datum */
13180 missingval = heap_getattr(heapTup,
13181 Anum_pg_attribute_attmissingval,
13182 attrelation->rd_att,
13183 &missingNull);
13185 /* if it's a null array there is nothing to do */
13187 if (!missingNull)
13190 * Get the datum out of the array and repack it in a new array
13191 * built with the new type data. We assume that since the table
13192 * doesn't need rewriting, the actual Datum doesn't need to be
13193 * changed, only the array metadata.
13196 int one = 1;
13197 bool isNull;
13198 Datum valuesAtt[Natts_pg_attribute] = {0};
13199 bool nullsAtt[Natts_pg_attribute] = {0};
13200 bool replacesAtt[Natts_pg_attribute] = {0};
13201 HeapTuple newTup;
13203 missingval = array_get_element(missingval,
13205 &one,
13207 attTup->attlen,
13208 attTup->attbyval,
13209 attTup->attalign,
13210 &isNull);
13211 missingval = PointerGetDatum(construct_array(&missingval,
13213 targettype,
13214 tform->typlen,
13215 tform->typbyval,
13216 tform->typalign));
13218 valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13219 replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13220 nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13222 newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13223 valuesAtt, nullsAtt, replacesAtt);
13224 heap_freetuple(heapTup);
13225 heapTup = newTup;
13226 attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13230 attTup->atttypid = targettype;
13231 attTup->atttypmod = targettypmod;
13232 attTup->attcollation = targetcollid;
13233 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13234 ereport(ERROR,
13235 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13236 errmsg("too many array dimensions"));
13237 attTup->attndims = list_length(typeName->arrayBounds);
13238 attTup->attlen = tform->typlen;
13239 attTup->attbyval = tform->typbyval;
13240 attTup->attalign = tform->typalign;
13241 attTup->attstorage = tform->typstorage;
13242 attTup->attcompression = InvalidCompressionMethod;
13244 ReleaseSysCache(typeTuple);
13246 CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13248 table_close(attrelation, RowExclusiveLock);
13250 /* Install dependencies on new datatype and collation */
13251 add_column_datatype_dependency(RelationGetRelid(rel), attnum, targettype);
13252 add_column_collation_dependency(RelationGetRelid(rel), attnum, targetcollid);
13255 * Drop any pg_statistic entry for the column, since it's now wrong type
13257 RemoveStatistics(RelationGetRelid(rel), attnum);
13259 InvokeObjectPostAlterHook(RelationRelationId,
13260 RelationGetRelid(rel), attnum);
13263 * Update the default, if present, by brute force --- remove and re-add
13264 * the default. Probably unsafe to take shortcuts, since the new version
13265 * may well have additional dependencies. (It's okay to do this now,
13266 * rather than after other ALTER TYPE commands, since the default won't
13267 * depend on other column types.)
13269 if (defaultexpr)
13272 * If it's a GENERATED default, drop its dependency records, in
13273 * particular its INTERNAL dependency on the column, which would
13274 * otherwise cause dependency.c to refuse to perform the deletion.
13276 if (attTup->attgenerated)
13278 Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13280 if (!OidIsValid(attrdefoid))
13281 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13282 RelationGetRelid(rel), attnum);
13283 (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13287 * Make updates-so-far visible, particularly the new pg_attribute row
13288 * which will be updated again.
13290 CommandCounterIncrement();
13293 * We use RESTRICT here for safety, but at present we do not expect
13294 * anything to depend on the default.
13296 RemoveAttrDefault(RelationGetRelid(rel), attnum, DROP_RESTRICT, true,
13297 true);
13299 StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13302 ObjectAddressSubSet(address, RelationRelationId,
13303 RelationGetRelid(rel), attnum);
13305 /* Cleanup */
13306 heap_freetuple(heapTup);
13308 return address;
13312 * Subroutine for ATExecAlterColumnType and ATExecSetExpression: Find everything
13313 * that depends on the column (constraints, indexes, etc), and record enough
13314 * information to let us recreate the objects.
13316 static void
13317 RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype,
13318 Relation rel, AttrNumber attnum, const char *colName)
13320 Relation depRel;
13321 ScanKeyData key[3];
13322 SysScanDesc scan;
13323 HeapTuple depTup;
13325 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13327 depRel = table_open(DependRelationId, RowExclusiveLock);
13329 ScanKeyInit(&key[0],
13330 Anum_pg_depend_refclassid,
13331 BTEqualStrategyNumber, F_OIDEQ,
13332 ObjectIdGetDatum(RelationRelationId));
13333 ScanKeyInit(&key[1],
13334 Anum_pg_depend_refobjid,
13335 BTEqualStrategyNumber, F_OIDEQ,
13336 ObjectIdGetDatum(RelationGetRelid(rel)));
13337 ScanKeyInit(&key[2],
13338 Anum_pg_depend_refobjsubid,
13339 BTEqualStrategyNumber, F_INT4EQ,
13340 Int32GetDatum((int32) attnum));
13342 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13343 NULL, 3, key);
13345 while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13347 Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13348 ObjectAddress foundObject;
13350 foundObject.classId = foundDep->classid;
13351 foundObject.objectId = foundDep->objid;
13352 foundObject.objectSubId = foundDep->objsubid;
13354 switch (foundObject.classId)
13356 case RelationRelationId:
13358 char relKind = get_rel_relkind(foundObject.objectId);
13360 if (relKind == RELKIND_INDEX ||
13361 relKind == RELKIND_PARTITIONED_INDEX)
13363 Assert(foundObject.objectSubId == 0);
13364 RememberIndexForRebuilding(foundObject.objectId, tab);
13366 else if (relKind == RELKIND_SEQUENCE)
13369 * This must be a SERIAL column's sequence. We need
13370 * not do anything to it.
13372 Assert(foundObject.objectSubId == 0);
13374 else
13376 /* Not expecting any other direct dependencies... */
13377 elog(ERROR, "unexpected object depending on column: %s",
13378 getObjectDescription(&foundObject, false));
13380 break;
13383 case ConstraintRelationId:
13384 Assert(foundObject.objectSubId == 0);
13385 RememberConstraintForRebuilding(foundObject.objectId, tab);
13386 break;
13388 case ProcedureRelationId:
13391 * A new-style SQL function can depend on a column, if that
13392 * column is referenced in the parsed function body. Ideally
13393 * we'd automatically update the function by deparsing and
13394 * reparsing it, but that's risky and might well fail anyhow.
13395 * FIXME someday.
13397 * This is only a problem for AT_AlterColumnType, not
13398 * AT_SetExpression.
13400 if (subtype == AT_AlterColumnType)
13401 ereport(ERROR,
13402 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13403 errmsg("cannot alter type of a column used by a function or procedure"),
13404 errdetail("%s depends on column \"%s\"",
13405 getObjectDescription(&foundObject, false),
13406 colName)));
13407 break;
13409 case RewriteRelationId:
13412 * View/rule bodies have pretty much the same issues as
13413 * function bodies. FIXME someday.
13415 if (subtype == AT_AlterColumnType)
13416 ereport(ERROR,
13417 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13418 errmsg("cannot alter type of a column used by a view or rule"),
13419 errdetail("%s depends on column \"%s\"",
13420 getObjectDescription(&foundObject, false),
13421 colName)));
13422 break;
13424 case TriggerRelationId:
13427 * A trigger can depend on a column because the column is
13428 * specified as an update target, or because the column is
13429 * used in the trigger's WHEN condition. The first case would
13430 * not require any extra work, but the second case would
13431 * require updating the WHEN expression, which has the same
13432 * issues as above. Since we can't easily tell which case
13433 * applies, we punt for both. FIXME someday.
13435 if (subtype == AT_AlterColumnType)
13436 ereport(ERROR,
13437 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13438 errmsg("cannot alter type of a column used in a trigger definition"),
13439 errdetail("%s depends on column \"%s\"",
13440 getObjectDescription(&foundObject, false),
13441 colName)));
13442 break;
13444 case PolicyRelationId:
13447 * A policy can depend on a column because the column is
13448 * specified in the policy's USING or WITH CHECK qual
13449 * expressions. It might be possible to rewrite and recheck
13450 * the policy expression, but punt for now. It's certainly
13451 * easy enough to remove and recreate the policy; still, FIXME
13452 * someday.
13454 if (subtype == AT_AlterColumnType)
13455 ereport(ERROR,
13456 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13457 errmsg("cannot alter type of a column used in a policy definition"),
13458 errdetail("%s depends on column \"%s\"",
13459 getObjectDescription(&foundObject, false),
13460 colName)));
13461 break;
13463 case AttrDefaultRelationId:
13465 ObjectAddress col = GetAttrDefaultColumnAddress(foundObject.objectId);
13467 if (col.objectId == RelationGetRelid(rel) &&
13468 col.objectSubId == attnum)
13471 * Ignore the column's own default expression. The
13472 * caller deals with it.
13475 else
13478 * This must be a reference from the expression of a
13479 * generated column elsewhere in the same table.
13480 * Changing the type/generated expression of a column
13481 * that is used by a generated column is not allowed
13482 * by SQL standard, so just punt for now. It might be
13483 * doable with some thinking and effort.
13485 if (subtype == AT_AlterColumnType)
13486 ereport(ERROR,
13487 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13488 errmsg("cannot alter type of a column used by a generated column"),
13489 errdetail("Column \"%s\" is used by generated column \"%s\".",
13490 colName,
13491 get_attname(col.objectId,
13492 col.objectSubId,
13493 false))));
13495 break;
13498 case StatisticExtRelationId:
13501 * Give the extended-stats machinery a chance to fix anything
13502 * that this column type change would break.
13504 RememberStatisticsForRebuilding(foundObject.objectId, tab);
13505 break;
13507 case PublicationRelRelationId:
13510 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
13511 * clause. Same issues as above. FIXME someday.
13513 if (subtype == AT_AlterColumnType)
13514 ereport(ERROR,
13515 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13516 errmsg("cannot alter type of a column used by a publication WHERE clause"),
13517 errdetail("%s depends on column \"%s\"",
13518 getObjectDescription(&foundObject, false),
13519 colName)));
13520 break;
13522 default:
13525 * We don't expect any other sorts of objects to depend on a
13526 * column.
13528 elog(ERROR, "unexpected object depending on column: %s",
13529 getObjectDescription(&foundObject, false));
13530 break;
13534 systable_endscan(scan);
13535 table_close(depRel, NoLock);
13539 * Subroutine for ATExecAlterColumnType: remember that a replica identity
13540 * needs to be reset.
13542 static void
13543 RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
13545 if (!get_index_isreplident(indoid))
13546 return;
13548 if (tab->replicaIdentityIndex)
13549 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
13551 tab->replicaIdentityIndex = get_rel_name(indoid);
13555 * Subroutine for ATExecAlterColumnType: remember any clustered index.
13557 static void
13558 RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
13560 if (!get_index_isclustered(indoid))
13561 return;
13563 if (tab->clusterOnIndex)
13564 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13566 tab->clusterOnIndex = get_rel_name(indoid);
13570 * Subroutine for ATExecAlterColumnType: remember that a constraint needs
13571 * to be rebuilt (which we might already know).
13573 static void
13574 RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
13577 * This de-duplication check is critical for two independent reasons: we
13578 * mustn't try to recreate the same constraint twice, and if a constraint
13579 * depends on more than one column whose type is to be altered, we must
13580 * capture its definition string before applying any of the column type
13581 * changes. ruleutils.c will get confused if we ask again later.
13583 if (!list_member_oid(tab->changedConstraintOids, conoid))
13585 /* OK, capture the constraint's existing definition string */
13586 char *defstring = pg_get_constraintdef_command(conoid);
13587 Oid indoid;
13589 tab->changedConstraintOids = lappend_oid(tab->changedConstraintOids,
13590 conoid);
13591 tab->changedConstraintDefs = lappend(tab->changedConstraintDefs,
13592 defstring);
13595 * For the index of a constraint, if any, remember if it is used for
13596 * the table's replica identity or if it is a clustered index, so that
13597 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13598 * those properties.
13600 indoid = get_constraint_index(conoid);
13601 if (OidIsValid(indoid))
13603 RememberReplicaIdentityForRebuilding(indoid, tab);
13604 RememberClusterOnForRebuilding(indoid, tab);
13610 * Subroutine for ATExecAlterColumnType: remember that an index needs
13611 * to be rebuilt (which we might already know).
13613 static void
13614 RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
13617 * This de-duplication check is critical for two independent reasons: we
13618 * mustn't try to recreate the same index twice, and if an index depends
13619 * on more than one column whose type is to be altered, we must capture
13620 * its definition string before applying any of the column type changes.
13621 * ruleutils.c will get confused if we ask again later.
13623 if (!list_member_oid(tab->changedIndexOids, indoid))
13626 * Before adding it as an index-to-rebuild, we'd better see if it
13627 * belongs to a constraint, and if so rebuild the constraint instead.
13628 * Typically this check fails, because constraint indexes normally
13629 * have only dependencies on their constraint. But it's possible for
13630 * such an index to also have direct dependencies on table columns,
13631 * for example with a partial exclusion constraint.
13633 Oid conoid = get_index_constraint(indoid);
13635 if (OidIsValid(conoid))
13637 RememberConstraintForRebuilding(conoid, tab);
13639 else
13641 /* OK, capture the index's existing definition string */
13642 char *defstring = pg_get_indexdef_string(indoid);
13644 tab->changedIndexOids = lappend_oid(tab->changedIndexOids,
13645 indoid);
13646 tab->changedIndexDefs = lappend(tab->changedIndexDefs,
13647 defstring);
13650 * Remember if this index is used for the table's replica identity
13651 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13652 * can queue up commands necessary to restore those properties.
13654 RememberReplicaIdentityForRebuilding(indoid, tab);
13655 RememberClusterOnForRebuilding(indoid, tab);
13661 * Subroutine for ATExecAlterColumnType: remember that a statistics object
13662 * needs to be rebuilt (which we might already know).
13664 static void
13665 RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
13668 * This de-duplication check is critical for two independent reasons: we
13669 * mustn't try to recreate the same statistics object twice, and if the
13670 * statistics object depends on more than one column whose type is to be
13671 * altered, we must capture its definition string before applying any of
13672 * the type changes. ruleutils.c will get confused if we ask again later.
13674 if (!list_member_oid(tab->changedStatisticsOids, stxoid))
13676 /* OK, capture the statistics object's existing definition string */
13677 char *defstring = pg_get_statisticsobjdef_string(stxoid);
13679 tab->changedStatisticsOids = lappend_oid(tab->changedStatisticsOids,
13680 stxoid);
13681 tab->changedStatisticsDefs = lappend(tab->changedStatisticsDefs,
13682 defstring);
13687 * Cleanup after we've finished all the ALTER TYPE or SET EXPRESSION
13688 * operations for a particular relation. We have to drop and recreate all the
13689 * indexes and constraints that depend on the altered columns. We do the
13690 * actual dropping here, but re-creation is managed by adding work queue
13691 * entries to do those steps later.
13693 static void
13694 ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
13696 ObjectAddress obj;
13697 ObjectAddresses *objects;
13698 ListCell *def_item;
13699 ListCell *oid_item;
13702 * Collect all the constraints and indexes to drop so we can process them
13703 * in a single call. That way we don't have to worry about dependencies
13704 * among them.
13706 objects = new_object_addresses();
13709 * Re-parse the index and constraint definitions, and attach them to the
13710 * appropriate work queue entries. We do this before dropping because in
13711 * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13712 * lock on the table the constraint is attached to, and we need to get
13713 * that before reparsing/dropping.
13715 * We can't rely on the output of deparsing to tell us which relation to
13716 * operate on, because concurrent activity might have made the name
13717 * resolve differently. Instead, we've got to use the OID of the
13718 * constraint or index we're processing to figure out which relation to
13719 * operate on.
13721 forboth(oid_item, tab->changedConstraintOids,
13722 def_item, tab->changedConstraintDefs)
13724 Oid oldId = lfirst_oid(oid_item);
13725 HeapTuple tup;
13726 Form_pg_constraint con;
13727 Oid relid;
13728 Oid confrelid;
13729 char contype;
13730 bool conislocal;
13732 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13733 if (!HeapTupleIsValid(tup)) /* should not happen */
13734 elog(ERROR, "cache lookup failed for constraint %u", oldId);
13735 con = (Form_pg_constraint) GETSTRUCT(tup);
13736 if (OidIsValid(con->conrelid))
13737 relid = con->conrelid;
13738 else
13740 /* must be a domain constraint */
13741 relid = get_typ_typrelid(getBaseType(con->contypid));
13742 if (!OidIsValid(relid))
13743 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13745 confrelid = con->confrelid;
13746 contype = con->contype;
13747 conislocal = con->conislocal;
13748 ReleaseSysCache(tup);
13750 ObjectAddressSet(obj, ConstraintRelationId, oldId);
13751 add_exact_object_address(&obj, objects);
13754 * If the constraint is inherited (only), we don't want to inject a
13755 * new definition here; it'll get recreated when ATAddCheckConstraint
13756 * recurses from adding the parent table's constraint. But we had to
13757 * carry the info this far so that we can drop the constraint below.
13759 if (!conislocal)
13760 continue;
13763 * When rebuilding an FK constraint that references the table we're
13764 * modifying, we might not yet have any lock on the FK's table, so get
13765 * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13766 * step, so there's no value in asking for anything weaker.
13768 if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
13769 LockRelationOid(relid, AccessExclusiveLock);
13771 ATPostAlterTypeParse(oldId, relid, confrelid,
13772 (char *) lfirst(def_item),
13773 wqueue, lockmode, tab->rewrite);
13775 forboth(oid_item, tab->changedIndexOids,
13776 def_item, tab->changedIndexDefs)
13778 Oid oldId = lfirst_oid(oid_item);
13779 Oid relid;
13781 relid = IndexGetRelation(oldId, false);
13782 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13783 (char *) lfirst(def_item),
13784 wqueue, lockmode, tab->rewrite);
13786 ObjectAddressSet(obj, RelationRelationId, oldId);
13787 add_exact_object_address(&obj, objects);
13790 /* add dependencies for new statistics */
13791 forboth(oid_item, tab->changedStatisticsOids,
13792 def_item, tab->changedStatisticsDefs)
13794 Oid oldId = lfirst_oid(oid_item);
13795 Oid relid;
13797 relid = StatisticsGetRelation(oldId, false);
13798 ATPostAlterTypeParse(oldId, relid, InvalidOid,
13799 (char *) lfirst(def_item),
13800 wqueue, lockmode, tab->rewrite);
13802 ObjectAddressSet(obj, StatisticExtRelationId, oldId);
13803 add_exact_object_address(&obj, objects);
13807 * Queue up command to restore replica identity index marking
13809 if (tab->replicaIdentityIndex)
13811 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13812 ReplicaIdentityStmt *subcmd = makeNode(ReplicaIdentityStmt);
13814 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13815 subcmd->name = tab->replicaIdentityIndex;
13816 cmd->subtype = AT_ReplicaIdentity;
13817 cmd->def = (Node *) subcmd;
13819 /* do it after indexes and constraints */
13820 tab->subcmds[AT_PASS_OLD_CONSTR] =
13821 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13825 * Queue up command to restore marking of index used for cluster.
13827 if (tab->clusterOnIndex)
13829 AlterTableCmd *cmd = makeNode(AlterTableCmd);
13831 cmd->subtype = AT_ClusterOn;
13832 cmd->name = tab->clusterOnIndex;
13834 /* do it after indexes and constraints */
13835 tab->subcmds[AT_PASS_OLD_CONSTR] =
13836 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13840 * It should be okay to use DROP_RESTRICT here, since nothing else should
13841 * be depending on these objects.
13843 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
13845 free_object_addresses(objects);
13848 * The objects will get recreated during subsequent passes over the work
13849 * queue.
13854 * Parse the previously-saved definition string for a constraint, index or
13855 * statistics object against the newly-established column data type(s), and
13856 * queue up the resulting command parsetrees for execution.
13858 * This might fail if, for example, you have a WHERE clause that uses an
13859 * operator that's not available for the new column type.
13861 static void
13862 ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
13863 List **wqueue, LOCKMODE lockmode, bool rewrite)
13865 List *raw_parsetree_list;
13866 List *querytree_list;
13867 ListCell *list_item;
13868 Relation rel;
13871 * We expect that we will get only ALTER TABLE and CREATE INDEX
13872 * statements. Hence, there is no need to pass them through
13873 * parse_analyze_*() or the rewriter, but instead we need to pass them
13874 * through parse_utilcmd.c to make them ready for execution.
13876 raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
13877 querytree_list = NIL;
13878 foreach(list_item, raw_parsetree_list)
13880 RawStmt *rs = lfirst_node(RawStmt, list_item);
13881 Node *stmt = rs->stmt;
13883 if (IsA(stmt, IndexStmt))
13884 querytree_list = lappend(querytree_list,
13885 transformIndexStmt(oldRelId,
13886 (IndexStmt *) stmt,
13887 cmd));
13888 else if (IsA(stmt, AlterTableStmt))
13890 List *beforeStmts;
13891 List *afterStmts;
13893 stmt = (Node *) transformAlterTableStmt(oldRelId,
13894 (AlterTableStmt *) stmt,
13895 cmd,
13896 &beforeStmts,
13897 &afterStmts);
13898 querytree_list = list_concat(querytree_list, beforeStmts);
13899 querytree_list = lappend(querytree_list, stmt);
13900 querytree_list = list_concat(querytree_list, afterStmts);
13902 else if (IsA(stmt, CreateStatsStmt))
13903 querytree_list = lappend(querytree_list,
13904 transformStatsStmt(oldRelId,
13905 (CreateStatsStmt *) stmt,
13906 cmd));
13907 else
13908 querytree_list = lappend(querytree_list, stmt);
13911 /* Caller should already have acquired whatever lock we need. */
13912 rel = relation_open(oldRelId, NoLock);
13915 * Attach each generated command to the proper place in the work queue.
13916 * Note this could result in creation of entirely new work-queue entries.
13918 * Also note that we have to tweak the command subtypes, because it turns
13919 * out that re-creation of indexes and constraints has to act a bit
13920 * differently from initial creation.
13922 foreach(list_item, querytree_list)
13924 Node *stm = (Node *) lfirst(list_item);
13925 AlteredTableInfo *tab;
13927 tab = ATGetQueueEntry(wqueue, rel);
13929 if (IsA(stm, IndexStmt))
13931 IndexStmt *stmt = (IndexStmt *) stm;
13932 AlterTableCmd *newcmd;
13934 if (!rewrite)
13935 TryReuseIndex(oldId, stmt);
13936 stmt->reset_default_tblspc = true;
13937 /* keep the index's comment */
13938 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
13940 newcmd = makeNode(AlterTableCmd);
13941 newcmd->subtype = AT_ReAddIndex;
13942 newcmd->def = (Node *) stmt;
13943 tab->subcmds[AT_PASS_OLD_INDEX] =
13944 lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
13946 else if (IsA(stm, AlterTableStmt))
13948 AlterTableStmt *stmt = (AlterTableStmt *) stm;
13949 ListCell *lcmd;
13951 foreach(lcmd, stmt->cmds)
13953 AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
13955 if (cmd->subtype == AT_AddIndex)
13957 IndexStmt *indstmt;
13958 Oid indoid;
13960 indstmt = castNode(IndexStmt, cmd->def);
13961 indoid = get_constraint_index(oldId);
13963 if (!rewrite)
13964 TryReuseIndex(indoid, indstmt);
13965 /* keep any comment on the index */
13966 indstmt->idxcomment = GetComment(indoid,
13967 RelationRelationId, 0);
13968 indstmt->reset_default_tblspc = true;
13970 cmd->subtype = AT_ReAddIndex;
13971 tab->subcmds[AT_PASS_OLD_INDEX] =
13972 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
13974 /* recreate any comment on the constraint */
13975 RebuildConstraintComment(tab,
13976 AT_PASS_OLD_INDEX,
13977 oldId,
13978 rel,
13979 NIL,
13980 indstmt->idxname);
13982 else if (cmd->subtype == AT_AddConstraint)
13984 Constraint *con = castNode(Constraint, cmd->def);
13986 con->old_pktable_oid = refRelId;
13987 /* rewriting neither side of a FK */
13988 if (con->contype == CONSTR_FOREIGN &&
13989 !rewrite && tab->rewrite == 0)
13990 TryReuseForeignKey(oldId, con);
13991 con->reset_default_tblspc = true;
13992 cmd->subtype = AT_ReAddConstraint;
13993 tab->subcmds[AT_PASS_OLD_CONSTR] =
13994 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13996 /* recreate any comment on the constraint */
13997 RebuildConstraintComment(tab,
13998 AT_PASS_OLD_CONSTR,
13999 oldId,
14000 rel,
14001 NIL,
14002 con->conname);
14004 else if (cmd->subtype == AT_SetNotNull)
14007 * The parser will create AT_SetNotNull subcommands for
14008 * columns of PRIMARY KEY indexes/constraints, but we need
14009 * not do anything with them here, because the columns'
14010 * NOT NULL marks will already have been propagated into
14011 * the new table definition.
14014 else
14015 elog(ERROR, "unexpected statement subtype: %d",
14016 (int) cmd->subtype);
14019 else if (IsA(stm, AlterDomainStmt))
14021 AlterDomainStmt *stmt = (AlterDomainStmt *) stm;
14023 if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14025 Constraint *con = castNode(Constraint, stmt->def);
14026 AlterTableCmd *cmd = makeNode(AlterTableCmd);
14028 cmd->subtype = AT_ReAddDomainConstraint;
14029 cmd->def = (Node *) stmt;
14030 tab->subcmds[AT_PASS_OLD_CONSTR] =
14031 lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14033 /* recreate any comment on the constraint */
14034 RebuildConstraintComment(tab,
14035 AT_PASS_OLD_CONSTR,
14036 oldId,
14037 NULL,
14038 stmt->typeName,
14039 con->conname);
14041 else
14042 elog(ERROR, "unexpected statement subtype: %d",
14043 (int) stmt->subtype);
14045 else if (IsA(stm, CreateStatsStmt))
14047 CreateStatsStmt *stmt = (CreateStatsStmt *) stm;
14048 AlterTableCmd *newcmd;
14050 /* keep the statistics object's comment */
14051 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14053 newcmd = makeNode(AlterTableCmd);
14054 newcmd->subtype = AT_ReAddStatistics;
14055 newcmd->def = (Node *) stmt;
14056 tab->subcmds[AT_PASS_MISC] =
14057 lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14059 else
14060 elog(ERROR, "unexpected statement type: %d",
14061 (int) nodeTag(stm));
14064 relation_close(rel, NoLock);
14068 * Subroutine for ATPostAlterTypeParse() to recreate any existing comment
14069 * for a table or domain constraint that is being rebuilt.
14071 * objid is the OID of the constraint.
14072 * Pass "rel" for a table constraint, or "domname" (domain's qualified name
14073 * as a string list) for a domain constraint.
14074 * (We could dig that info, as well as the conname, out of the pg_constraint
14075 * entry; but callers already have them so might as well pass them.)
14077 static void
14078 RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid,
14079 Relation rel, List *domname,
14080 const char *conname)
14082 CommentStmt *cmd;
14083 char *comment_str;
14084 AlterTableCmd *newcmd;
14086 /* Look for comment for object wanted, and leave if none */
14087 comment_str = GetComment(objid, ConstraintRelationId, 0);
14088 if (comment_str == NULL)
14089 return;
14091 /* Build CommentStmt node, copying all input data for safety */
14092 cmd = makeNode(CommentStmt);
14093 if (rel)
14095 cmd->objtype = OBJECT_TABCONSTRAINT;
14096 cmd->object = (Node *)
14097 list_make3(makeString(get_namespace_name(RelationGetNamespace(rel))),
14098 makeString(pstrdup(RelationGetRelationName(rel))),
14099 makeString(pstrdup(conname)));
14101 else
14103 cmd->objtype = OBJECT_DOMCONSTRAINT;
14104 cmd->object = (Node *)
14105 list_make2(makeTypeNameFromNameList(copyObject(domname)),
14106 makeString(pstrdup(conname)));
14108 cmd->comment = comment_str;
14110 /* Append it to list of commands */
14111 newcmd = makeNode(AlterTableCmd);
14112 newcmd->subtype = AT_ReAddComment;
14113 newcmd->def = (Node *) cmd;
14114 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14118 * Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
14119 * for the real analysis, then mutates the IndexStmt based on that verdict.
14121 static void
14122 TryReuseIndex(Oid oldId, IndexStmt *stmt)
14124 if (CheckIndexCompatible(oldId,
14125 stmt->accessMethod,
14126 stmt->indexParams,
14127 stmt->excludeOpNames))
14129 Relation irel = index_open(oldId, NoLock);
14131 /* If it's a partitioned index, there is no storage to share. */
14132 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14134 stmt->oldNumber = irel->rd_locator.relNumber;
14135 stmt->oldCreateSubid = irel->rd_createSubid;
14136 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14138 index_close(irel, NoLock);
14143 * Subroutine for ATPostAlterTypeParse().
14145 * Stash the old P-F equality operator into the Constraint node, for possible
14146 * use by ATAddForeignKeyConstraint() in determining whether revalidation of
14147 * this constraint can be skipped.
14149 static void
14150 TryReuseForeignKey(Oid oldId, Constraint *con)
14152 HeapTuple tup;
14153 Datum adatum;
14154 ArrayType *arr;
14155 Oid *rawarr;
14156 int numkeys;
14157 int i;
14159 Assert(con->contype == CONSTR_FOREIGN);
14160 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14162 tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14163 if (!HeapTupleIsValid(tup)) /* should not happen */
14164 elog(ERROR, "cache lookup failed for constraint %u", oldId);
14166 adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14167 Anum_pg_constraint_conpfeqop);
14168 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14169 numkeys = ARR_DIMS(arr)[0];
14170 /* test follows the one in ri_FetchConstraintInfo() */
14171 if (ARR_NDIM(arr) != 1 ||
14172 ARR_HASNULL(arr) ||
14173 ARR_ELEMTYPE(arr) != OIDOID)
14174 elog(ERROR, "conpfeqop is not a 1-D Oid array");
14175 rawarr = (Oid *) ARR_DATA_PTR(arr);
14177 /* stash a List of the operator Oids in our Constraint node */
14178 for (i = 0; i < numkeys; i++)
14179 con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14181 ReleaseSysCache(tup);
14185 * ALTER COLUMN .. OPTIONS ( ... )
14187 * Returns the address of the modified column
14189 static ObjectAddress
14190 ATExecAlterColumnGenericOptions(Relation rel,
14191 const char *colName,
14192 List *options,
14193 LOCKMODE lockmode)
14195 Relation ftrel;
14196 Relation attrel;
14197 ForeignServer *server;
14198 ForeignDataWrapper *fdw;
14199 HeapTuple tuple;
14200 HeapTuple newtuple;
14201 bool isnull;
14202 Datum repl_val[Natts_pg_attribute];
14203 bool repl_null[Natts_pg_attribute];
14204 bool repl_repl[Natts_pg_attribute];
14205 Datum datum;
14206 Form_pg_foreign_table fttableform;
14207 Form_pg_attribute atttableform;
14208 AttrNumber attnum;
14209 ObjectAddress address;
14211 if (options == NIL)
14212 return InvalidObjectAddress;
14214 /* First, determine FDW validator associated to the foreign table. */
14215 ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14216 tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14217 if (!HeapTupleIsValid(tuple))
14218 ereport(ERROR,
14219 (errcode(ERRCODE_UNDEFINED_OBJECT),
14220 errmsg("foreign table \"%s\" does not exist",
14221 RelationGetRelationName(rel))));
14222 fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14223 server = GetForeignServer(fttableform->ftserver);
14224 fdw = GetForeignDataWrapper(server->fdwid);
14226 table_close(ftrel, AccessShareLock);
14227 ReleaseSysCache(tuple);
14229 attrel = table_open(AttributeRelationId, RowExclusiveLock);
14230 tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14231 if (!HeapTupleIsValid(tuple))
14232 ereport(ERROR,
14233 (errcode(ERRCODE_UNDEFINED_COLUMN),
14234 errmsg("column \"%s\" of relation \"%s\" does not exist",
14235 colName, RelationGetRelationName(rel))));
14237 /* Prevent them from altering a system attribute */
14238 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14239 attnum = atttableform->attnum;
14240 if (attnum <= 0)
14241 ereport(ERROR,
14242 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14243 errmsg("cannot alter system column \"%s\"", colName)));
14246 /* Initialize buffers for new tuple values */
14247 memset(repl_val, 0, sizeof(repl_val));
14248 memset(repl_null, false, sizeof(repl_null));
14249 memset(repl_repl, false, sizeof(repl_repl));
14251 /* Extract the current options */
14252 datum = SysCacheGetAttr(ATTNAME,
14253 tuple,
14254 Anum_pg_attribute_attfdwoptions,
14255 &isnull);
14256 if (isnull)
14257 datum = PointerGetDatum(NULL);
14259 /* Transform the options */
14260 datum = transformGenericOptions(AttributeRelationId,
14261 datum,
14262 options,
14263 fdw->fdwvalidator);
14265 if (PointerIsValid(DatumGetPointer(datum)))
14266 repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14267 else
14268 repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14270 repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14272 /* Everything looks good - update the tuple */
14274 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14275 repl_val, repl_null, repl_repl);
14277 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14279 InvokeObjectPostAlterHook(RelationRelationId,
14280 RelationGetRelid(rel),
14281 atttableform->attnum);
14282 ObjectAddressSubSet(address, RelationRelationId,
14283 RelationGetRelid(rel), attnum);
14285 ReleaseSysCache(tuple);
14287 table_close(attrel, RowExclusiveLock);
14289 heap_freetuple(newtuple);
14291 return address;
14295 * ALTER TABLE OWNER
14297 * recursing is true if we are recursing from a table to its indexes,
14298 * sequences, or toast table. We don't allow the ownership of those things to
14299 * be changed separately from the parent table. Also, we can skip permission
14300 * checks (this is necessary not just an optimization, else we'd fail to
14301 * handle toast tables properly).
14303 * recursing is also true if ALTER TYPE OWNER is calling us to fix up a
14304 * free-standing composite type.
14306 void
14307 ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
14309 Relation target_rel;
14310 Relation class_rel;
14311 HeapTuple tuple;
14312 Form_pg_class tuple_class;
14315 * Get exclusive lock till end of transaction on the target table. Use
14316 * relation_open so that we can work on indexes and sequences.
14318 target_rel = relation_open(relationOid, lockmode);
14320 /* Get its pg_class tuple, too */
14321 class_rel = table_open(RelationRelationId, RowExclusiveLock);
14323 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14324 if (!HeapTupleIsValid(tuple))
14325 elog(ERROR, "cache lookup failed for relation %u", relationOid);
14326 tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14328 /* Can we change the ownership of this tuple? */
14329 switch (tuple_class->relkind)
14331 case RELKIND_RELATION:
14332 case RELKIND_VIEW:
14333 case RELKIND_MATVIEW:
14334 case RELKIND_FOREIGN_TABLE:
14335 case RELKIND_PARTITIONED_TABLE:
14336 /* ok to change owner */
14337 break;
14338 case RELKIND_INDEX:
14339 if (!recursing)
14342 * Because ALTER INDEX OWNER used to be allowed, and in fact
14343 * is generated by old versions of pg_dump, we give a warning
14344 * and do nothing rather than erroring out. Also, to avoid
14345 * unnecessary chatter while restoring those old dumps, say
14346 * nothing at all if the command would be a no-op anyway.
14348 if (tuple_class->relowner != newOwnerId)
14349 ereport(WARNING,
14350 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14351 errmsg("cannot change owner of index \"%s\"",
14352 NameStr(tuple_class->relname)),
14353 errhint("Change the ownership of the index's table instead.")));
14354 /* quick hack to exit via the no-op path */
14355 newOwnerId = tuple_class->relowner;
14357 break;
14358 case RELKIND_PARTITIONED_INDEX:
14359 if (recursing)
14360 break;
14361 ereport(ERROR,
14362 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14363 errmsg("cannot change owner of index \"%s\"",
14364 NameStr(tuple_class->relname)),
14365 errhint("Change the ownership of the index's table instead.")));
14366 break;
14367 case RELKIND_SEQUENCE:
14368 if (!recursing &&
14369 tuple_class->relowner != newOwnerId)
14371 /* if it's an owned sequence, disallow changing it by itself */
14372 Oid tableId;
14373 int32 colId;
14375 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14376 sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14377 ereport(ERROR,
14378 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14379 errmsg("cannot change owner of sequence \"%s\"",
14380 NameStr(tuple_class->relname)),
14381 errdetail("Sequence \"%s\" is linked to table \"%s\".",
14382 NameStr(tuple_class->relname),
14383 get_rel_name(tableId))));
14385 break;
14386 case RELKIND_COMPOSITE_TYPE:
14387 if (recursing)
14388 break;
14389 ereport(ERROR,
14390 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14391 errmsg("\"%s\" is a composite type",
14392 NameStr(tuple_class->relname)),
14393 /* translator: %s is an SQL ALTER command */
14394 errhint("Use %s instead.",
14395 "ALTER TYPE")));
14396 break;
14397 case RELKIND_TOASTVALUE:
14398 if (recursing)
14399 break;
14400 /* FALL THRU */
14401 default:
14402 ereport(ERROR,
14403 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14404 errmsg("cannot change owner of relation \"%s\"",
14405 NameStr(tuple_class->relname)),
14406 errdetail_relkind_not_supported(tuple_class->relkind)));
14410 * If the new owner is the same as the existing owner, consider the
14411 * command to have succeeded. This is for dump restoration purposes.
14413 if (tuple_class->relowner != newOwnerId)
14415 Datum repl_val[Natts_pg_class];
14416 bool repl_null[Natts_pg_class];
14417 bool repl_repl[Natts_pg_class];
14418 Acl *newAcl;
14419 Datum aclDatum;
14420 bool isNull;
14421 HeapTuple newtuple;
14423 /* skip permission checks when recursing to index or toast table */
14424 if (!recursing)
14426 /* Superusers can always do it */
14427 if (!superuser())
14429 Oid namespaceOid = tuple_class->relnamespace;
14430 AclResult aclresult;
14432 /* Otherwise, must be owner of the existing object */
14433 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
14434 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relationOid)),
14435 RelationGetRelationName(target_rel));
14437 /* Must be able to become new owner */
14438 check_can_set_role(GetUserId(), newOwnerId);
14440 /* New owner must have CREATE privilege on namespace */
14441 aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14442 ACL_CREATE);
14443 if (aclresult != ACLCHECK_OK)
14444 aclcheck_error(aclresult, OBJECT_SCHEMA,
14445 get_namespace_name(namespaceOid));
14449 memset(repl_null, false, sizeof(repl_null));
14450 memset(repl_repl, false, sizeof(repl_repl));
14452 repl_repl[Anum_pg_class_relowner - 1] = true;
14453 repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
14456 * Determine the modified ACL for the new owner. This is only
14457 * necessary when the ACL is non-null.
14459 aclDatum = SysCacheGetAttr(RELOID, tuple,
14460 Anum_pg_class_relacl,
14461 &isNull);
14462 if (!isNull)
14464 newAcl = aclnewowner(DatumGetAclP(aclDatum),
14465 tuple_class->relowner, newOwnerId);
14466 repl_repl[Anum_pg_class_relacl - 1] = true;
14467 repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
14470 newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
14472 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
14474 heap_freetuple(newtuple);
14477 * We must similarly update any per-column ACLs to reflect the new
14478 * owner; for neatness reasons that's split out as a subroutine.
14480 change_owner_fix_column_acls(relationOid,
14481 tuple_class->relowner,
14482 newOwnerId);
14485 * Update owner dependency reference, if any. A composite type has
14486 * none, because it's tracked for the pg_type entry instead of here;
14487 * indexes and TOAST tables don't have their own entries either.
14489 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
14490 tuple_class->relkind != RELKIND_INDEX &&
14491 tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
14492 tuple_class->relkind != RELKIND_TOASTVALUE)
14493 changeDependencyOnOwner(RelationRelationId, relationOid,
14494 newOwnerId);
14497 * Also change the ownership of the table's row type, if it has one
14499 if (OidIsValid(tuple_class->reltype))
14500 AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
14503 * If we are operating on a table or materialized view, also change
14504 * the ownership of any indexes and sequences that belong to the
14505 * relation, as well as its toast table (if it has one).
14507 if (tuple_class->relkind == RELKIND_RELATION ||
14508 tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
14509 tuple_class->relkind == RELKIND_MATVIEW ||
14510 tuple_class->relkind == RELKIND_TOASTVALUE)
14512 List *index_oid_list;
14513 ListCell *i;
14515 /* Find all the indexes belonging to this relation */
14516 index_oid_list = RelationGetIndexList(target_rel);
14518 /* For each index, recursively change its ownership */
14519 foreach(i, index_oid_list)
14520 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14522 list_free(index_oid_list);
14525 /* If it has a toast table, recurse to change its ownership */
14526 if (tuple_class->reltoastrelid != InvalidOid)
14527 ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14528 true, lockmode);
14530 /* If it has dependent sequences, recurse to change them too */
14531 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
14534 InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14536 ReleaseSysCache(tuple);
14537 table_close(class_rel, RowExclusiveLock);
14538 relation_close(target_rel, NoLock);
14542 * change_owner_fix_column_acls
14544 * Helper function for ATExecChangeOwner. Scan the columns of the table
14545 * and fix any non-null column ACLs to reflect the new owner.
14547 static void
14548 change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
14550 Relation attRelation;
14551 SysScanDesc scan;
14552 ScanKeyData key[1];
14553 HeapTuple attributeTuple;
14555 attRelation = table_open(AttributeRelationId, RowExclusiveLock);
14556 ScanKeyInit(&key[0],
14557 Anum_pg_attribute_attrelid,
14558 BTEqualStrategyNumber, F_OIDEQ,
14559 ObjectIdGetDatum(relationOid));
14560 scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
14561 true, NULL, 1, key);
14562 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14564 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14565 Datum repl_val[Natts_pg_attribute];
14566 bool repl_null[Natts_pg_attribute];
14567 bool repl_repl[Natts_pg_attribute];
14568 Acl *newAcl;
14569 Datum aclDatum;
14570 bool isNull;
14571 HeapTuple newtuple;
14573 /* Ignore dropped columns */
14574 if (att->attisdropped)
14575 continue;
14577 aclDatum = heap_getattr(attributeTuple,
14578 Anum_pg_attribute_attacl,
14579 RelationGetDescr(attRelation),
14580 &isNull);
14581 /* Null ACLs do not require changes */
14582 if (isNull)
14583 continue;
14585 memset(repl_null, false, sizeof(repl_null));
14586 memset(repl_repl, false, sizeof(repl_repl));
14588 newAcl = aclnewowner(DatumGetAclP(aclDatum),
14589 oldOwnerId, newOwnerId);
14590 repl_repl[Anum_pg_attribute_attacl - 1] = true;
14591 repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
14593 newtuple = heap_modify_tuple(attributeTuple,
14594 RelationGetDescr(attRelation),
14595 repl_val, repl_null, repl_repl);
14597 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14599 heap_freetuple(newtuple);
14601 systable_endscan(scan);
14602 table_close(attRelation, RowExclusiveLock);
14606 * change_owner_recurse_to_sequences
14608 * Helper function for ATExecChangeOwner. Examines pg_depend searching
14609 * for sequences that are dependent on serial columns, and changes their
14610 * ownership.
14612 static void
14613 change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
14615 Relation depRel;
14616 SysScanDesc scan;
14617 ScanKeyData key[2];
14618 HeapTuple tup;
14621 * SERIAL sequences are those having an auto dependency on one of the
14622 * table's columns (we don't care *which* column, exactly).
14624 depRel = table_open(DependRelationId, AccessShareLock);
14626 ScanKeyInit(&key[0],
14627 Anum_pg_depend_refclassid,
14628 BTEqualStrategyNumber, F_OIDEQ,
14629 ObjectIdGetDatum(RelationRelationId));
14630 ScanKeyInit(&key[1],
14631 Anum_pg_depend_refobjid,
14632 BTEqualStrategyNumber, F_OIDEQ,
14633 ObjectIdGetDatum(relationOid));
14634 /* we leave refobjsubid unspecified */
14636 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14637 NULL, 2, key);
14639 while (HeapTupleIsValid(tup = systable_getnext(scan)))
14641 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14642 Relation seqRel;
14644 /* skip dependencies other than auto dependencies on columns */
14645 if (depForm->refobjsubid == 0 ||
14646 depForm->classid != RelationRelationId ||
14647 depForm->objsubid != 0 ||
14648 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14649 continue;
14651 /* Use relation_open just in case it's an index */
14652 seqRel = relation_open(depForm->objid, lockmode);
14654 /* skip non-sequence relations */
14655 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14657 /* No need to keep the lock */
14658 relation_close(seqRel, lockmode);
14659 continue;
14662 /* We don't need to close the sequence while we alter it. */
14663 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14665 /* Now we can close it. Keep the lock till end of transaction. */
14666 relation_close(seqRel, NoLock);
14669 systable_endscan(scan);
14671 relation_close(depRel, AccessShareLock);
14675 * ALTER TABLE CLUSTER ON
14677 * The only thing we have to do is to change the indisclustered bits.
14679 * Return the address of the new clustering index.
14681 static ObjectAddress
14682 ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
14684 Oid indexOid;
14685 ObjectAddress address;
14687 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14689 if (!OidIsValid(indexOid))
14690 ereport(ERROR,
14691 (errcode(ERRCODE_UNDEFINED_OBJECT),
14692 errmsg("index \"%s\" for table \"%s\" does not exist",
14693 indexName, RelationGetRelationName(rel))));
14695 /* Check index is valid to cluster on */
14696 check_index_is_clusterable(rel, indexOid, lockmode);
14698 /* And do the work */
14699 mark_index_clustered(rel, indexOid, false);
14701 ObjectAddressSet(address,
14702 RelationRelationId, indexOid);
14704 return address;
14708 * ALTER TABLE SET WITHOUT CLUSTER
14710 * We have to find any indexes on the table that have indisclustered bit
14711 * set and turn it off.
14713 static void
14714 ATExecDropCluster(Relation rel, LOCKMODE lockmode)
14716 mark_index_clustered(rel, InvalidOid, false);
14720 * Preparation phase for SET ACCESS METHOD
14722 * Check that the access method exists and determine whether a change is
14723 * actually needed.
14725 static void
14726 ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
14728 Oid amoid;
14731 * Look up the access method name and check that it differs from the
14732 * table's current AM. If DEFAULT was specified for a partitioned table
14733 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
14735 if (amname != NULL)
14736 amoid = get_table_am_oid(amname, false);
14737 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14738 amoid = InvalidOid;
14739 else
14740 amoid = get_table_am_oid(default_table_access_method, false);
14742 /* if it's a match, phase 3 doesn't need to do anything */
14743 if (rel->rd_rel->relam == amoid)
14744 return;
14746 /* Save info for Phase 3 to do the real work */
14747 tab->rewrite |= AT_REWRITE_ACCESS_METHOD;
14748 tab->newAccessMethod = amoid;
14749 tab->chgAccessMethod = true;
14753 * Special handling of ALTER TABLE SET ACCESS METHOD for relations with no
14754 * storage that have an interest in preserving AM.
14756 * Since these have no storage, setting the access method is a catalog only
14757 * operation.
14759 static void
14760 ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
14762 Relation pg_class;
14763 Oid oldAccessMethodId;
14764 HeapTuple tuple;
14765 Form_pg_class rd_rel;
14766 Oid reloid = RelationGetRelid(rel);
14769 * Shouldn't be called on relations having storage; these are processed in
14770 * phase 3.
14772 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
14774 /* Get a modifiable copy of the relation's pg_class row. */
14775 pg_class = table_open(RelationRelationId, RowExclusiveLock);
14777 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
14778 if (!HeapTupleIsValid(tuple))
14779 elog(ERROR, "cache lookup failed for relation %u", reloid);
14780 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
14782 /* Update the pg_class row. */
14783 oldAccessMethodId = rd_rel->relam;
14784 rd_rel->relam = newAccessMethodId;
14786 /* Leave if no update required */
14787 if (rd_rel->relam == oldAccessMethodId)
14789 heap_freetuple(tuple);
14790 table_close(pg_class, RowExclusiveLock);
14791 return;
14794 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
14797 * Update the dependency on the new access method. No dependency is added
14798 * if the new access method is InvalidOid (default case). Be very careful
14799 * that this has to compare the previous value stored in pg_class with the
14800 * new one.
14802 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
14804 ObjectAddress relobj,
14805 referenced;
14808 * New access method is defined and there was no dependency
14809 * previously, so record a new one.
14811 ObjectAddressSet(relobj, RelationRelationId, reloid);
14812 ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
14813 recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
14815 else if (OidIsValid(oldAccessMethodId) &&
14816 !OidIsValid(rd_rel->relam))
14819 * There was an access method defined, and no new one, so just remove
14820 * the existing dependency.
14822 deleteDependencyRecordsForClass(RelationRelationId, reloid,
14823 AccessMethodRelationId,
14824 DEPENDENCY_NORMAL);
14826 else
14828 Assert(OidIsValid(oldAccessMethodId) &&
14829 OidIsValid(rd_rel->relam));
14831 /* Both are valid, so update the dependency */
14832 changeDependencyFor(RelationRelationId, reloid,
14833 AccessMethodRelationId,
14834 oldAccessMethodId, rd_rel->relam);
14837 /* make the relam and dependency changes visible */
14838 CommandCounterIncrement();
14840 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14842 heap_freetuple(tuple);
14843 table_close(pg_class, RowExclusiveLock);
14847 * ALTER TABLE SET TABLESPACE
14849 static void
14850 ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
14852 Oid tablespaceId;
14854 /* Check that the tablespace exists */
14855 tablespaceId = get_tablespace_oid(tablespacename, false);
14857 /* Check permissions except when moving to database's default */
14858 if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14860 AclResult aclresult;
14862 aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
14863 if (aclresult != ACLCHECK_OK)
14864 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
14867 /* Save info for Phase 3 to do the real work */
14868 if (OidIsValid(tab->newTableSpace))
14869 ereport(ERROR,
14870 (errcode(ERRCODE_SYNTAX_ERROR),
14871 errmsg("cannot have multiple SET TABLESPACE subcommands")));
14873 tab->newTableSpace = tablespaceId;
14877 * Set, reset, or replace reloptions.
14879 static void
14880 ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
14881 LOCKMODE lockmode)
14883 Oid relid;
14884 Relation pgclass;
14885 HeapTuple tuple;
14886 HeapTuple newtuple;
14887 Datum datum;
14888 bool isnull;
14889 Datum newOptions;
14890 Datum repl_val[Natts_pg_class];
14891 bool repl_null[Natts_pg_class];
14892 bool repl_repl[Natts_pg_class];
14893 static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14895 if (defList == NIL && operation != AT_ReplaceRelOptions)
14896 return; /* nothing to do */
14898 pgclass = table_open(RelationRelationId, RowExclusiveLock);
14900 /* Fetch heap tuple */
14901 relid = RelationGetRelid(rel);
14902 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14903 if (!HeapTupleIsValid(tuple))
14904 elog(ERROR, "cache lookup failed for relation %u", relid);
14906 if (operation == AT_ReplaceRelOptions)
14909 * If we're supposed to replace the reloptions list, we just pretend
14910 * there were none before.
14912 datum = (Datum) 0;
14913 isnull = true;
14915 else
14917 /* Get the old reloptions */
14918 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14919 &isnull);
14922 /* Generate new proposed reloptions (text array) */
14923 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14924 defList, NULL, validnsps, false,
14925 operation == AT_ResetRelOptions);
14927 /* Validate */
14928 switch (rel->rd_rel->relkind)
14930 case RELKIND_RELATION:
14931 case RELKIND_TOASTVALUE:
14932 case RELKIND_MATVIEW:
14933 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
14934 break;
14935 case RELKIND_PARTITIONED_TABLE:
14936 (void) partitioned_table_reloptions(newOptions, true);
14937 break;
14938 case RELKIND_VIEW:
14939 (void) view_reloptions(newOptions, true);
14940 break;
14941 case RELKIND_INDEX:
14942 case RELKIND_PARTITIONED_INDEX:
14943 (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
14944 break;
14945 default:
14946 ereport(ERROR,
14947 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14948 errmsg("cannot set options for relation \"%s\"",
14949 RelationGetRelationName(rel)),
14950 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
14951 break;
14954 /* Special-case validation of view options */
14955 if (rel->rd_rel->relkind == RELKIND_VIEW)
14957 Query *view_query = get_view_query(rel);
14958 List *view_options = untransformRelOptions(newOptions);
14959 ListCell *cell;
14960 bool check_option = false;
14962 foreach(cell, view_options)
14964 DefElem *defel = (DefElem *) lfirst(cell);
14966 if (strcmp(defel->defname, "check_option") == 0)
14967 check_option = true;
14971 * If the check option is specified, look to see if the view is
14972 * actually auto-updatable or not.
14974 if (check_option)
14976 const char *view_updatable_error =
14977 view_query_is_auto_updatable(view_query, true);
14979 if (view_updatable_error)
14980 ereport(ERROR,
14981 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14982 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14983 errhint("%s", _(view_updatable_error))));
14988 * All we need do here is update the pg_class row; the new options will be
14989 * propagated into relcaches during post-commit cache inval.
14991 memset(repl_val, 0, sizeof(repl_val));
14992 memset(repl_null, false, sizeof(repl_null));
14993 memset(repl_repl, false, sizeof(repl_repl));
14995 if (newOptions != (Datum) 0)
14996 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
14997 else
14998 repl_null[Anum_pg_class_reloptions - 1] = true;
15000 repl_repl[Anum_pg_class_reloptions - 1] = true;
15002 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15003 repl_val, repl_null, repl_repl);
15005 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15007 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15009 heap_freetuple(newtuple);
15011 ReleaseSysCache(tuple);
15013 /* repeat the whole exercise for the toast table, if there's one */
15014 if (OidIsValid(rel->rd_rel->reltoastrelid))
15016 Relation toastrel;
15017 Oid toastid = rel->rd_rel->reltoastrelid;
15019 toastrel = table_open(toastid, lockmode);
15021 /* Fetch heap tuple */
15022 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15023 if (!HeapTupleIsValid(tuple))
15024 elog(ERROR, "cache lookup failed for relation %u", toastid);
15026 if (operation == AT_ReplaceRelOptions)
15029 * If we're supposed to replace the reloptions list, we just
15030 * pretend there were none before.
15032 datum = (Datum) 0;
15033 isnull = true;
15035 else
15037 /* Get the old reloptions */
15038 datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15039 &isnull);
15042 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15043 defList, "toast", validnsps, false,
15044 operation == AT_ResetRelOptions);
15046 (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15048 memset(repl_val, 0, sizeof(repl_val));
15049 memset(repl_null, false, sizeof(repl_null));
15050 memset(repl_repl, false, sizeof(repl_repl));
15052 if (newOptions != (Datum) 0)
15053 repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15054 else
15055 repl_null[Anum_pg_class_reloptions - 1] = true;
15057 repl_repl[Anum_pg_class_reloptions - 1] = true;
15059 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15060 repl_val, repl_null, repl_repl);
15062 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15064 InvokeObjectPostAlterHookArg(RelationRelationId,
15065 RelationGetRelid(toastrel), 0,
15066 InvalidOid, true);
15068 heap_freetuple(newtuple);
15070 ReleaseSysCache(tuple);
15072 table_close(toastrel, NoLock);
15075 table_close(pgclass, RowExclusiveLock);
15079 * Execute ALTER TABLE SET TABLESPACE for cases where there is no tuple
15080 * rewriting to be done, so we just want to copy the data as fast as possible.
15082 static void
15083 ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
15085 Relation rel;
15086 Oid reltoastrelid;
15087 RelFileNumber newrelfilenumber;
15088 RelFileLocator newrlocator;
15089 List *reltoastidxids = NIL;
15090 ListCell *lc;
15093 * Need lock here in case we are recursing to toast table or index
15095 rel = relation_open(tableOid, lockmode);
15097 /* Check first if relation can be moved to new tablespace */
15098 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15100 InvokeObjectPostAlterHook(RelationRelationId,
15101 RelationGetRelid(rel), 0);
15102 relation_close(rel, NoLock);
15103 return;
15106 reltoastrelid = rel->rd_rel->reltoastrelid;
15107 /* Fetch the list of indexes on toast relation if necessary */
15108 if (OidIsValid(reltoastrelid))
15110 Relation toastRel = relation_open(reltoastrelid, lockmode);
15112 reltoastidxids = RelationGetIndexList(toastRel);
15113 relation_close(toastRel, lockmode);
15117 * Relfilenumbers are not unique in databases across tablespaces, so we
15118 * need to allocate a new one in the new tablespace.
15120 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15121 rel->rd_rel->relpersistence);
15123 /* Open old and new relation */
15124 newrlocator = rel->rd_locator;
15125 newrlocator.relNumber = newrelfilenumber;
15126 newrlocator.spcOid = newTableSpace;
15128 /* hand off to AM to actually create new rel storage and copy the data */
15129 if (rel->rd_rel->relkind == RELKIND_INDEX)
15131 index_copy_data(rel, newrlocator);
15133 else
15135 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15136 table_relation_copy_data(rel, &newrlocator);
15140 * Update the pg_class row.
15142 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15143 * executed on pg_class or its indexes (the above copy wouldn't contain
15144 * the updated pg_class entry), but that's forbidden with
15145 * CheckRelationTableSpaceMove().
15147 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15149 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15151 RelationAssumeNewRelfilelocator(rel);
15153 relation_close(rel, NoLock);
15155 /* Make sure the reltablespace change is visible */
15156 CommandCounterIncrement();
15158 /* Move associated toast relation and/or indexes, too */
15159 if (OidIsValid(reltoastrelid))
15160 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15161 foreach(lc, reltoastidxids)
15162 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15164 /* Clean up */
15165 list_free(reltoastidxids);
15169 * Special handling of ALTER TABLE SET TABLESPACE for relations with no
15170 * storage that have an interest in preserving tablespace.
15172 * Since these have no storage the tablespace can be updated with a simple
15173 * metadata only operation to update the tablespace.
15175 static void
15176 ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
15179 * Shouldn't be called on relations having storage; these are processed in
15180 * phase 3.
15182 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15184 /* check if relation can be moved to its new tablespace */
15185 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15187 InvokeObjectPostAlterHook(RelationRelationId,
15188 RelationGetRelid(rel),
15190 return;
15193 /* Update can be done, so change reltablespace */
15194 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15196 InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15198 /* Make sure the reltablespace change is visible */
15199 CommandCounterIncrement();
15203 * Alter Table ALL ... SET TABLESPACE
15205 * Allows a user to move all objects of some type in a given tablespace in the
15206 * current database to another tablespace. Objects can be chosen based on the
15207 * owner of the object also, to allow users to move only their objects.
15208 * The user must have CREATE rights on the new tablespace, as usual. The main
15209 * permissions handling is done by the lower-level table move function.
15211 * All to-be-moved objects are locked first. If NOWAIT is specified and the
15212 * lock can't be acquired then we ereport(ERROR).
15215 AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
15217 List *relations = NIL;
15218 ListCell *l;
15219 ScanKeyData key[1];
15220 Relation rel;
15221 TableScanDesc scan;
15222 HeapTuple tuple;
15223 Oid orig_tablespaceoid;
15224 Oid new_tablespaceoid;
15225 List *role_oids = roleSpecsToIds(stmt->roles);
15227 /* Ensure we were not asked to move something we can't */
15228 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15229 stmt->objtype != OBJECT_MATVIEW)
15230 ereport(ERROR,
15231 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15232 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15234 /* Get the orig and new tablespace OIDs */
15235 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15236 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15238 /* Can't move shared relations in to or out of pg_global */
15239 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15240 if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15241 new_tablespaceoid == GLOBALTABLESPACE_OID)
15242 ereport(ERROR,
15243 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15244 errmsg("cannot move relations in to or out of pg_global tablespace")));
15247 * Must have CREATE rights on the new tablespace, unless it is the
15248 * database default tablespace (which all users implicitly have CREATE
15249 * rights on).
15251 if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15253 AclResult aclresult;
15255 aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15256 ACL_CREATE);
15257 if (aclresult != ACLCHECK_OK)
15258 aclcheck_error(aclresult, OBJECT_TABLESPACE,
15259 get_tablespace_name(new_tablespaceoid));
15263 * Now that the checks are done, check if we should set either to
15264 * InvalidOid because it is our database's default tablespace.
15266 if (orig_tablespaceoid == MyDatabaseTableSpace)
15267 orig_tablespaceoid = InvalidOid;
15269 if (new_tablespaceoid == MyDatabaseTableSpace)
15270 new_tablespaceoid = InvalidOid;
15272 /* no-op */
15273 if (orig_tablespaceoid == new_tablespaceoid)
15274 return new_tablespaceoid;
15277 * Walk the list of objects in the tablespace and move them. This will
15278 * only find objects in our database, of course.
15280 ScanKeyInit(&key[0],
15281 Anum_pg_class_reltablespace,
15282 BTEqualStrategyNumber, F_OIDEQ,
15283 ObjectIdGetDatum(orig_tablespaceoid));
15285 rel = table_open(RelationRelationId, AccessShareLock);
15286 scan = table_beginscan_catalog(rel, 1, key);
15287 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15289 Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15290 Oid relOid = relForm->oid;
15293 * Do not move objects in pg_catalog as part of this, if an admin
15294 * really wishes to do so, they can issue the individual ALTER
15295 * commands directly.
15297 * Also, explicitly avoid any shared tables, temp tables, or TOAST
15298 * (TOAST will be moved with the main table).
15300 if (IsCatalogNamespace(relForm->relnamespace) ||
15301 relForm->relisshared ||
15302 isAnyTempNamespace(relForm->relnamespace) ||
15303 IsToastNamespace(relForm->relnamespace))
15304 continue;
15306 /* Only move the object type requested */
15307 if ((stmt->objtype == OBJECT_TABLE &&
15308 relForm->relkind != RELKIND_RELATION &&
15309 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15310 (stmt->objtype == OBJECT_INDEX &&
15311 relForm->relkind != RELKIND_INDEX &&
15312 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15313 (stmt->objtype == OBJECT_MATVIEW &&
15314 relForm->relkind != RELKIND_MATVIEW))
15315 continue;
15317 /* Check if we are only moving objects owned by certain roles */
15318 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15319 continue;
15322 * Handle permissions-checking here since we are locking the tables
15323 * and also to avoid doing a bunch of work only to fail part-way. Note
15324 * that permissions will also be checked by AlterTableInternal().
15326 * Caller must be considered an owner on the table to move it.
15328 if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15329 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
15330 NameStr(relForm->relname));
15332 if (stmt->nowait &&
15333 !ConditionalLockRelationOid(relOid, AccessExclusiveLock))
15334 ereport(ERROR,
15335 (errcode(ERRCODE_OBJECT_IN_USE),
15336 errmsg("aborting because lock on relation \"%s.%s\" is not available",
15337 get_namespace_name(relForm->relnamespace),
15338 NameStr(relForm->relname))));
15339 else
15340 LockRelationOid(relOid, AccessExclusiveLock);
15342 /* Add to our list of objects to move */
15343 relations = lappend_oid(relations, relOid);
15346 table_endscan(scan);
15347 table_close(rel, AccessShareLock);
15349 if (relations == NIL)
15350 ereport(NOTICE,
15351 (errcode(ERRCODE_NO_DATA_FOUND),
15352 errmsg("no matching relations in tablespace \"%s\" found",
15353 orig_tablespaceoid == InvalidOid ? "(database default)" :
15354 get_tablespace_name(orig_tablespaceoid))));
15356 /* Everything is locked, loop through and move all of the relations. */
15357 foreach(l, relations)
15359 List *cmds = NIL;
15360 AlterTableCmd *cmd = makeNode(AlterTableCmd);
15362 cmd->subtype = AT_SetTableSpace;
15363 cmd->name = stmt->new_tablespacename;
15365 cmds = lappend(cmds, cmd);
15367 EventTriggerAlterTableStart((Node *) stmt);
15368 /* OID is set by AlterTableInternal */
15369 AlterTableInternal(lfirst_oid(l), cmds, false);
15370 EventTriggerAlterTableEnd();
15373 return new_tablespaceoid;
15376 static void
15377 index_copy_data(Relation rel, RelFileLocator newrlocator)
15379 SMgrRelation dstrel;
15382 * Since we copy the file directly without looking at the shared buffers,
15383 * we'd better first flush out any pages of the source relation that are
15384 * in shared buffers. We assume no new changes will be made while we are
15385 * holding exclusive lock on the rel.
15387 FlushRelationBuffers(rel);
15390 * Create and copy all forks of the relation, and schedule unlinking of
15391 * old physical files.
15393 * NOTE: any conflict in relfilenumber value will be caught in
15394 * RelationCreateStorage().
15396 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15398 /* copy main fork */
15399 RelationCopyStorage(RelationGetSmgr(rel), dstrel, MAIN_FORKNUM,
15400 rel->rd_rel->relpersistence);
15402 /* copy those extra forks that exist */
15403 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15404 forkNum <= MAX_FORKNUM; forkNum++)
15406 if (smgrexists(RelationGetSmgr(rel), forkNum))
15408 smgrcreate(dstrel, forkNum, false);
15411 * WAL log creation if the relation is persistent, or this is the
15412 * init fork of an unlogged relation.
15414 if (RelationIsPermanent(rel) ||
15415 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
15416 forkNum == INIT_FORKNUM))
15417 log_smgrcreate(&newrlocator, forkNum);
15418 RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
15419 rel->rd_rel->relpersistence);
15423 /* drop old relation, and close new one */
15424 RelationDropStorage(rel);
15425 smgrclose(dstrel);
15429 * ALTER TABLE ENABLE/DISABLE TRIGGER
15431 * We just pass this off to trigger.c.
15433 static void
15434 ATExecEnableDisableTrigger(Relation rel, const char *trigname,
15435 char fires_when, bool skip_system, bool recurse,
15436 LOCKMODE lockmode)
15438 EnableDisableTrigger(rel, trigname, InvalidOid,
15439 fires_when, skip_system, recurse,
15440 lockmode);
15442 InvokeObjectPostAlterHook(RelationRelationId,
15443 RelationGetRelid(rel), 0);
15447 * ALTER TABLE ENABLE/DISABLE RULE
15449 * We just pass this off to rewriteDefine.c.
15451 static void
15452 ATExecEnableDisableRule(Relation rel, const char *rulename,
15453 char fires_when, LOCKMODE lockmode)
15455 EnableDisableRule(rel, rulename, fires_when);
15457 InvokeObjectPostAlterHook(RelationRelationId,
15458 RelationGetRelid(rel), 0);
15462 * ALTER TABLE INHERIT
15464 * Add a parent to the child's parents. This verifies that all the columns and
15465 * check constraints of the parent appear in the child and that they have the
15466 * same data types and expressions.
15468 static void
15469 ATPrepAddInherit(Relation child_rel)
15471 if (child_rel->rd_rel->reloftype)
15472 ereport(ERROR,
15473 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15474 errmsg("cannot change inheritance of typed table")));
15476 if (child_rel->rd_rel->relispartition)
15477 ereport(ERROR,
15478 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15479 errmsg("cannot change inheritance of a partition")));
15481 if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15482 ereport(ERROR,
15483 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15484 errmsg("cannot change inheritance of partitioned table")));
15488 * Return the address of the new parent relation.
15490 static ObjectAddress
15491 ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
15493 Relation parent_rel;
15494 List *children;
15495 ObjectAddress address;
15496 const char *trigger_name;
15499 * A self-exclusive lock is needed here. See the similar case in
15500 * MergeAttributes() for a full explanation.
15502 parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
15505 * Must be owner of both parent and child -- child was checked by
15506 * ATSimplePermissions call in ATPrepCmd
15508 ATSimplePermissions(AT_AddInherit, parent_rel, ATT_TABLE | ATT_FOREIGN_TABLE);
15510 /* Permanent rels cannot inherit from temporary ones */
15511 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15512 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
15513 ereport(ERROR,
15514 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15515 errmsg("cannot inherit from temporary relation \"%s\"",
15516 RelationGetRelationName(parent_rel))));
15518 /* If parent rel is temp, it must belong to this session */
15519 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15520 !parent_rel->rd_islocaltemp)
15521 ereport(ERROR,
15522 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15523 errmsg("cannot inherit from temporary relation of another session")));
15525 /* Ditto for the child */
15526 if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15527 !child_rel->rd_islocaltemp)
15528 ereport(ERROR,
15529 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15530 errmsg("cannot inherit to temporary relation of another session")));
15532 /* Prevent partitioned tables from becoming inheritance parents */
15533 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15534 ereport(ERROR,
15535 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15536 errmsg("cannot inherit from partitioned table \"%s\"",
15537 parent->relname)));
15539 /* Likewise for partitions */
15540 if (parent_rel->rd_rel->relispartition)
15541 ereport(ERROR,
15542 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15543 errmsg("cannot inherit from a partition")));
15546 * Prevent circularity by seeing if proposed parent inherits from child.
15547 * (In particular, this disallows making a rel inherit from itself.)
15549 * This is not completely bulletproof because of race conditions: in
15550 * multi-level inheritance trees, someone else could concurrently be
15551 * making another inheritance link that closes the loop but does not join
15552 * either of the rels we have locked. Preventing that seems to require
15553 * exclusive locks on the entire inheritance tree, which is a cure worse
15554 * than the disease. find_all_inheritors() will cope with circularity
15555 * anyway, so don't sweat it too much.
15557 * We use weakest lock we can on child's children, namely AccessShareLock.
15559 children = find_all_inheritors(RelationGetRelid(child_rel),
15560 AccessShareLock, NULL);
15562 if (list_member_oid(children, RelationGetRelid(parent_rel)))
15563 ereport(ERROR,
15564 (errcode(ERRCODE_DUPLICATE_TABLE),
15565 errmsg("circular inheritance not allowed"),
15566 errdetail("\"%s\" is already a child of \"%s\".",
15567 parent->relname,
15568 RelationGetRelationName(child_rel))));
15571 * If child_rel has row-level triggers with transition tables, we
15572 * currently don't allow it to become an inheritance child. See also
15573 * prohibitions in ATExecAttachPartition() and CreateTrigger().
15575 trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
15576 if (trigger_name != NULL)
15577 ereport(ERROR,
15578 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15579 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15580 trigger_name, RelationGetRelationName(child_rel)),
15581 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15583 /* OK to create inheritance */
15584 CreateInheritance(child_rel, parent_rel, false);
15586 ObjectAddressSet(address, RelationRelationId,
15587 RelationGetRelid(parent_rel));
15589 /* keep our lock on the parent relation until commit */
15590 table_close(parent_rel, NoLock);
15592 return address;
15596 * CreateInheritance
15597 * Catalog manipulation portion of creating inheritance between a child
15598 * table and a parent table.
15600 * Common to ATExecAddInherit() and ATExecAttachPartition().
15602 static void
15603 CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
15605 Relation catalogRelation;
15606 SysScanDesc scan;
15607 ScanKeyData key;
15608 HeapTuple inheritsTuple;
15609 int32 inhseqno;
15611 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15612 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15615 * Check for duplicates in the list of parents, and determine the highest
15616 * inhseqno already present; we'll use the next one for the new parent.
15617 * Also, if proposed child is a partition, it cannot already be
15618 * inheriting.
15620 * Note: we do not reject the case where the child already inherits from
15621 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15623 ScanKeyInit(&key,
15624 Anum_pg_inherits_inhrelid,
15625 BTEqualStrategyNumber, F_OIDEQ,
15626 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15627 scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15628 true, NULL, 1, &key);
15630 /* inhseqno sequences start at 1 */
15631 inhseqno = 0;
15632 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15634 Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15636 if (inh->inhparent == RelationGetRelid(parent_rel))
15637 ereport(ERROR,
15638 (errcode(ERRCODE_DUPLICATE_TABLE),
15639 errmsg("relation \"%s\" would be inherited from more than once",
15640 RelationGetRelationName(parent_rel))));
15642 if (inh->inhseqno > inhseqno)
15643 inhseqno = inh->inhseqno;
15645 systable_endscan(scan);
15647 /* Match up the columns and bump attinhcount as needed */
15648 MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
15650 /* Match up the constraints and bump coninhcount as needed */
15651 MergeConstraintsIntoExisting(child_rel, parent_rel);
15654 * OK, it looks valid. Make the catalog entries that show inheritance.
15656 StoreCatalogInheritance1(RelationGetRelid(child_rel),
15657 RelationGetRelid(parent_rel),
15658 inhseqno + 1,
15659 catalogRelation,
15660 parent_rel->rd_rel->relkind ==
15661 RELKIND_PARTITIONED_TABLE);
15663 /* Now we're done with pg_inherits */
15664 table_close(catalogRelation, RowExclusiveLock);
15668 * Obtain the source-text form of the constraint expression for a check
15669 * constraint, given its pg_constraint tuple
15671 static char *
15672 decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
15674 Form_pg_constraint con;
15675 bool isnull;
15676 Datum attr;
15677 Datum expr;
15679 con = (Form_pg_constraint) GETSTRUCT(contup);
15680 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15681 if (isnull)
15682 elog(ERROR, "null conbin for constraint %u", con->oid);
15684 expr = DirectFunctionCall2(pg_get_expr, attr,
15685 ObjectIdGetDatum(con->conrelid));
15686 return TextDatumGetCString(expr);
15690 * Determine whether two check constraints are functionally equivalent
15692 * The test we apply is to see whether they reverse-compile to the same
15693 * source string. This insulates us from issues like whether attributes
15694 * have the same physical column numbers in parent and child relations.
15696 static bool
15697 constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
15699 Form_pg_constraint acon = (Form_pg_constraint) GETSTRUCT(a);
15700 Form_pg_constraint bcon = (Form_pg_constraint) GETSTRUCT(b);
15702 if (acon->condeferrable != bcon->condeferrable ||
15703 acon->condeferred != bcon->condeferred ||
15704 strcmp(decompile_conbin(a, tupleDesc),
15705 decompile_conbin(b, tupleDesc)) != 0)
15706 return false;
15707 else
15708 return true;
15712 * Check columns in child table match up with columns in parent, and increment
15713 * their attinhcount.
15715 * Called by CreateInheritance
15717 * Currently all parent columns must be found in child. Missing columns are an
15718 * error. One day we might consider creating new columns like CREATE TABLE
15719 * does. However, that is widely unpopular --- in the common use case of
15720 * partitioned tables it's a foot-gun.
15722 * The data type must match exactly. If the parent column is NOT NULL then
15723 * the child must be as well. Defaults are not compared, however.
15725 static void
15726 MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
15728 Relation attrrel;
15729 TupleDesc parent_desc;
15731 attrrel = table_open(AttributeRelationId, RowExclusiveLock);
15732 parent_desc = RelationGetDescr(parent_rel);
15734 for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
15736 Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
15737 char *parent_attname = NameStr(parent_att->attname);
15738 HeapTuple tuple;
15740 /* Ignore dropped columns in the parent. */
15741 if (parent_att->attisdropped)
15742 continue;
15744 /* Find same column in child (matching on column name). */
15745 tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
15746 if (HeapTupleIsValid(tuple))
15748 Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
15750 if (parent_att->atttypid != child_att->atttypid ||
15751 parent_att->atttypmod != child_att->atttypmod)
15752 ereport(ERROR,
15753 (errcode(ERRCODE_DATATYPE_MISMATCH),
15754 errmsg("child table \"%s\" has different type for column \"%s\"",
15755 RelationGetRelationName(child_rel), parent_attname)));
15757 if (parent_att->attcollation != child_att->attcollation)
15758 ereport(ERROR,
15759 (errcode(ERRCODE_COLLATION_MISMATCH),
15760 errmsg("child table \"%s\" has different collation for column \"%s\"",
15761 RelationGetRelationName(child_rel), parent_attname)));
15764 * Check child doesn't discard NOT NULL property. (Other
15765 * constraints are checked elsewhere.)
15767 if (parent_att->attnotnull && !child_att->attnotnull)
15768 ereport(ERROR,
15769 (errcode(ERRCODE_DATATYPE_MISMATCH),
15770 errmsg("column \"%s\" in child table must be marked NOT NULL",
15771 parent_attname)));
15774 * Child column must be generated if and only if parent column is.
15776 if (parent_att->attgenerated && !child_att->attgenerated)
15777 ereport(ERROR,
15778 (errcode(ERRCODE_DATATYPE_MISMATCH),
15779 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
15780 if (child_att->attgenerated && !parent_att->attgenerated)
15781 ereport(ERROR,
15782 (errcode(ERRCODE_DATATYPE_MISMATCH),
15783 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
15786 * Regular inheritance children are independent enough not to
15787 * inherit identity columns. But partitions are integral part of
15788 * a partitioned table and inherit identity column.
15790 if (ispartition)
15791 child_att->attidentity = parent_att->attidentity;
15794 * OK, bump the child column's inheritance count. (If we fail
15795 * later on, this change will just roll back.)
15797 child_att->attinhcount++;
15798 if (child_att->attinhcount < 0)
15799 ereport(ERROR,
15800 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15801 errmsg("too many inheritance parents"));
15804 * In case of partitions, we must enforce that value of attislocal
15805 * is same in all partitions. (Note: there are only inherited
15806 * attributes in partitions)
15808 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15810 Assert(child_att->attinhcount == 1);
15811 child_att->attislocal = false;
15814 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
15815 heap_freetuple(tuple);
15817 else
15819 ereport(ERROR,
15820 (errcode(ERRCODE_DATATYPE_MISMATCH),
15821 errmsg("child table is missing column \"%s\"", parent_attname)));
15825 table_close(attrrel, RowExclusiveLock);
15829 * Check constraints in child table match up with constraints in parent,
15830 * and increment their coninhcount.
15832 * Constraints that are marked ONLY in the parent are ignored.
15834 * Called by CreateInheritance
15836 * Currently all constraints in parent must be present in the child. One day we
15837 * may consider adding new constraints like CREATE TABLE does.
15839 * XXX This is O(N^2) which may be an issue with tables with hundreds of
15840 * constraints. As long as tables have more like 10 constraints it shouldn't be
15841 * a problem though. Even 100 constraints ought not be the end of the world.
15843 * XXX See MergeWithExistingConstraint too if you change this code.
15845 static void
15846 MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
15848 Relation constraintrel;
15849 SysScanDesc parent_scan;
15850 ScanKeyData parent_key;
15851 HeapTuple parent_tuple;
15852 Oid parent_relid = RelationGetRelid(parent_rel);
15854 constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
15856 /* Outer loop scans through the parent's constraint definitions */
15857 ScanKeyInit(&parent_key,
15858 Anum_pg_constraint_conrelid,
15859 BTEqualStrategyNumber, F_OIDEQ,
15860 ObjectIdGetDatum(parent_relid));
15861 parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15862 true, NULL, 1, &parent_key);
15864 while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15866 Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15867 SysScanDesc child_scan;
15868 ScanKeyData child_key;
15869 HeapTuple child_tuple;
15870 bool found = false;
15872 if (parent_con->contype != CONSTRAINT_CHECK)
15873 continue;
15875 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15876 if (parent_con->connoinherit)
15877 continue;
15879 /* Search for a child constraint matching this one */
15880 ScanKeyInit(&child_key,
15881 Anum_pg_constraint_conrelid,
15882 BTEqualStrategyNumber, F_OIDEQ,
15883 ObjectIdGetDatum(RelationGetRelid(child_rel)));
15884 child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15885 true, NULL, 1, &child_key);
15887 while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15889 Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
15890 HeapTuple child_copy;
15892 if (child_con->contype != CONSTRAINT_CHECK)
15893 continue;
15895 if (strcmp(NameStr(parent_con->conname),
15896 NameStr(child_con->conname)) != 0)
15897 continue;
15899 if (!constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
15900 ereport(ERROR,
15901 (errcode(ERRCODE_DATATYPE_MISMATCH),
15902 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15903 RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
15905 /* If the child constraint is "no inherit" then cannot merge */
15906 if (child_con->connoinherit)
15907 ereport(ERROR,
15908 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15909 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15910 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15913 * If the child constraint is "not valid" then cannot merge with a
15914 * valid parent constraint
15916 if (parent_con->convalidated && !child_con->convalidated)
15917 ereport(ERROR,
15918 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15919 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15920 NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15923 * OK, bump the child constraint's inheritance count. (If we fail
15924 * later on, this change will just roll back.)
15926 child_copy = heap_copytuple(child_tuple);
15927 child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
15928 child_con->coninhcount++;
15929 if (child_con->coninhcount < 0)
15930 ereport(ERROR,
15931 errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15932 errmsg("too many inheritance parents"));
15935 * In case of partitions, an inherited constraint must be
15936 * inherited only once since it cannot have multiple parents and
15937 * it is never considered local.
15939 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15941 Assert(child_con->coninhcount == 1);
15942 child_con->conislocal = false;
15945 CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
15946 heap_freetuple(child_copy);
15948 found = true;
15949 break;
15952 systable_endscan(child_scan);
15954 if (!found)
15955 ereport(ERROR,
15956 (errcode(ERRCODE_DATATYPE_MISMATCH),
15957 errmsg("child table is missing constraint \"%s\"",
15958 NameStr(parent_con->conname))));
15961 systable_endscan(parent_scan);
15962 table_close(constraintrel, RowExclusiveLock);
15966 * ALTER TABLE NO INHERIT
15968 * Return value is the address of the relation that is no longer parent.
15970 static ObjectAddress
15971 ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
15973 ObjectAddress address;
15974 Relation parent_rel;
15976 if (rel->rd_rel->relispartition)
15977 ereport(ERROR,
15978 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15979 errmsg("cannot change inheritance of a partition")));
15982 * AccessShareLock on the parent is probably enough, seeing that DROP
15983 * TABLE doesn't lock parent tables at all. We need some lock since we'll
15984 * be inspecting the parent's schema.
15986 parent_rel = table_openrv(parent, AccessShareLock);
15989 * We don't bother to check ownership of the parent table --- ownership of
15990 * the child is presumed enough rights.
15993 /* Off to RemoveInheritance() where most of the work happens */
15994 RemoveInheritance(rel, parent_rel, false);
15996 ObjectAddressSet(address, RelationRelationId,
15997 RelationGetRelid(parent_rel));
15999 /* keep our lock on the parent relation until commit */
16000 table_close(parent_rel, NoLock);
16002 return address;
16006 * MarkInheritDetached
16008 * Set inhdetachpending for a partition, for ATExecDetachPartition
16009 * in concurrent mode. While at it, verify that no other partition is
16010 * already pending detach.
16012 static void
16013 MarkInheritDetached(Relation child_rel, Relation parent_rel)
16015 Relation catalogRelation;
16016 SysScanDesc scan;
16017 ScanKeyData key;
16018 HeapTuple inheritsTuple;
16019 bool found = false;
16021 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16024 * Find pg_inherits entries by inhparent. (We need to scan them all in
16025 * order to verify that no other partition is pending detach.)
16027 catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16028 ScanKeyInit(&key,
16029 Anum_pg_inherits_inhparent,
16030 BTEqualStrategyNumber, F_OIDEQ,
16031 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16032 scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16033 true, NULL, 1, &key);
16035 while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16037 Form_pg_inherits inhForm;
16039 inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16040 if (inhForm->inhdetachpending)
16041 ereport(ERROR,
16042 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16043 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16044 get_rel_name(inhForm->inhrelid),
16045 get_namespace_name(parent_rel->rd_rel->relnamespace),
16046 RelationGetRelationName(parent_rel)),
16047 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16049 if (inhForm->inhrelid == RelationGetRelid(child_rel))
16051 HeapTuple newtup;
16053 newtup = heap_copytuple(inheritsTuple);
16054 ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16056 CatalogTupleUpdate(catalogRelation,
16057 &inheritsTuple->t_self,
16058 newtup);
16059 found = true;
16060 heap_freetuple(newtup);
16061 /* keep looking, to ensure we catch others pending detach */
16065 /* Done */
16066 systable_endscan(scan);
16067 table_close(catalogRelation, RowExclusiveLock);
16069 if (!found)
16070 ereport(ERROR,
16071 (errcode(ERRCODE_UNDEFINED_TABLE),
16072 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16073 RelationGetRelationName(child_rel),
16074 RelationGetRelationName(parent_rel))));
16078 * RemoveInheritance
16080 * Drop a parent from the child's parents. This just adjusts the attinhcount
16081 * and attislocal of the columns and removes the pg_inherit and pg_depend
16082 * entries. expect_detached is passed down to DeleteInheritsTuple, q.v..
16084 * If attinhcount goes to 0 then attislocal gets set to true. If it goes back
16085 * up attislocal stays true, which means if a child is ever removed from a
16086 * parent then its columns will never be automatically dropped which may
16087 * surprise. But at least we'll never surprise by dropping columns someone
16088 * isn't expecting to be dropped which would actually mean data loss.
16090 * coninhcount and conislocal for inherited constraints are adjusted in
16091 * exactly the same way.
16093 * Common to ATExecDropInherit() and ATExecDetachPartition().
16095 static void
16096 RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
16098 Relation catalogRelation;
16099 SysScanDesc scan;
16100 ScanKeyData key[3];
16101 HeapTuple attributeTuple,
16102 constraintTuple;
16103 List *connames;
16104 bool found;
16105 bool is_partitioning;
16107 is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16109 found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16110 RelationGetRelid(parent_rel),
16111 expect_detached,
16112 RelationGetRelationName(child_rel));
16113 if (!found)
16115 if (is_partitioning)
16116 ereport(ERROR,
16117 (errcode(ERRCODE_UNDEFINED_TABLE),
16118 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16119 RelationGetRelationName(child_rel),
16120 RelationGetRelationName(parent_rel))));
16121 else
16122 ereport(ERROR,
16123 (errcode(ERRCODE_UNDEFINED_TABLE),
16124 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16125 RelationGetRelationName(parent_rel),
16126 RelationGetRelationName(child_rel))));
16130 * Search through child columns looking for ones matching parent rel
16132 catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16133 ScanKeyInit(&key[0],
16134 Anum_pg_attribute_attrelid,
16135 BTEqualStrategyNumber, F_OIDEQ,
16136 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16137 scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16138 true, NULL, 1, key);
16139 while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16141 Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16143 /* Ignore if dropped or not inherited */
16144 if (att->attisdropped)
16145 continue;
16146 if (att->attinhcount <= 0)
16147 continue;
16149 if (SearchSysCacheExistsAttName(RelationGetRelid(parent_rel),
16150 NameStr(att->attname)))
16152 /* Decrement inhcount and possibly set islocal to true */
16153 HeapTuple copyTuple = heap_copytuple(attributeTuple);
16154 Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16156 copy_att->attinhcount--;
16157 if (copy_att->attinhcount == 0)
16158 copy_att->attislocal = true;
16160 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16161 heap_freetuple(copyTuple);
16164 systable_endscan(scan);
16165 table_close(catalogRelation, RowExclusiveLock);
16168 * Likewise, find inherited check constraints and disinherit them. To do
16169 * this, we first need a list of the names of the parent's check
16170 * constraints. (We cheat a bit by only checking for name matches,
16171 * assuming that the expressions will match.)
16173 catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16174 ScanKeyInit(&key[0],
16175 Anum_pg_constraint_conrelid,
16176 BTEqualStrategyNumber, F_OIDEQ,
16177 ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16178 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16179 true, NULL, 1, key);
16181 connames = NIL;
16183 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16185 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16187 if (con->contype == CONSTRAINT_CHECK)
16188 connames = lappend(connames, pstrdup(NameStr(con->conname)));
16191 systable_endscan(scan);
16193 /* Now scan the child's constraints */
16194 ScanKeyInit(&key[0],
16195 Anum_pg_constraint_conrelid,
16196 BTEqualStrategyNumber, F_OIDEQ,
16197 ObjectIdGetDatum(RelationGetRelid(child_rel)));
16198 scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16199 true, NULL, 1, key);
16201 while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16203 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16204 bool match;
16206 if (con->contype != CONSTRAINT_CHECK)
16207 continue;
16209 match = false;
16210 foreach_ptr(char, chkname, connames)
16212 if (strcmp(NameStr(con->conname), chkname) == 0)
16214 match = true;
16215 break;
16219 if (match)
16221 /* Decrement inhcount and possibly set islocal to true */
16222 HeapTuple copyTuple = heap_copytuple(constraintTuple);
16223 Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16225 if (copy_con->coninhcount <= 0) /* shouldn't happen */
16226 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16227 RelationGetRelid(child_rel), NameStr(copy_con->conname));
16229 copy_con->coninhcount--;
16230 if (copy_con->coninhcount == 0)
16231 copy_con->conislocal = true;
16233 CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16234 heap_freetuple(copyTuple);
16238 systable_endscan(scan);
16239 table_close(catalogRelation, RowExclusiveLock);
16241 drop_parent_dependency(RelationGetRelid(child_rel),
16242 RelationRelationId,
16243 RelationGetRelid(parent_rel),
16244 child_dependency_type(is_partitioning));
16247 * Post alter hook of this inherits. Since object_access_hook doesn't take
16248 * multiple object identifiers, we relay oid of parent relation using
16249 * auxiliary_id argument.
16251 InvokeObjectPostAlterHookArg(InheritsRelationId,
16252 RelationGetRelid(child_rel), 0,
16253 RelationGetRelid(parent_rel), false);
16257 * Drop the dependency created by StoreCatalogInheritance1 (CREATE TABLE
16258 * INHERITS/ALTER TABLE INHERIT -- refclassid will be RelationRelationId) or
16259 * heap_create_with_catalog (CREATE TABLE OF/ALTER TABLE OF -- refclassid will
16260 * be TypeRelationId). There's no convenient way to do this, so go trawling
16261 * through pg_depend.
16263 static void
16264 drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid,
16265 DependencyType deptype)
16267 Relation catalogRelation;
16268 SysScanDesc scan;
16269 ScanKeyData key[3];
16270 HeapTuple depTuple;
16272 catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16274 ScanKeyInit(&key[0],
16275 Anum_pg_depend_classid,
16276 BTEqualStrategyNumber, F_OIDEQ,
16277 ObjectIdGetDatum(RelationRelationId));
16278 ScanKeyInit(&key[1],
16279 Anum_pg_depend_objid,
16280 BTEqualStrategyNumber, F_OIDEQ,
16281 ObjectIdGetDatum(relid));
16282 ScanKeyInit(&key[2],
16283 Anum_pg_depend_objsubid,
16284 BTEqualStrategyNumber, F_INT4EQ,
16285 Int32GetDatum(0));
16287 scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16288 NULL, 3, key);
16290 while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16292 Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16294 if (dep->refclassid == refclassid &&
16295 dep->refobjid == refobjid &&
16296 dep->refobjsubid == 0 &&
16297 dep->deptype == deptype)
16298 CatalogTupleDelete(catalogRelation, &depTuple->t_self);
16301 systable_endscan(scan);
16302 table_close(catalogRelation, RowExclusiveLock);
16306 * ALTER TABLE OF
16308 * Attach a table to a composite type, as though it had been created with CREATE
16309 * TABLE OF. All attname, atttypid, atttypmod and attcollation must match. The
16310 * subject table must not have inheritance parents. These restrictions ensure
16311 * that you cannot create a configuration impossible with CREATE TABLE OF alone.
16313 * The address of the type is returned.
16315 static ObjectAddress
16316 ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
16318 Oid relid = RelationGetRelid(rel);
16319 Type typetuple;
16320 Form_pg_type typeform;
16321 Oid typeid;
16322 Relation inheritsRelation,
16323 relationRelation;
16324 SysScanDesc scan;
16325 ScanKeyData key;
16326 AttrNumber table_attno,
16327 type_attno;
16328 TupleDesc typeTupleDesc,
16329 tableTupleDesc;
16330 ObjectAddress tableobj,
16331 typeobj;
16332 HeapTuple classtuple;
16334 /* Validate the type. */
16335 typetuple = typenameType(NULL, ofTypename, NULL);
16336 check_of_type(typetuple);
16337 typeform = (Form_pg_type) GETSTRUCT(typetuple);
16338 typeid = typeform->oid;
16340 /* Fail if the table has any inheritance parents. */
16341 inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
16342 ScanKeyInit(&key,
16343 Anum_pg_inherits_inhrelid,
16344 BTEqualStrategyNumber, F_OIDEQ,
16345 ObjectIdGetDatum(relid));
16346 scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
16347 true, NULL, 1, &key);
16348 if (HeapTupleIsValid(systable_getnext(scan)))
16349 ereport(ERROR,
16350 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16351 errmsg("typed tables cannot inherit")));
16352 systable_endscan(scan);
16353 table_close(inheritsRelation, AccessShareLock);
16356 * Check the tuple descriptors for compatibility. Unlike inheritance, we
16357 * require that the order also match. However, attnotnull need not match.
16359 typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
16360 tableTupleDesc = RelationGetDescr(rel);
16361 table_attno = 1;
16362 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
16364 Form_pg_attribute type_attr,
16365 table_attr;
16366 const char *type_attname,
16367 *table_attname;
16369 /* Get the next non-dropped type attribute. */
16370 type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
16371 if (type_attr->attisdropped)
16372 continue;
16373 type_attname = NameStr(type_attr->attname);
16375 /* Get the next non-dropped table attribute. */
16378 if (table_attno > tableTupleDesc->natts)
16379 ereport(ERROR,
16380 (errcode(ERRCODE_DATATYPE_MISMATCH),
16381 errmsg("table is missing column \"%s\"",
16382 type_attname)));
16383 table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
16384 table_attno++;
16385 } while (table_attr->attisdropped);
16386 table_attname = NameStr(table_attr->attname);
16388 /* Compare name. */
16389 if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
16390 ereport(ERROR,
16391 (errcode(ERRCODE_DATATYPE_MISMATCH),
16392 errmsg("table has column \"%s\" where type requires \"%s\"",
16393 table_attname, type_attname)));
16395 /* Compare type. */
16396 if (table_attr->atttypid != type_attr->atttypid ||
16397 table_attr->atttypmod != type_attr->atttypmod ||
16398 table_attr->attcollation != type_attr->attcollation)
16399 ereport(ERROR,
16400 (errcode(ERRCODE_DATATYPE_MISMATCH),
16401 errmsg("table \"%s\" has different type for column \"%s\"",
16402 RelationGetRelationName(rel), type_attname)));
16404 ReleaseTupleDesc(typeTupleDesc);
16406 /* Any remaining columns at the end of the table had better be dropped. */
16407 for (; table_attno <= tableTupleDesc->natts; table_attno++)
16409 Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
16410 table_attno - 1);
16412 if (!table_attr->attisdropped)
16413 ereport(ERROR,
16414 (errcode(ERRCODE_DATATYPE_MISMATCH),
16415 errmsg("table has extra column \"%s\"",
16416 NameStr(table_attr->attname))));
16419 /* If the table was already typed, drop the existing dependency. */
16420 if (rel->rd_rel->reloftype)
16421 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16422 DEPENDENCY_NORMAL);
16424 /* Record a dependency on the new type. */
16425 tableobj.classId = RelationRelationId;
16426 tableobj.objectId = relid;
16427 tableobj.objectSubId = 0;
16428 typeobj.classId = TypeRelationId;
16429 typeobj.objectId = typeid;
16430 typeobj.objectSubId = 0;
16431 recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
16433 /* Update pg_class.reloftype */
16434 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16435 classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16436 if (!HeapTupleIsValid(classtuple))
16437 elog(ERROR, "cache lookup failed for relation %u", relid);
16438 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
16439 CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
16441 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16443 heap_freetuple(classtuple);
16444 table_close(relationRelation, RowExclusiveLock);
16446 ReleaseSysCache(typetuple);
16448 return typeobj;
16452 * ALTER TABLE NOT OF
16454 * Detach a typed table from its originating type. Just clear reloftype and
16455 * remove the dependency.
16457 static void
16458 ATExecDropOf(Relation rel, LOCKMODE lockmode)
16460 Oid relid = RelationGetRelid(rel);
16461 Relation relationRelation;
16462 HeapTuple tuple;
16464 if (!OidIsValid(rel->rd_rel->reloftype))
16465 ereport(ERROR,
16466 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16467 errmsg("\"%s\" is not a typed table",
16468 RelationGetRelationName(rel))));
16471 * We don't bother to check ownership of the type --- ownership of the
16472 * table is presumed enough rights. No lock required on the type, either.
16475 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16476 DEPENDENCY_NORMAL);
16478 /* Clear pg_class.reloftype */
16479 relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16480 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16481 if (!HeapTupleIsValid(tuple))
16482 elog(ERROR, "cache lookup failed for relation %u", relid);
16483 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
16484 CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
16486 InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16488 heap_freetuple(tuple);
16489 table_close(relationRelation, RowExclusiveLock);
16493 * relation_mark_replica_identity: Update a table's replica identity
16495 * Iff ri_type = REPLICA_IDENTITY_INDEX, indexOid must be the Oid of a suitable
16496 * index. Otherwise, it must be InvalidOid.
16498 * Caller had better hold an exclusive lock on the relation, as the results
16499 * of running two of these concurrently wouldn't be pretty.
16501 static void
16502 relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid,
16503 bool is_internal)
16505 Relation pg_index;
16506 Relation pg_class;
16507 HeapTuple pg_class_tuple;
16508 HeapTuple pg_index_tuple;
16509 Form_pg_class pg_class_form;
16510 Form_pg_index pg_index_form;
16511 ListCell *index;
16514 * Check whether relreplident has changed, and update it if so.
16516 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16517 pg_class_tuple = SearchSysCacheCopy1(RELOID,
16518 ObjectIdGetDatum(RelationGetRelid(rel)));
16519 if (!HeapTupleIsValid(pg_class_tuple))
16520 elog(ERROR, "cache lookup failed for relation \"%s\"",
16521 RelationGetRelationName(rel));
16522 pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
16523 if (pg_class_form->relreplident != ri_type)
16525 pg_class_form->relreplident = ri_type;
16526 CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
16528 table_close(pg_class, RowExclusiveLock);
16529 heap_freetuple(pg_class_tuple);
16532 * Update the per-index indisreplident flags correctly.
16534 pg_index = table_open(IndexRelationId, RowExclusiveLock);
16535 foreach(index, RelationGetIndexList(rel))
16537 Oid thisIndexOid = lfirst_oid(index);
16538 bool dirty = false;
16540 pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
16541 ObjectIdGetDatum(thisIndexOid));
16542 if (!HeapTupleIsValid(pg_index_tuple))
16543 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
16544 pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
16546 if (thisIndexOid == indexOid)
16548 /* Set the bit if not already set. */
16549 if (!pg_index_form->indisreplident)
16551 dirty = true;
16552 pg_index_form->indisreplident = true;
16555 else
16557 /* Unset the bit if set. */
16558 if (pg_index_form->indisreplident)
16560 dirty = true;
16561 pg_index_form->indisreplident = false;
16565 if (dirty)
16567 CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
16568 InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
16569 InvalidOid, is_internal);
16572 * Invalidate the relcache for the table, so that after we commit
16573 * all sessions will refresh the table's replica identity index
16574 * before attempting any UPDATE or DELETE on the table. (If we
16575 * changed the table's pg_class row above, then a relcache inval
16576 * is already queued due to that; but we might not have.)
16578 CacheInvalidateRelcache(rel);
16580 heap_freetuple(pg_index_tuple);
16583 table_close(pg_index, RowExclusiveLock);
16587 * ALTER TABLE <name> REPLICA IDENTITY ...
16589 static void
16590 ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
16592 Oid indexOid;
16593 Relation indexRel;
16594 int key;
16596 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16598 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16599 return;
16601 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16603 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16604 return;
16606 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16608 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16609 return;
16611 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
16613 /* fallthrough */ ;
16615 else
16616 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16618 /* Check that the index exists */
16619 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16620 if (!OidIsValid(indexOid))
16621 ereport(ERROR,
16622 (errcode(ERRCODE_UNDEFINED_OBJECT),
16623 errmsg("index \"%s\" for table \"%s\" does not exist",
16624 stmt->name, RelationGetRelationName(rel))));
16626 indexRel = index_open(indexOid, ShareLock);
16628 /* Check that the index is on the relation we're altering. */
16629 if (indexRel->rd_index == NULL ||
16630 indexRel->rd_index->indrelid != RelationGetRelid(rel))
16631 ereport(ERROR,
16632 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16633 errmsg("\"%s\" is not an index for table \"%s\"",
16634 RelationGetRelationName(indexRel),
16635 RelationGetRelationName(rel))));
16636 /* The AM must support uniqueness, and the index must in fact be unique. */
16637 if (!indexRel->rd_indam->amcanunique ||
16638 !indexRel->rd_index->indisunique)
16639 ereport(ERROR,
16640 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16641 errmsg("cannot use non-unique index \"%s\" as replica identity",
16642 RelationGetRelationName(indexRel))));
16643 /* Deferred indexes are not guaranteed to be always unique. */
16644 if (!indexRel->rd_index->indimmediate)
16645 ereport(ERROR,
16646 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16647 errmsg("cannot use non-immediate index \"%s\" as replica identity",
16648 RelationGetRelationName(indexRel))));
16649 /* Expression indexes aren't supported. */
16650 if (RelationGetIndexExpressions(indexRel) != NIL)
16651 ereport(ERROR,
16652 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16653 errmsg("cannot use expression index \"%s\" as replica identity",
16654 RelationGetRelationName(indexRel))));
16655 /* Predicate indexes aren't supported. */
16656 if (RelationGetIndexPredicate(indexRel) != NIL)
16657 ereport(ERROR,
16658 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16659 errmsg("cannot use partial index \"%s\" as replica identity",
16660 RelationGetRelationName(indexRel))));
16662 /* Check index for nullable columns. */
16663 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16665 int16 attno = indexRel->rd_index->indkey.values[key];
16666 Form_pg_attribute attr;
16669 * Reject any other system columns. (Going forward, we'll disallow
16670 * indexes containing such columns in the first place, but they might
16671 * exist in older branches.)
16673 if (attno <= 0)
16674 ereport(ERROR,
16675 (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16676 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16677 RelationGetRelationName(indexRel), attno)));
16679 attr = TupleDescAttr(rel->rd_att, attno - 1);
16680 if (!attr->attnotnull)
16681 ereport(ERROR,
16682 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16683 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16684 RelationGetRelationName(indexRel),
16685 NameStr(attr->attname))));
16688 /* This index is suitable for use as a replica identity. Mark it. */
16689 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16691 index_close(indexRel, NoLock);
16695 * ALTER TABLE ENABLE/DISABLE ROW LEVEL SECURITY
16697 static void
16698 ATExecSetRowSecurity(Relation rel, bool rls)
16700 Relation pg_class;
16701 Oid relid;
16702 HeapTuple tuple;
16704 relid = RelationGetRelid(rel);
16706 /* Pull the record for this relation and update it */
16707 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16709 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16711 if (!HeapTupleIsValid(tuple))
16712 elog(ERROR, "cache lookup failed for relation %u", relid);
16714 ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
16715 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16717 InvokeObjectPostAlterHook(RelationRelationId,
16718 RelationGetRelid(rel), 0);
16720 table_close(pg_class, RowExclusiveLock);
16721 heap_freetuple(tuple);
16725 * ALTER TABLE FORCE/NO FORCE ROW LEVEL SECURITY
16727 static void
16728 ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
16730 Relation pg_class;
16731 Oid relid;
16732 HeapTuple tuple;
16734 relid = RelationGetRelid(rel);
16736 pg_class = table_open(RelationRelationId, RowExclusiveLock);
16738 tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16740 if (!HeapTupleIsValid(tuple))
16741 elog(ERROR, "cache lookup failed for relation %u", relid);
16743 ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
16744 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16746 InvokeObjectPostAlterHook(RelationRelationId,
16747 RelationGetRelid(rel), 0);
16749 table_close(pg_class, RowExclusiveLock);
16750 heap_freetuple(tuple);
16754 * ALTER FOREIGN TABLE <name> OPTIONS (...)
16756 static void
16757 ATExecGenericOptions(Relation rel, List *options)
16759 Relation ftrel;
16760 ForeignServer *server;
16761 ForeignDataWrapper *fdw;
16762 HeapTuple tuple;
16763 bool isnull;
16764 Datum repl_val[Natts_pg_foreign_table];
16765 bool repl_null[Natts_pg_foreign_table];
16766 bool repl_repl[Natts_pg_foreign_table];
16767 Datum datum;
16768 Form_pg_foreign_table tableform;
16770 if (options == NIL)
16771 return;
16773 ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
16775 tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
16776 ObjectIdGetDatum(rel->rd_id));
16777 if (!HeapTupleIsValid(tuple))
16778 ereport(ERROR,
16779 (errcode(ERRCODE_UNDEFINED_OBJECT),
16780 errmsg("foreign table \"%s\" does not exist",
16781 RelationGetRelationName(rel))));
16782 tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16783 server = GetForeignServer(tableform->ftserver);
16784 fdw = GetForeignDataWrapper(server->fdwid);
16786 memset(repl_val, 0, sizeof(repl_val));
16787 memset(repl_null, false, sizeof(repl_null));
16788 memset(repl_repl, false, sizeof(repl_repl));
16790 /* Extract the current options */
16791 datum = SysCacheGetAttr(FOREIGNTABLEREL,
16792 tuple,
16793 Anum_pg_foreign_table_ftoptions,
16794 &isnull);
16795 if (isnull)
16796 datum = PointerGetDatum(NULL);
16798 /* Transform the options */
16799 datum = transformGenericOptions(ForeignTableRelationId,
16800 datum,
16801 options,
16802 fdw->fdwvalidator);
16804 if (PointerIsValid(DatumGetPointer(datum)))
16805 repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
16806 else
16807 repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16809 repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16811 /* Everything looks good - update the tuple */
16813 tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16814 repl_val, repl_null, repl_repl);
16816 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
16819 * Invalidate relcache so that all sessions will refresh any cached plans
16820 * that might depend on the old options.
16822 CacheInvalidateRelcache(rel);
16824 InvokeObjectPostAlterHook(ForeignTableRelationId,
16825 RelationGetRelid(rel), 0);
16827 table_close(ftrel, RowExclusiveLock);
16829 heap_freetuple(tuple);
16833 * ALTER TABLE ALTER COLUMN SET COMPRESSION
16835 * Return value is the address of the modified column
16837 static ObjectAddress
16838 ATExecSetCompression(Relation rel,
16839 const char *column,
16840 Node *newValue,
16841 LOCKMODE lockmode)
16843 Relation attrel;
16844 HeapTuple tuple;
16845 Form_pg_attribute atttableform;
16846 AttrNumber attnum;
16847 char *compression;
16848 char cmethod;
16849 ObjectAddress address;
16851 compression = strVal(newValue);
16853 attrel = table_open(AttributeRelationId, RowExclusiveLock);
16855 /* copy the cache entry so we can scribble on it below */
16856 tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
16857 if (!HeapTupleIsValid(tuple))
16858 ereport(ERROR,
16859 (errcode(ERRCODE_UNDEFINED_COLUMN),
16860 errmsg("column \"%s\" of relation \"%s\" does not exist",
16861 column, RelationGetRelationName(rel))));
16863 /* prevent them from altering a system attribute */
16864 atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16865 attnum = atttableform->attnum;
16866 if (attnum <= 0)
16867 ereport(ERROR,
16868 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16869 errmsg("cannot alter system column \"%s\"", column)));
16872 * Check that column type is compressible, then get the attribute
16873 * compression method code
16875 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
16877 /* update pg_attribute entry */
16878 atttableform->attcompression = cmethod;
16879 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
16881 InvokeObjectPostAlterHook(RelationRelationId,
16882 RelationGetRelid(rel),
16883 attnum);
16886 * Apply the change to indexes as well (only for simple index columns,
16887 * matching behavior of index.c ConstructTupleDescriptor()).
16889 SetIndexStorageProperties(rel, attrel, attnum,
16890 false, 0,
16891 true, cmethod,
16892 lockmode);
16894 heap_freetuple(tuple);
16896 table_close(attrel, RowExclusiveLock);
16898 /* make changes visible */
16899 CommandCounterIncrement();
16901 ObjectAddressSubSet(address, RelationRelationId,
16902 RelationGetRelid(rel), attnum);
16903 return address;
16908 * Preparation phase for SET LOGGED/UNLOGGED
16910 * This verifies that we're not trying to change a temp table. Also,
16911 * existing foreign key constraints are checked to avoid ending up with
16912 * permanent tables referencing unlogged tables.
16914 * Return value is false if the operation is a no-op (in which case the
16915 * checks are skipped), otherwise true.
16917 static bool
16918 ATPrepChangePersistence(Relation rel, bool toLogged)
16920 Relation pg_constraint;
16921 HeapTuple tuple;
16922 SysScanDesc scan;
16923 ScanKeyData skey[1];
16926 * Disallow changing status for a temp table. Also verify whether we can
16927 * get away with doing nothing; in such cases we don't need to run the
16928 * checks below, either.
16930 switch (rel->rd_rel->relpersistence)
16932 case RELPERSISTENCE_TEMP:
16933 ereport(ERROR,
16934 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16935 errmsg("cannot change logged status of table \"%s\" because it is temporary",
16936 RelationGetRelationName(rel)),
16937 errtable(rel)));
16938 break;
16939 case RELPERSISTENCE_PERMANENT:
16940 if (toLogged)
16941 /* nothing to do */
16942 return false;
16943 break;
16944 case RELPERSISTENCE_UNLOGGED:
16945 if (!toLogged)
16946 /* nothing to do */
16947 return false;
16948 break;
16952 * Check that the table is not part of any publication when changing to
16953 * UNLOGGED, as UNLOGGED tables can't be published.
16955 if (!toLogged &&
16956 GetRelationPublications(RelationGetRelid(rel)) != NIL)
16957 ereport(ERROR,
16958 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16959 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16960 RelationGetRelationName(rel)),
16961 errdetail("Unlogged relations cannot be replicated.")));
16964 * Check existing foreign key constraints to preserve the invariant that
16965 * permanent tables cannot reference unlogged ones. Self-referencing
16966 * foreign keys can safely be ignored.
16968 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
16971 * Scan conrelid if changing to permanent, else confrelid. This also
16972 * determines whether a useful index exists.
16974 ScanKeyInit(&skey[0],
16975 toLogged ? Anum_pg_constraint_conrelid :
16976 Anum_pg_constraint_confrelid,
16977 BTEqualStrategyNumber, F_OIDEQ,
16978 ObjectIdGetDatum(RelationGetRelid(rel)));
16979 scan = systable_beginscan(pg_constraint,
16980 toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
16981 true, NULL, 1, skey);
16983 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
16985 Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
16987 if (con->contype == CONSTRAINT_FOREIGN)
16989 Oid foreignrelid;
16990 Relation foreignrel;
16992 /* the opposite end of what we used as scankey */
16993 foreignrelid = toLogged ? con->confrelid : con->conrelid;
16995 /* ignore if self-referencing */
16996 if (RelationGetRelid(rel) == foreignrelid)
16997 continue;
16999 foreignrel = relation_open(foreignrelid, AccessShareLock);
17001 if (toLogged)
17003 if (!RelationIsPermanent(foreignrel))
17004 ereport(ERROR,
17005 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17006 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17007 RelationGetRelationName(rel),
17008 RelationGetRelationName(foreignrel)),
17009 errtableconstraint(rel, NameStr(con->conname))));
17011 else
17013 if (RelationIsPermanent(foreignrel))
17014 ereport(ERROR,
17015 (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17016 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17017 RelationGetRelationName(rel),
17018 RelationGetRelationName(foreignrel)),
17019 errtableconstraint(rel, NameStr(con->conname))));
17022 relation_close(foreignrel, AccessShareLock);
17026 systable_endscan(scan);
17028 table_close(pg_constraint, AccessShareLock);
17030 return true;
17034 * Execute ALTER TABLE SET SCHEMA
17036 ObjectAddress
17037 AlterTableNamespace(AlterObjectSchemaStmt *stmt, Oid *oldschema)
17039 Relation rel;
17040 Oid relid;
17041 Oid oldNspOid;
17042 Oid nspOid;
17043 RangeVar *newrv;
17044 ObjectAddresses *objsMoved;
17045 ObjectAddress myself;
17047 relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock,
17048 stmt->missing_ok ? RVR_MISSING_OK : 0,
17049 RangeVarCallbackForAlterRelation,
17050 (void *) stmt);
17052 if (!OidIsValid(relid))
17054 ereport(NOTICE,
17055 (errmsg("relation \"%s\" does not exist, skipping",
17056 stmt->relation->relname)));
17057 return InvalidObjectAddress;
17060 rel = relation_open(relid, NoLock);
17062 oldNspOid = RelationGetNamespace(rel);
17064 /* If it's an owned sequence, disallow moving it by itself. */
17065 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17067 Oid tableId;
17068 int32 colId;
17070 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17071 sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17072 ereport(ERROR,
17073 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17074 errmsg("cannot move an owned sequence into another schema"),
17075 errdetail("Sequence \"%s\" is linked to table \"%s\".",
17076 RelationGetRelationName(rel),
17077 get_rel_name(tableId))));
17080 /* Get and lock schema OID and check its permissions. */
17081 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17082 nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17084 /* common checks on switching namespaces */
17085 CheckSetNamespace(oldNspOid, nspOid);
17087 objsMoved = new_object_addresses();
17088 AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17089 free_object_addresses(objsMoved);
17091 ObjectAddressSet(myself, RelationRelationId, relid);
17093 if (oldschema)
17094 *oldschema = oldNspOid;
17096 /* close rel, but keep lock until commit */
17097 relation_close(rel, NoLock);
17099 return myself;
17103 * The guts of relocating a table or materialized view to another namespace:
17104 * besides moving the relation itself, its dependent objects are relocated to
17105 * the new schema.
17107 void
17108 AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
17109 ObjectAddresses *objsMoved)
17111 Relation classRel;
17113 Assert(objsMoved != NULL);
17115 /* OK, modify the pg_class row and pg_depend entry */
17116 classRel = table_open(RelationRelationId, RowExclusiveLock);
17118 AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17119 nspOid, true, objsMoved);
17121 /* Fix the table's row type too, if it has one */
17122 if (OidIsValid(rel->rd_rel->reltype))
17123 AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17124 false, /* isImplicitArray */
17125 false, /* ignoreDependent */
17126 false, /* errorOnTableType */
17127 objsMoved);
17129 /* Fix other dependent stuff */
17130 AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17131 AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17132 objsMoved, AccessExclusiveLock);
17133 AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17134 false, objsMoved);
17136 table_close(classRel, RowExclusiveLock);
17140 * The guts of relocating a relation to another namespace: fix the pg_class
17141 * entry, and the pg_depend entry if any. Caller must already have
17142 * opened and write-locked pg_class.
17144 void
17145 AlterRelationNamespaceInternal(Relation classRel, Oid relOid,
17146 Oid oldNspOid, Oid newNspOid,
17147 bool hasDependEntry,
17148 ObjectAddresses *objsMoved)
17150 HeapTuple classTup;
17151 Form_pg_class classForm;
17152 ObjectAddress thisobj;
17153 bool already_done = false;
17155 classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17156 if (!HeapTupleIsValid(classTup))
17157 elog(ERROR, "cache lookup failed for relation %u", relOid);
17158 classForm = (Form_pg_class) GETSTRUCT(classTup);
17160 Assert(classForm->relnamespace == oldNspOid);
17162 thisobj.classId = RelationRelationId;
17163 thisobj.objectId = relOid;
17164 thisobj.objectSubId = 0;
17167 * If the object has already been moved, don't move it again. If it's
17168 * already in the right place, don't move it, but still fire the object
17169 * access hook.
17171 already_done = object_address_present(&thisobj, objsMoved);
17172 if (!already_done && oldNspOid != newNspOid)
17174 /* check for duplicate name (more friendly than unique-index failure) */
17175 if (get_relname_relid(NameStr(classForm->relname),
17176 newNspOid) != InvalidOid)
17177 ereport(ERROR,
17178 (errcode(ERRCODE_DUPLICATE_TABLE),
17179 errmsg("relation \"%s\" already exists in schema \"%s\"",
17180 NameStr(classForm->relname),
17181 get_namespace_name(newNspOid))));
17183 /* classTup is a copy, so OK to scribble on */
17184 classForm->relnamespace = newNspOid;
17186 CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
17188 /* Update dependency on schema if caller said so */
17189 if (hasDependEntry &&
17190 changeDependencyFor(RelationRelationId,
17191 relOid,
17192 NamespaceRelationId,
17193 oldNspOid,
17194 newNspOid) != 1)
17195 elog(ERROR, "could not change schema dependency for relation \"%s\"",
17196 NameStr(classForm->relname));
17198 if (!already_done)
17200 add_exact_object_address(&thisobj, objsMoved);
17202 InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17205 heap_freetuple(classTup);
17209 * Move all indexes for the specified relation to another namespace.
17211 * Note: we assume adequate permission checking was done by the caller,
17212 * and that the caller has a suitable lock on the owning relation.
17214 static void
17215 AlterIndexNamespaces(Relation classRel, Relation rel,
17216 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
17218 List *indexList;
17219 ListCell *l;
17221 indexList = RelationGetIndexList(rel);
17223 foreach(l, indexList)
17225 Oid indexOid = lfirst_oid(l);
17226 ObjectAddress thisobj;
17228 thisobj.classId = RelationRelationId;
17229 thisobj.objectId = indexOid;
17230 thisobj.objectSubId = 0;
17233 * Note: currently, the index will not have its own dependency on the
17234 * namespace, so we don't need to do changeDependencyFor(). There's no
17235 * row type in pg_type, either.
17237 * XXX this objsMoved test may be pointless -- surely we have a single
17238 * dependency link from a relation to each index?
17240 if (!object_address_present(&thisobj, objsMoved))
17242 AlterRelationNamespaceInternal(classRel, indexOid,
17243 oldNspOid, newNspOid,
17244 false, objsMoved);
17245 add_exact_object_address(&thisobj, objsMoved);
17249 list_free(indexList);
17253 * Move all identity and SERIAL-column sequences of the specified relation to another
17254 * namespace.
17256 * Note: we assume adequate permission checking was done by the caller,
17257 * and that the caller has a suitable lock on the owning relation.
17259 static void
17260 AlterSeqNamespaces(Relation classRel, Relation rel,
17261 Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved,
17262 LOCKMODE lockmode)
17264 Relation depRel;
17265 SysScanDesc scan;
17266 ScanKeyData key[2];
17267 HeapTuple tup;
17270 * SERIAL sequences are those having an auto dependency on one of the
17271 * table's columns (we don't care *which* column, exactly).
17273 depRel = table_open(DependRelationId, AccessShareLock);
17275 ScanKeyInit(&key[0],
17276 Anum_pg_depend_refclassid,
17277 BTEqualStrategyNumber, F_OIDEQ,
17278 ObjectIdGetDatum(RelationRelationId));
17279 ScanKeyInit(&key[1],
17280 Anum_pg_depend_refobjid,
17281 BTEqualStrategyNumber, F_OIDEQ,
17282 ObjectIdGetDatum(RelationGetRelid(rel)));
17283 /* we leave refobjsubid unspecified */
17285 scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17286 NULL, 2, key);
17288 while (HeapTupleIsValid(tup = systable_getnext(scan)))
17290 Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17291 Relation seqRel;
17293 /* skip dependencies other than auto dependencies on columns */
17294 if (depForm->refobjsubid == 0 ||
17295 depForm->classid != RelationRelationId ||
17296 depForm->objsubid != 0 ||
17297 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17298 continue;
17300 /* Use relation_open just in case it's an index */
17301 seqRel = relation_open(depForm->objid, lockmode);
17303 /* skip non-sequence relations */
17304 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17306 /* No need to keep the lock */
17307 relation_close(seqRel, lockmode);
17308 continue;
17311 /* Fix the pg_class and pg_depend entries */
17312 AlterRelationNamespaceInternal(classRel, depForm->objid,
17313 oldNspOid, newNspOid,
17314 true, objsMoved);
17317 * Sequences used to have entries in pg_type, but no longer do. If we
17318 * ever re-instate that, we'll need to move the pg_type entry to the
17319 * new namespace, too (using AlterTypeNamespaceInternal).
17321 Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
17323 /* Now we can close it. Keep the lock till end of transaction. */
17324 relation_close(seqRel, NoLock);
17327 systable_endscan(scan);
17329 relation_close(depRel, AccessShareLock);
17334 * This code supports
17335 * CREATE TEMP TABLE ... ON COMMIT { DROP | PRESERVE ROWS | DELETE ROWS }
17337 * Because we only support this for TEMP tables, it's sufficient to remember
17338 * the state in a backend-local data structure.
17342 * Register a newly-created relation's ON COMMIT action.
17344 void
17345 register_on_commit_action(Oid relid, OnCommitAction action)
17347 OnCommitItem *oc;
17348 MemoryContext oldcxt;
17351 * We needn't bother registering the relation unless there is an ON COMMIT
17352 * action we need to take.
17354 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
17355 return;
17357 oldcxt = MemoryContextSwitchTo(CacheMemoryContext);
17359 oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
17360 oc->relid = relid;
17361 oc->oncommit = action;
17362 oc->creating_subid = GetCurrentSubTransactionId();
17363 oc->deleting_subid = InvalidSubTransactionId;
17366 * We use lcons() here so that ON COMMIT actions are processed in reverse
17367 * order of registration. That might not be essential but it seems
17368 * reasonable.
17370 on_commits = lcons(oc, on_commits);
17372 MemoryContextSwitchTo(oldcxt);
17376 * Unregister any ON COMMIT action when a relation is deleted.
17378 * Actually, we only mark the OnCommitItem entry as to be deleted after commit.
17380 void
17381 remove_on_commit_action(Oid relid)
17383 ListCell *l;
17385 foreach(l, on_commits)
17387 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17389 if (oc->relid == relid)
17391 oc->deleting_subid = GetCurrentSubTransactionId();
17392 break;
17398 * Perform ON COMMIT actions.
17400 * This is invoked just before actually committing, since it's possible
17401 * to encounter errors.
17403 void
17404 PreCommit_on_commit_actions(void)
17406 ListCell *l;
17407 List *oids_to_truncate = NIL;
17408 List *oids_to_drop = NIL;
17410 foreach(l, on_commits)
17412 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17414 /* Ignore entry if already dropped in this xact */
17415 if (oc->deleting_subid != InvalidSubTransactionId)
17416 continue;
17418 switch (oc->oncommit)
17420 case ONCOMMIT_NOOP:
17421 case ONCOMMIT_PRESERVE_ROWS:
17422 /* Do nothing (there shouldn't be such entries, actually) */
17423 break;
17424 case ONCOMMIT_DELETE_ROWS:
17427 * If this transaction hasn't accessed any temporary
17428 * relations, we can skip truncating ON COMMIT DELETE ROWS
17429 * tables, as they must still be empty.
17431 if ((MyXactFlags & XACT_FLAGS_ACCESSEDTEMPNAMESPACE))
17432 oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
17433 break;
17434 case ONCOMMIT_DROP:
17435 oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
17436 break;
17441 * Truncate relations before dropping so that all dependencies between
17442 * relations are removed after they are worked on. Doing it like this
17443 * might be a waste as it is possible that a relation being truncated will
17444 * be dropped anyway due to its parent being dropped, but this makes the
17445 * code more robust because of not having to re-check that the relation
17446 * exists at truncation time.
17448 if (oids_to_truncate != NIL)
17449 heap_truncate(oids_to_truncate);
17451 if (oids_to_drop != NIL)
17453 ObjectAddresses *targetObjects = new_object_addresses();
17455 foreach(l, oids_to_drop)
17457 ObjectAddress object;
17459 object.classId = RelationRelationId;
17460 object.objectId = lfirst_oid(l);
17461 object.objectSubId = 0;
17463 Assert(!object_address_present(&object, targetObjects));
17465 add_exact_object_address(&object, targetObjects);
17469 * Object deletion might involve toast table access (to clean up
17470 * toasted catalog entries), so ensure we have a valid snapshot.
17472 PushActiveSnapshot(GetTransactionSnapshot());
17475 * Since this is an automatic drop, rather than one directly initiated
17476 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17478 performMultipleDeletions(targetObjects, DROP_CASCADE,
17479 PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
17481 PopActiveSnapshot();
17483 #ifdef USE_ASSERT_CHECKING
17486 * Note that table deletion will call remove_on_commit_action, so the
17487 * entry should get marked as deleted.
17489 foreach(l, on_commits)
17491 OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17493 if (oc->oncommit != ONCOMMIT_DROP)
17494 continue;
17496 Assert(oc->deleting_subid != InvalidSubTransactionId);
17498 #endif
17503 * Post-commit or post-abort cleanup for ON COMMIT management.
17505 * All we do here is remove no-longer-needed OnCommitItem entries.
17507 * During commit, remove entries that were deleted during this transaction;
17508 * during abort, remove those created during this transaction.
17510 void
17511 AtEOXact_on_commit_actions(bool isCommit)
17513 ListCell *cur_item;
17515 foreach(cur_item, on_commits)
17517 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17519 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
17520 oc->creating_subid != InvalidSubTransactionId)
17522 /* cur_item must be removed */
17523 on_commits = foreach_delete_current(on_commits, cur_item);
17524 pfree(oc);
17526 else
17528 /* cur_item must be preserved */
17529 oc->creating_subid = InvalidSubTransactionId;
17530 oc->deleting_subid = InvalidSubTransactionId;
17536 * Post-subcommit or post-subabort cleanup for ON COMMIT management.
17538 * During subabort, we can immediately remove entries created during this
17539 * subtransaction. During subcommit, just relabel entries marked during
17540 * this subtransaction as being the parent's responsibility.
17542 void
17543 AtEOSubXact_on_commit_actions(bool isCommit, SubTransactionId mySubid,
17544 SubTransactionId parentSubid)
17546 ListCell *cur_item;
17548 foreach(cur_item, on_commits)
17550 OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17552 if (!isCommit && oc->creating_subid == mySubid)
17554 /* cur_item must be removed */
17555 on_commits = foreach_delete_current(on_commits, cur_item);
17556 pfree(oc);
17558 else
17560 /* cur_item must be preserved */
17561 if (oc->creating_subid == mySubid)
17562 oc->creating_subid = parentSubid;
17563 if (oc->deleting_subid == mySubid)
17564 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
17570 * This is intended as a callback for RangeVarGetRelidExtended(). It allows
17571 * the relation to be locked only if (1) it's a plain or partitioned table,
17572 * materialized view, or TOAST table and (2) the current user is the owner (or
17573 * the superuser) or has been granted MAINTAIN. This meets the
17574 * permission-checking needs of CLUSTER, REINDEX TABLE, and REFRESH
17575 * MATERIALIZED VIEW; we expose it here so that it can be used by all.
17577 void
17578 RangeVarCallbackMaintainsTable(const RangeVar *relation,
17579 Oid relId, Oid oldRelId, void *arg)
17581 char relkind;
17582 AclResult aclresult;
17584 /* Nothing to do if the relation was not found. */
17585 if (!OidIsValid(relId))
17586 return;
17589 * If the relation does exist, check whether it's an index. But note that
17590 * the relation might have been dropped between the time we did the name
17591 * lookup and now. In that case, there's nothing to do.
17593 relkind = get_rel_relkind(relId);
17594 if (!relkind)
17595 return;
17596 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
17597 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
17598 ereport(ERROR,
17599 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17600 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
17602 /* Check permissions */
17603 aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
17604 if (aclresult != ACLCHECK_OK)
17605 aclcheck_error(aclresult,
17606 get_relkind_objtype(get_rel_relkind(relId)),
17607 relation->relname);
17611 * Callback to RangeVarGetRelidExtended() for TRUNCATE processing.
17613 static void
17614 RangeVarCallbackForTruncate(const RangeVar *relation,
17615 Oid relId, Oid oldRelId, void *arg)
17617 HeapTuple tuple;
17619 /* Nothing to do if the relation was not found. */
17620 if (!OidIsValid(relId))
17621 return;
17623 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17624 if (!HeapTupleIsValid(tuple)) /* should not happen */
17625 elog(ERROR, "cache lookup failed for relation %u", relId);
17627 truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
17628 truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17630 ReleaseSysCache(tuple);
17634 * Callback for RangeVarGetRelidExtended(). Checks that the current user is
17635 * the owner of the relation, or superuser.
17637 void
17638 RangeVarCallbackOwnsRelation(const RangeVar *relation,
17639 Oid relId, Oid oldRelId, void *arg)
17641 HeapTuple tuple;
17643 /* Nothing to do if the relation was not found. */
17644 if (!OidIsValid(relId))
17645 return;
17647 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17648 if (!HeapTupleIsValid(tuple)) /* should not happen */
17649 elog(ERROR, "cache lookup failed for relation %u", relId);
17651 if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17652 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relId)),
17653 relation->relname);
17655 if (!allowSystemTableMods &&
17656 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
17657 ereport(ERROR,
17658 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17659 errmsg("permission denied: \"%s\" is a system catalog",
17660 relation->relname)));
17662 ReleaseSysCache(tuple);
17666 * Common RangeVarGetRelid callback for rename, set schema, and alter table
17667 * processing.
17669 static void
17670 RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
17671 void *arg)
17673 Node *stmt = (Node *) arg;
17674 ObjectType reltype;
17675 HeapTuple tuple;
17676 Form_pg_class classform;
17677 AclResult aclresult;
17678 char relkind;
17680 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
17681 if (!HeapTupleIsValid(tuple))
17682 return; /* concurrently dropped */
17683 classform = (Form_pg_class) GETSTRUCT(tuple);
17684 relkind = classform->relkind;
17686 /* Must own relation. */
17687 if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
17688 aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relid)), rv->relname);
17690 /* No system table modifications unless explicitly allowed. */
17691 if (!allowSystemTableMods && IsSystemClass(relid, classform))
17692 ereport(ERROR,
17693 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17694 errmsg("permission denied: \"%s\" is a system catalog",
17695 rv->relname)));
17698 * Extract the specified relation type from the statement parse tree.
17700 * Also, for ALTER .. RENAME, check permissions: the user must (still)
17701 * have CREATE rights on the containing namespace.
17703 if (IsA(stmt, RenameStmt))
17705 aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17706 GetUserId(), ACL_CREATE);
17707 if (aclresult != ACLCHECK_OK)
17708 aclcheck_error(aclresult, OBJECT_SCHEMA,
17709 get_namespace_name(classform->relnamespace));
17710 reltype = ((RenameStmt *) stmt)->renameType;
17712 else if (IsA(stmt, AlterObjectSchemaStmt))
17713 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17715 else if (IsA(stmt, AlterTableStmt))
17716 reltype = ((AlterTableStmt *) stmt)->objtype;
17717 else
17719 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17720 reltype = OBJECT_TABLE; /* placate compiler */
17724 * For compatibility with prior releases, we allow ALTER TABLE to be used
17725 * with most other types of relations (but not composite types). We allow
17726 * similar flexibility for ALTER INDEX in the case of RENAME, but not
17727 * otherwise. Otherwise, the user must select the correct form of the
17728 * command for the relation at issue.
17730 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
17731 ereport(ERROR,
17732 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17733 errmsg("\"%s\" is not a sequence", rv->relname)));
17735 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
17736 ereport(ERROR,
17737 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17738 errmsg("\"%s\" is not a view", rv->relname)));
17740 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
17741 ereport(ERROR,
17742 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17743 errmsg("\"%s\" is not a materialized view", rv->relname)));
17745 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
17746 ereport(ERROR,
17747 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17748 errmsg("\"%s\" is not a foreign table", rv->relname)));
17750 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
17751 ereport(ERROR,
17752 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17753 errmsg("\"%s\" is not a composite type", rv->relname)));
17755 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17756 relkind != RELKIND_PARTITIONED_INDEX
17757 && !IsA(stmt, RenameStmt))
17758 ereport(ERROR,
17759 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17760 errmsg("\"%s\" is not an index", rv->relname)));
17763 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17764 * TYPE for that.
17766 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
17767 ereport(ERROR,
17768 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17769 errmsg("\"%s\" is a composite type", rv->relname),
17770 /* translator: %s is an SQL ALTER command */
17771 errhint("Use %s instead.",
17772 "ALTER TYPE")));
17775 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17776 * to a different schema, such as indexes and TOAST tables.
17778 if (IsA(stmt, AlterObjectSchemaStmt))
17780 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
17781 ereport(ERROR,
17782 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17783 errmsg("cannot change schema of index \"%s\"",
17784 rv->relname),
17785 errhint("Change the schema of the table instead.")));
17786 else if (relkind == RELKIND_COMPOSITE_TYPE)
17787 ereport(ERROR,
17788 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17789 errmsg("cannot change schema of composite type \"%s\"",
17790 rv->relname),
17791 /* translator: %s is an SQL ALTER command */
17792 errhint("Use %s instead.",
17793 "ALTER TYPE")));
17794 else if (relkind == RELKIND_TOASTVALUE)
17795 ereport(ERROR,
17796 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17797 errmsg("cannot change schema of TOAST table \"%s\"",
17798 rv->relname),
17799 errhint("Change the schema of the table instead.")));
17802 ReleaseSysCache(tuple);
17806 * Transform any expressions present in the partition key
17808 * Returns a transformed PartitionSpec.
17810 static PartitionSpec *
17811 transformPartitionSpec(Relation rel, PartitionSpec *partspec)
17813 PartitionSpec *newspec;
17814 ParseState *pstate;
17815 ParseNamespaceItem *nsitem;
17816 ListCell *l;
17818 newspec = makeNode(PartitionSpec);
17820 newspec->strategy = partspec->strategy;
17821 newspec->partParams = NIL;
17822 newspec->location = partspec->location;
17824 /* Check valid number of columns for strategy */
17825 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
17826 list_length(partspec->partParams) != 1)
17827 ereport(ERROR,
17828 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17829 errmsg("cannot use \"list\" partition strategy with more than one column")));
17832 * Create a dummy ParseState and insert the target relation as its sole
17833 * rangetable entry. We need a ParseState for transformExpr.
17835 pstate = make_parsestate(NULL);
17836 nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
17837 NULL, false, true);
17838 addNSItemToQuery(pstate, nsitem, true, true, true);
17840 /* take care of any partition expressions */
17841 foreach(l, partspec->partParams)
17843 PartitionElem *pelem = lfirst_node(PartitionElem, l);
17845 if (pelem->expr)
17847 /* Copy, to avoid scribbling on the input */
17848 pelem = copyObject(pelem);
17850 /* Now do parse transformation of the expression */
17851 pelem->expr = transformExpr(pstate, pelem->expr,
17852 EXPR_KIND_PARTITION_EXPRESSION);
17854 /* we have to fix its collations too */
17855 assign_expr_collations(pstate, pelem->expr);
17858 newspec->partParams = lappend(newspec->partParams, pelem);
17861 return newspec;
17865 * Compute per-partition-column information from a list of PartitionElems.
17866 * Expressions in the PartitionElems must be parse-analyzed already.
17868 static void
17869 ComputePartitionAttrs(ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs,
17870 List **partexprs, Oid *partopclass, Oid *partcollation,
17871 PartitionStrategy strategy)
17873 int attn;
17874 ListCell *lc;
17875 Oid am_oid;
17877 attn = 0;
17878 foreach(lc, partParams)
17880 PartitionElem *pelem = lfirst_node(PartitionElem, lc);
17881 Oid atttype;
17882 Oid attcollation;
17884 if (pelem->name != NULL)
17886 /* Simple attribute reference */
17887 HeapTuple atttuple;
17888 Form_pg_attribute attform;
17890 atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
17891 pelem->name);
17892 if (!HeapTupleIsValid(atttuple))
17893 ereport(ERROR,
17894 (errcode(ERRCODE_UNDEFINED_COLUMN),
17895 errmsg("column \"%s\" named in partition key does not exist",
17896 pelem->name),
17897 parser_errposition(pstate, pelem->location)));
17898 attform = (Form_pg_attribute) GETSTRUCT(atttuple);
17900 if (attform->attnum <= 0)
17901 ereport(ERROR,
17902 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17903 errmsg("cannot use system column \"%s\" in partition key",
17904 pelem->name),
17905 parser_errposition(pstate, pelem->location)));
17908 * Generated columns cannot work: They are computed after BEFORE
17909 * triggers, but partition routing is done before all triggers.
17911 if (attform->attgenerated)
17912 ereport(ERROR,
17913 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17914 errmsg("cannot use generated column in partition key"),
17915 errdetail("Column \"%s\" is a generated column.",
17916 pelem->name),
17917 parser_errposition(pstate, pelem->location)));
17919 partattrs[attn] = attform->attnum;
17920 atttype = attform->atttypid;
17921 attcollation = attform->attcollation;
17922 ReleaseSysCache(atttuple);
17924 else
17926 /* Expression */
17927 Node *expr = pelem->expr;
17928 char partattname[16];
17930 Assert(expr != NULL);
17931 atttype = exprType(expr);
17932 attcollation = exprCollation(expr);
17935 * The expression must be of a storable type (e.g., not RECORD).
17936 * The test is the same as for whether a table column is of a safe
17937 * type (which is why we needn't check for the non-expression
17938 * case).
17940 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
17941 CheckAttributeType(partattname,
17942 atttype, attcollation,
17943 NIL, CHKATYPE_IS_PARTKEY);
17946 * Strip any top-level COLLATE clause. This ensures that we treat
17947 * "x COLLATE y" and "(x COLLATE y)" alike.
17949 while (IsA(expr, CollateExpr))
17950 expr = (Node *) ((CollateExpr *) expr)->arg;
17952 if (IsA(expr, Var) &&
17953 ((Var *) expr)->varattno > 0)
17956 * User wrote "(column)" or "(column COLLATE something)".
17957 * Treat it like simple attribute anyway.
17959 partattrs[attn] = ((Var *) expr)->varattno;
17961 else
17963 Bitmapset *expr_attrs = NULL;
17964 int i;
17966 partattrs[attn] = 0; /* marks the column as expression */
17967 *partexprs = lappend(*partexprs, expr);
17970 * transformPartitionSpec() should have already rejected
17971 * subqueries, aggregates, window functions, and SRFs, based
17972 * on the EXPR_KIND_ for partition expressions.
17976 * Cannot allow system column references, since that would
17977 * make partition routing impossible: their values won't be
17978 * known yet when we need to do that.
17980 pull_varattnos(expr, 1, &expr_attrs);
17981 for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
17983 if (bms_is_member(i - FirstLowInvalidHeapAttributeNumber,
17984 expr_attrs))
17985 ereport(ERROR,
17986 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17987 errmsg("partition key expressions cannot contain system column references")));
17991 * Generated columns cannot work: They are computed after
17992 * BEFORE triggers, but partition routing is done before all
17993 * triggers.
17995 i = -1;
17996 while ((i = bms_next_member(expr_attrs, i)) >= 0)
17998 AttrNumber attno = i + FirstLowInvalidHeapAttributeNumber;
18000 if (attno > 0 &&
18001 TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18002 ereport(ERROR,
18003 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18004 errmsg("cannot use generated column in partition key"),
18005 errdetail("Column \"%s\" is a generated column.",
18006 get_attname(RelationGetRelid(rel), attno, false)),
18007 parser_errposition(pstate, pelem->location)));
18011 * Preprocess the expression before checking for mutability.
18012 * This is essential for the reasons described in
18013 * contain_mutable_functions_after_planning. However, we call
18014 * expression_planner for ourselves rather than using that
18015 * function, because if constant-folding reduces the
18016 * expression to a constant, we'd like to know that so we can
18017 * complain below.
18019 * Like contain_mutable_functions_after_planning, assume that
18020 * expression_planner won't scribble on its input, so this
18021 * won't affect the partexprs entry we saved above.
18023 expr = (Node *) expression_planner((Expr *) expr);
18026 * Partition expressions cannot contain mutable functions,
18027 * because a given row must always map to the same partition
18028 * as long as there is no change in the partition boundary
18029 * structure.
18031 if (contain_mutable_functions(expr))
18032 ereport(ERROR,
18033 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18034 errmsg("functions in partition key expression must be marked IMMUTABLE")));
18037 * While it is not exactly *wrong* for a partition expression
18038 * to be a constant, it seems better to reject such keys.
18040 if (IsA(expr, Const))
18041 ereport(ERROR,
18042 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18043 errmsg("cannot use constant expression as partition key")));
18048 * Apply collation override if any
18050 if (pelem->collation)
18051 attcollation = get_collation_oid(pelem->collation, false);
18054 * Check we have a collation iff it's a collatable type. The only
18055 * expected failures here are (1) COLLATE applied to a noncollatable
18056 * type, or (2) partition expression had an unresolved collation. But
18057 * we might as well code this to be a complete consistency check.
18059 if (type_is_collatable(atttype))
18061 if (!OidIsValid(attcollation))
18062 ereport(ERROR,
18063 (errcode(ERRCODE_INDETERMINATE_COLLATION),
18064 errmsg("could not determine which collation to use for partition expression"),
18065 errhint("Use the COLLATE clause to set the collation explicitly.")));
18067 else
18069 if (OidIsValid(attcollation))
18070 ereport(ERROR,
18071 (errcode(ERRCODE_DATATYPE_MISMATCH),
18072 errmsg("collations are not supported by type %s",
18073 format_type_be(atttype))));
18076 partcollation[attn] = attcollation;
18079 * Identify the appropriate operator class. For list and range
18080 * partitioning, we use a btree operator class; hash partitioning uses
18081 * a hash operator class.
18083 if (strategy == PARTITION_STRATEGY_HASH)
18084 am_oid = HASH_AM_OID;
18085 else
18086 am_oid = BTREE_AM_OID;
18088 if (!pelem->opclass)
18090 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18092 if (!OidIsValid(partopclass[attn]))
18094 if (strategy == PARTITION_STRATEGY_HASH)
18095 ereport(ERROR,
18096 (errcode(ERRCODE_UNDEFINED_OBJECT),
18097 errmsg("data type %s has no default operator class for access method \"%s\"",
18098 format_type_be(atttype), "hash"),
18099 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18100 else
18101 ereport(ERROR,
18102 (errcode(ERRCODE_UNDEFINED_OBJECT),
18103 errmsg("data type %s has no default operator class for access method \"%s\"",
18104 format_type_be(atttype), "btree"),
18105 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18108 else
18109 partopclass[attn] = ResolveOpClass(pelem->opclass,
18110 atttype,
18111 am_oid == HASH_AM_OID ? "hash" : "btree",
18112 am_oid);
18114 attn++;
18119 * PartConstraintImpliedByRelConstraint
18120 * Do scanrel's existing constraints imply the partition constraint?
18122 * "Existing constraints" include its check constraints and column-level
18123 * not-null constraints. partConstraint describes the partition constraint,
18124 * in implicit-AND form.
18126 bool
18127 PartConstraintImpliedByRelConstraint(Relation scanrel,
18128 List *partConstraint)
18130 List *existConstraint = NIL;
18131 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18132 int i;
18134 if (constr && constr->has_not_null)
18136 int natts = scanrel->rd_att->natts;
18138 for (i = 1; i <= natts; i++)
18140 Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18142 if (att->attnotnull && !att->attisdropped)
18144 NullTest *ntest = makeNode(NullTest);
18146 ntest->arg = (Expr *) makeVar(1,
18148 att->atttypid,
18149 att->atttypmod,
18150 att->attcollation,
18152 ntest->nulltesttype = IS_NOT_NULL;
18155 * argisrow=false is correct even for a composite column,
18156 * because attnotnull does not represent a SQL-spec IS NOT
18157 * NULL test in such a case, just IS DISTINCT FROM NULL.
18159 ntest->argisrow = false;
18160 ntest->location = -1;
18161 existConstraint = lappend(existConstraint, ntest);
18166 return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18170 * ConstraintImpliedByRelConstraint
18171 * Do scanrel's existing constraints imply the given constraint?
18173 * testConstraint is the constraint to validate. provenConstraint is a
18174 * caller-provided list of conditions which this function may assume
18175 * to be true. Both provenConstraint and testConstraint must be in
18176 * implicit-AND form, must only contain immutable clauses, and must
18177 * contain only Vars with varno = 1.
18179 bool
18180 ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
18182 List *existConstraint = list_copy(provenConstraint);
18183 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18184 int num_check,
18187 num_check = (constr != NULL) ? constr->num_check : 0;
18188 for (i = 0; i < num_check; i++)
18190 Node *cexpr;
18193 * If this constraint hasn't been fully validated yet, we must ignore
18194 * it here.
18196 if (!constr->check[i].ccvalid)
18197 continue;
18199 cexpr = stringToNode(constr->check[i].ccbin);
18202 * Run each expression through const-simplification and
18203 * canonicalization. It is necessary, because we will be comparing it
18204 * to similarly-processed partition constraint expressions, and may
18205 * fail to detect valid matches without this.
18207 cexpr = eval_const_expressions(NULL, cexpr);
18208 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18210 existConstraint = list_concat(existConstraint,
18211 make_ands_implicit((Expr *) cexpr));
18215 * Try to make the proof. Since we are comparing CHECK constraints, we
18216 * need to use weak implication, i.e., we assume existConstraint is
18217 * not-false and try to prove the same for testConstraint.
18219 * Note that predicate_implied_by assumes its first argument is known
18220 * immutable. That should always be true for both NOT NULL and partition
18221 * constraints, so we don't test it here.
18223 return predicate_implied_by(testConstraint, existConstraint, true);
18227 * QueuePartitionConstraintValidation
18229 * Add an entry to wqueue to have the given partition constraint validated by
18230 * Phase 3, for the given relation, and all its children.
18232 * We first verify whether the given constraint is implied by pre-existing
18233 * relation constraints; if it is, there's no need to scan the table to
18234 * validate, so don't queue in that case.
18236 static void
18237 QueuePartitionConstraintValidation(List **wqueue, Relation scanrel,
18238 List *partConstraint,
18239 bool validate_default)
18242 * Based on the table's existing constraints, determine whether or not we
18243 * may skip scanning the table.
18245 if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18247 if (!validate_default)
18248 ereport(DEBUG1,
18249 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18250 RelationGetRelationName(scanrel))));
18251 else
18252 ereport(DEBUG1,
18253 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18254 RelationGetRelationName(scanrel))));
18255 return;
18259 * Constraints proved insufficient. For plain relations, queue a
18260 * validation item now; for partitioned tables, recurse to process each
18261 * partition.
18263 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18265 AlteredTableInfo *tab;
18267 /* Grab a work queue entry. */
18268 tab = ATGetQueueEntry(wqueue, scanrel);
18269 Assert(tab->partition_constraint == NULL);
18270 tab->partition_constraint = (Expr *) linitial(partConstraint);
18271 tab->validate_default = validate_default;
18273 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18275 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
18276 int i;
18278 for (i = 0; i < partdesc->nparts; i++)
18280 Relation part_rel;
18281 List *thisPartConstraint;
18284 * This is the minimum lock we need to prevent deadlocks.
18286 part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18289 * Adjust the constraint for scanrel so that it matches this
18290 * partition's attribute numbers.
18292 thisPartConstraint =
18293 map_partition_varattnos(partConstraint, 1,
18294 part_rel, scanrel);
18296 QueuePartitionConstraintValidation(wqueue, part_rel,
18297 thisPartConstraint,
18298 validate_default);
18299 table_close(part_rel, NoLock); /* keep lock till commit */
18305 * attachPartitionTable: attach a new partition to the partitioned table
18307 * wqueue: the ALTER TABLE work queue; can be NULL when not running as part
18308 * of an ALTER TABLE sequence.
18309 * rel: partitioned relation;
18310 * attachrel: relation of attached partition;
18311 * bound: bounds of attached relation.
18313 static void
18314 attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
18316 /* OK to create inheritance. Rest of the checks performed there */
18317 CreateInheritance(attachrel, rel, true);
18319 /* Update the pg_class entry. */
18320 StorePartitionBound(attachrel, rel, bound);
18322 /* Ensure there exists a correct set of indexes in the partition. */
18323 AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
18325 /* and triggers */
18326 CloneRowTriggersToPartition(rel, attachrel);
18329 * Clone foreign key constraints. Callee is responsible for setting up
18330 * for phase 3 constraint verification.
18332 CloneForeignKeyConstraints(wqueue, rel, attachrel);
18336 * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
18338 * Return the address of the newly attached partition.
18340 static ObjectAddress
18341 ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd,
18342 AlterTableUtilityContext *context)
18344 Relation attachrel,
18345 catalog;
18346 List *attachrel_children;
18347 List *partConstraint;
18348 SysScanDesc scan;
18349 ScanKeyData skey;
18350 AttrNumber attno;
18351 int natts;
18352 TupleDesc tupleDesc;
18353 ObjectAddress address;
18354 const char *trigger_name;
18355 Oid defaultPartOid;
18356 List *partBoundConstraint;
18357 ParseState *pstate = make_parsestate(NULL);
18359 pstate->p_sourcetext = context->queryString;
18362 * We must lock the default partition if one exists, because attaching a
18363 * new partition will change its partition constraint.
18365 defaultPartOid =
18366 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
18367 if (OidIsValid(defaultPartOid))
18368 LockRelationOid(defaultPartOid, AccessExclusiveLock);
18370 attachrel = table_openrv(cmd->name, AccessExclusiveLock);
18373 * XXX I think it'd be a good idea to grab locks on all tables referenced
18374 * by FKs at this point also.
18378 * Must be owner of both parent and source table -- parent was checked by
18379 * ATSimplePermissions call in ATPrepCmd
18381 ATSimplePermissions(AT_AttachPartition, attachrel, ATT_TABLE | ATT_FOREIGN_TABLE);
18383 /* A partition can only have one parent */
18384 if (attachrel->rd_rel->relispartition)
18385 ereport(ERROR,
18386 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18387 errmsg("\"%s\" is already a partition",
18388 RelationGetRelationName(attachrel))));
18390 if (OidIsValid(attachrel->rd_rel->reloftype))
18391 ereport(ERROR,
18392 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18393 errmsg("cannot attach a typed table as partition")));
18396 * Table being attached should not already be part of inheritance; either
18397 * as a child table...
18399 catalog = table_open(InheritsRelationId, AccessShareLock);
18400 ScanKeyInit(&skey,
18401 Anum_pg_inherits_inhrelid,
18402 BTEqualStrategyNumber, F_OIDEQ,
18403 ObjectIdGetDatum(RelationGetRelid(attachrel)));
18404 scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
18405 NULL, 1, &skey);
18406 if (HeapTupleIsValid(systable_getnext(scan)))
18407 ereport(ERROR,
18408 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18409 errmsg("cannot attach inheritance child as partition")));
18410 systable_endscan(scan);
18412 /* ...or as a parent table (except the case when it is partitioned) */
18413 ScanKeyInit(&skey,
18414 Anum_pg_inherits_inhparent,
18415 BTEqualStrategyNumber, F_OIDEQ,
18416 ObjectIdGetDatum(RelationGetRelid(attachrel)));
18417 scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
18418 1, &skey);
18419 if (HeapTupleIsValid(systable_getnext(scan)) &&
18420 attachrel->rd_rel->relkind == RELKIND_RELATION)
18421 ereport(ERROR,
18422 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18423 errmsg("cannot attach inheritance parent as partition")));
18424 systable_endscan(scan);
18425 table_close(catalog, AccessShareLock);
18428 * Prevent circularity by seeing if rel is a partition of attachrel. (In
18429 * particular, this disallows making a rel a partition of itself.)
18431 * We do that by checking if rel is a member of the list of attachrel's
18432 * partitions provided the latter is partitioned at all. We want to avoid
18433 * having to construct this list again, so we request the strongest lock
18434 * on all partitions. We need the strongest lock, because we may decide
18435 * to scan them if we find out that the table being attached (or its leaf
18436 * partitions) may contain rows that violate the partition constraint. If
18437 * the table has a constraint that would prevent such rows, which by
18438 * definition is present in all the partitions, we need not scan the
18439 * table, nor its partitions. But we cannot risk a deadlock by taking a
18440 * weaker lock now and the stronger one only when needed.
18442 attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
18443 AccessExclusiveLock, NULL);
18444 if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
18445 ereport(ERROR,
18446 (errcode(ERRCODE_DUPLICATE_TABLE),
18447 errmsg("circular inheritance not allowed"),
18448 errdetail("\"%s\" is already a child of \"%s\".",
18449 RelationGetRelationName(rel),
18450 RelationGetRelationName(attachrel))));
18452 /* If the parent is permanent, so must be all of its partitions. */
18453 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
18454 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
18455 ereport(ERROR,
18456 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18457 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18458 RelationGetRelationName(rel))));
18460 /* Temp parent cannot have a partition that is itself not a temp */
18461 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18462 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
18463 ereport(ERROR,
18464 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18465 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18466 RelationGetRelationName(rel))));
18468 /* If the parent is temp, it must belong to this session */
18469 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18470 !rel->rd_islocaltemp)
18471 ereport(ERROR,
18472 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18473 errmsg("cannot attach as partition of temporary relation of another session")));
18475 /* Ditto for the partition */
18476 if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18477 !attachrel->rd_islocaltemp)
18478 ereport(ERROR,
18479 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18480 errmsg("cannot attach temporary relation of another session as partition")));
18483 * Check if attachrel has any identity columns or any columns that aren't
18484 * in the parent.
18486 tupleDesc = RelationGetDescr(attachrel);
18487 natts = tupleDesc->natts;
18488 for (attno = 1; attno <= natts; attno++)
18490 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
18491 char *attributeName = NameStr(attribute->attname);
18493 /* Ignore dropped */
18494 if (attribute->attisdropped)
18495 continue;
18497 if (attribute->attidentity)
18498 ereport(ERROR,
18499 errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18500 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
18501 RelationGetRelationName(attachrel), attributeName),
18502 errdetail("The new partition may not contain an identity column."));
18504 /* Try to find the column in parent (matching on column name) */
18505 if (!SearchSysCacheExists2(ATTNAME,
18506 ObjectIdGetDatum(RelationGetRelid(rel)),
18507 CStringGetDatum(attributeName)))
18508 ereport(ERROR,
18509 (errcode(ERRCODE_DATATYPE_MISMATCH),
18510 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18511 RelationGetRelationName(attachrel), attributeName,
18512 RelationGetRelationName(rel)),
18513 errdetail("The new partition may contain only the columns present in parent.")));
18517 * If child_rel has row-level triggers with transition tables, we
18518 * currently don't allow it to become a partition. See also prohibitions
18519 * in ATExecAddInherit() and CreateTrigger().
18521 trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
18522 if (trigger_name != NULL)
18523 ereport(ERROR,
18524 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18525 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18526 trigger_name, RelationGetRelationName(attachrel)),
18527 errdetail("ROW triggers with transition tables are not supported on partitions.")));
18530 * Check that the new partition's bound is valid and does not overlap any
18531 * of existing partitions of the parent - note that it does not return on
18532 * error.
18534 check_new_partition_bound(RelationGetRelationName(attachrel), rel,
18535 cmd->bound, pstate);
18537 /* Attach a new partition to the partitioned table. */
18538 attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
18541 * Generate partition constraint from the partition bound specification.
18542 * If the parent itself is a partition, make sure to include its
18543 * constraint as well.
18545 partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
18546 partConstraint = list_concat(partBoundConstraint,
18547 RelationGetPartitionQual(rel));
18549 /* Skip validation if there are no constraints to validate. */
18550 if (partConstraint)
18553 * Run the partition quals through const-simplification similar to
18554 * check constraints. We skip canonicalize_qual, though, because
18555 * partition quals should be in canonical form already.
18557 partConstraint =
18558 (List *) eval_const_expressions(NULL,
18559 (Node *) partConstraint);
18561 /* XXX this sure looks wrong */
18562 partConstraint = list_make1(make_ands_explicit(partConstraint));
18565 * Adjust the generated constraint to match this partition's attribute
18566 * numbers.
18568 partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
18569 rel);
18571 /* Validate partition constraints against the table being attached. */
18572 QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
18573 false);
18577 * If we're attaching a partition other than the default partition and a
18578 * default one exists, then that partition's partition constraint changes,
18579 * so add an entry to the work queue to validate it, too. (We must not do
18580 * this when the partition being attached is the default one; we already
18581 * did it above!)
18583 if (OidIsValid(defaultPartOid))
18585 Relation defaultrel;
18586 List *defPartConstraint;
18588 Assert(!cmd->bound->is_default);
18590 /* we already hold a lock on the default partition */
18591 defaultrel = table_open(defaultPartOid, NoLock);
18592 defPartConstraint =
18593 get_proposed_default_constraint(partBoundConstraint);
18596 * Map the Vars in the constraint expression from rel's attnos to
18597 * defaultrel's.
18599 defPartConstraint =
18600 map_partition_varattnos(defPartConstraint,
18601 1, defaultrel, rel);
18602 QueuePartitionConstraintValidation(wqueue, defaultrel,
18603 defPartConstraint, true);
18605 /* keep our lock until commit. */
18606 table_close(defaultrel, NoLock);
18609 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
18612 * If the partition we just attached is partitioned itself, invalidate
18613 * relcache for all descendent partitions too to ensure that their
18614 * rd_partcheck expression trees are rebuilt; partitions already locked at
18615 * the beginning of this function.
18617 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18619 ListCell *l;
18621 foreach(l, attachrel_children)
18623 CacheInvalidateRelcacheByRelid(lfirst_oid(l));
18627 /* keep our lock until commit */
18628 table_close(attachrel, NoLock);
18630 return address;
18634 * AttachPartitionEnsureIndexes
18635 * subroutine for ATExecAttachPartition to create/match indexes
18637 * Enforce the indexing rule for partitioned tables during ALTER TABLE / ATTACH
18638 * PARTITION: every partition must have an index attached to each index on the
18639 * partitioned table.
18641 static void
18642 AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
18644 List *idxes;
18645 List *attachRelIdxs;
18646 Relation *attachrelIdxRels;
18647 IndexInfo **attachInfos;
18648 ListCell *cell;
18649 MemoryContext cxt;
18650 MemoryContext oldcxt;
18652 cxt = AllocSetContextCreate(CurrentMemoryContext,
18653 "AttachPartitionEnsureIndexes",
18654 ALLOCSET_DEFAULT_SIZES);
18655 oldcxt = MemoryContextSwitchTo(cxt);
18657 idxes = RelationGetIndexList(rel);
18658 attachRelIdxs = RelationGetIndexList(attachrel);
18659 attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18660 attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18662 /* Build arrays of all existing indexes and their IndexInfos */
18663 foreach_oid(cldIdxId, attachRelIdxs)
18665 int i = foreach_current_index(cldIdxId);
18667 attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18668 attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18672 * If we're attaching a foreign table, we must fail if any of the indexes
18673 * is a constraint index; otherwise, there's nothing to do here. Do this
18674 * before starting work, to avoid wasting the effort of building a few
18675 * non-unique indexes before coming across a unique one.
18677 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18679 foreach(cell, idxes)
18681 Oid idx = lfirst_oid(cell);
18682 Relation idxRel = index_open(idx, AccessShareLock);
18684 if (idxRel->rd_index->indisunique ||
18685 idxRel->rd_index->indisprimary)
18686 ereport(ERROR,
18687 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18688 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18689 RelationGetRelationName(attachrel),
18690 RelationGetRelationName(rel)),
18691 errdetail("Partitioned table \"%s\" contains unique indexes.",
18692 RelationGetRelationName(rel))));
18693 index_close(idxRel, AccessShareLock);
18696 goto out;
18700 * For each index on the partitioned table, find a matching one in the
18701 * partition-to-be; if one is not found, create one.
18703 foreach(cell, idxes)
18705 Oid idx = lfirst_oid(cell);
18706 Relation idxRel = index_open(idx, AccessShareLock);
18707 IndexInfo *info;
18708 AttrMap *attmap;
18709 bool found = false;
18710 Oid constraintOid;
18713 * Ignore indexes in the partitioned table other than partitioned
18714 * indexes.
18716 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18718 index_close(idxRel, AccessShareLock);
18719 continue;
18722 /* construct an indexinfo to compare existing indexes against */
18723 info = BuildIndexInfo(idxRel);
18724 attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18725 RelationGetDescr(rel),
18726 false);
18727 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18730 * Scan the list of existing indexes in the partition-to-be, and mark
18731 * the first matching, valid, unattached one we find, if any, as
18732 * partition of the parent index. If we find one, we're done.
18734 for (int i = 0; i < list_length(attachRelIdxs); i++)
18736 Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18737 Oid cldConstrOid = InvalidOid;
18739 /* does this index have a parent? if so, can't use it */
18740 if (attachrelIdxRels[i]->rd_rel->relispartition)
18741 continue;
18743 /* If this index is invalid, can't use it */
18744 if (!attachrelIdxRels[i]->rd_index->indisvalid)
18745 continue;
18747 if (CompareIndexInfo(attachInfos[i], info,
18748 attachrelIdxRels[i]->rd_indcollation,
18749 idxRel->rd_indcollation,
18750 attachrelIdxRels[i]->rd_opfamily,
18751 idxRel->rd_opfamily,
18752 attmap))
18755 * If this index is being created in the parent because of a
18756 * constraint, then the child needs to have a constraint also,
18757 * so look for one. If there is no such constraint, this
18758 * index is no good, so keep looking.
18760 if (OidIsValid(constraintOid))
18762 cldConstrOid =
18763 get_relation_idx_constraint_oid(RelationGetRelid(attachrel),
18764 cldIdxId);
18765 /* no dice */
18766 if (!OidIsValid(cldConstrOid))
18767 continue;
18769 /* Ensure they're both the same type of constraint */
18770 if (get_constraint_type(constraintOid) !=
18771 get_constraint_type(cldConstrOid))
18772 continue;
18775 /* bingo. */
18776 IndexSetParentIndex(attachrelIdxRels[i], idx);
18777 if (OidIsValid(constraintOid))
18778 ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18779 RelationGetRelid(attachrel));
18780 found = true;
18782 CommandCounterIncrement();
18783 break;
18788 * If no suitable index was found in the partition-to-be, create one
18789 * now.
18791 if (!found)
18793 IndexStmt *stmt;
18794 Oid conOid;
18796 stmt = generateClonedIndexStmt(NULL,
18797 idxRel, attmap,
18798 &conOid);
18799 DefineIndex(RelationGetRelid(attachrel), stmt, InvalidOid,
18800 RelationGetRelid(idxRel),
18801 conOid,
18803 true, false, false, false, false);
18806 index_close(idxRel, AccessShareLock);
18809 out:
18810 /* Clean up. */
18811 for (int i = 0; i < list_length(attachRelIdxs); i++)
18812 index_close(attachrelIdxRels[i], AccessShareLock);
18813 MemoryContextSwitchTo(oldcxt);
18814 MemoryContextDelete(cxt);
18818 * CloneRowTriggersToPartition
18819 * subroutine for ATExecAttachPartition/DefineRelation to create row
18820 * triggers on partitions
18822 static void
18823 CloneRowTriggersToPartition(Relation parent, Relation partition)
18825 Relation pg_trigger;
18826 ScanKeyData key;
18827 SysScanDesc scan;
18828 HeapTuple tuple;
18829 MemoryContext perTupCxt;
18831 ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18832 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
18833 pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
18834 scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
18835 true, NULL, 1, &key);
18837 perTupCxt = AllocSetContextCreate(CurrentMemoryContext,
18838 "clone trig", ALLOCSET_SMALL_SIZES);
18840 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18842 Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
18843 CreateTrigStmt *trigStmt;
18844 Node *qual = NULL;
18845 Datum value;
18846 bool isnull;
18847 List *cols = NIL;
18848 List *trigargs = NIL;
18849 MemoryContext oldcxt;
18852 * Ignore statement-level triggers; those are not cloned.
18854 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
18855 continue;
18858 * Don't clone internal triggers, because the constraint cloning code
18859 * will.
18861 if (trigForm->tgisinternal)
18862 continue;
18865 * Complain if we find an unexpected trigger type.
18867 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
18868 !TRIGGER_FOR_AFTER(trigForm->tgtype))
18869 elog(ERROR, "unexpected trigger \"%s\" found",
18870 NameStr(trigForm->tgname));
18872 /* Use short-lived context for CREATE TRIGGER */
18873 oldcxt = MemoryContextSwitchTo(perTupCxt);
18876 * If there is a WHEN clause, generate a 'cooked' version of it that's
18877 * appropriate for the partition.
18879 value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
18880 RelationGetDescr(pg_trigger), &isnull);
18881 if (!isnull)
18883 qual = stringToNode(TextDatumGetCString(value));
18884 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
18885 partition, parent);
18886 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
18887 partition, parent);
18891 * If there is a column list, transform it to a list of column names.
18892 * Note we don't need to map this list in any way ...
18894 if (trigForm->tgattr.dim1 > 0)
18896 int i;
18898 for (i = 0; i < trigForm->tgattr.dim1; i++)
18900 Form_pg_attribute col;
18902 col = TupleDescAttr(parent->rd_att,
18903 trigForm->tgattr.values[i] - 1);
18904 cols = lappend(cols,
18905 makeString(pstrdup(NameStr(col->attname))));
18909 /* Reconstruct trigger arguments list. */
18910 if (trigForm->tgnargs > 0)
18912 char *p;
18914 value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
18915 RelationGetDescr(pg_trigger), &isnull);
18916 if (isnull)
18917 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18918 NameStr(trigForm->tgname), RelationGetRelationName(partition));
18920 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
18922 for (int i = 0; i < trigForm->tgnargs; i++)
18924 trigargs = lappend(trigargs, makeString(pstrdup(p)));
18925 p += strlen(p) + 1;
18929 trigStmt = makeNode(CreateTrigStmt);
18930 trigStmt->replace = false;
18931 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
18932 trigStmt->trigname = NameStr(trigForm->tgname);
18933 trigStmt->relation = NULL;
18934 trigStmt->funcname = NULL; /* passed separately */
18935 trigStmt->args = trigargs;
18936 trigStmt->row = true;
18937 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
18938 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
18939 trigStmt->columns = cols;
18940 trigStmt->whenClause = NULL; /* passed separately */
18941 trigStmt->transitionRels = NIL; /* not supported at present */
18942 trigStmt->deferrable = trigForm->tgdeferrable;
18943 trigStmt->initdeferred = trigForm->tginitdeferred;
18944 trigStmt->constrrel = NULL; /* passed separately */
18946 CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
18947 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
18948 trigForm->tgfoid, trigForm->oid, qual,
18949 false, true, trigForm->tgenabled);
18951 MemoryContextSwitchTo(oldcxt);
18952 MemoryContextReset(perTupCxt);
18955 MemoryContextDelete(perTupCxt);
18957 systable_endscan(scan);
18958 table_close(pg_trigger, RowExclusiveLock);
18962 * ALTER TABLE DETACH PARTITION
18964 * Return the address of the relation that is no longer a partition of rel.
18966 * If concurrent mode is requested, we run in two transactions. A side-
18967 * effect is that this command cannot run in a multi-part ALTER TABLE.
18968 * Currently, that's enforced by the grammar.
18970 * The strategy for concurrency is to first modify the partition's
18971 * pg_inherit catalog row to make it visible to everyone that the
18972 * partition is detached, lock the partition against writes, and commit
18973 * the transaction; anyone who requests the partition descriptor from
18974 * that point onwards has to ignore such a partition. In a second
18975 * transaction, we wait until all transactions that could have seen the
18976 * partition as attached are gone, then we remove the rest of partition
18977 * metadata (pg_inherits and pg_class.relpartbounds).
18979 static ObjectAddress
18980 ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
18981 RangeVar *name, bool concurrent)
18983 Relation partRel;
18984 ObjectAddress address;
18985 Oid defaultPartOid;
18988 * We must lock the default partition, because detaching this partition
18989 * will change its partition constraint.
18991 defaultPartOid =
18992 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
18993 if (OidIsValid(defaultPartOid))
18996 * Concurrent detaching when a default partition exists is not
18997 * supported. The main problem is that the default partition
18998 * constraint would change. And there's a definitional problem: what
18999 * should happen to the tuples that are being inserted that belong to
19000 * the partition being detached? Putting them on the partition being
19001 * detached would be wrong, since they'd become "lost" after the
19002 * detaching completes but we cannot put them in the default partition
19003 * either until we alter its partition constraint.
19005 * I think we could solve this problem if we effected the constraint
19006 * change before committing the first transaction. But the lock would
19007 * have to remain AEL and it would cause concurrent query planning to
19008 * be blocked, so changing it that way would be even worse.
19010 if (concurrent)
19011 ereport(ERROR,
19012 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19013 errmsg("cannot detach partitions concurrently when a default partition exists")));
19014 LockRelationOid(defaultPartOid, AccessExclusiveLock);
19018 * In concurrent mode, the partition is locked with share-update-exclusive
19019 * in the first transaction. This allows concurrent transactions to be
19020 * doing DML to the partition.
19022 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19023 AccessExclusiveLock);
19026 * Check inheritance conditions and either delete the pg_inherits row (in
19027 * non-concurrent mode) or just set the inhdetachpending flag.
19029 if (!concurrent)
19030 RemoveInheritance(partRel, rel, false);
19031 else
19032 MarkInheritDetached(partRel, rel);
19035 * Ensure that foreign keys still hold after this detach. This keeps
19036 * locks on the referencing tables, which prevents concurrent transactions
19037 * from adding rows that we wouldn't see. For this to work in concurrent
19038 * mode, it is critical that the partition appears as no longer attached
19039 * for the RI queries as soon as the first transaction commits.
19041 ATDetachCheckNoForeignKeyRefs(partRel);
19044 * Concurrent mode has to work harder; first we add a new constraint to
19045 * the partition that matches the partition constraint. Then we close our
19046 * existing transaction, and in a new one wait for all processes to catch
19047 * up on the catalog updates we've done so far; at that point we can
19048 * complete the operation.
19050 if (concurrent)
19052 Oid partrelid,
19053 parentrelid;
19054 LOCKTAG tag;
19055 char *parentrelname;
19056 char *partrelname;
19059 * Add a new constraint to the partition being detached, which
19060 * supplants the partition constraint (unless there is one already).
19062 DetachAddConstraintIfNeeded(wqueue, partRel);
19065 * We're almost done now; the only traces that remain are the
19066 * pg_inherits tuple and the partition's relpartbounds. Before we can
19067 * remove those, we need to wait until all transactions that know that
19068 * this is a partition are gone.
19072 * Remember relation OIDs to re-acquire them later; and relation names
19073 * too, for error messages if something is dropped in between.
19075 partrelid = RelationGetRelid(partRel);
19076 parentrelid = RelationGetRelid(rel);
19077 parentrelname = MemoryContextStrdup(PortalContext,
19078 RelationGetRelationName(rel));
19079 partrelname = MemoryContextStrdup(PortalContext,
19080 RelationGetRelationName(partRel));
19082 /* Invalidate relcache entries for the parent -- must be before close */
19083 CacheInvalidateRelcache(rel);
19085 table_close(partRel, NoLock);
19086 table_close(rel, NoLock);
19087 tab->rel = NULL;
19089 /* Make updated catalog entry visible */
19090 PopActiveSnapshot();
19091 CommitTransactionCommand();
19093 StartTransactionCommand();
19096 * Now wait. This ensures that all queries that were planned
19097 * including the partition are finished before we remove the rest of
19098 * catalog entries. We don't need or indeed want to acquire this
19099 * lock, though -- that would block later queries.
19101 * We don't need to concern ourselves with waiting for a lock on the
19102 * partition itself, since we will acquire AccessExclusiveLock below.
19104 SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19105 WaitForLockersMultiple(list_make1(&tag), AccessExclusiveLock, false);
19108 * Now acquire locks in both relations again. Note they may have been
19109 * removed in the meantime, so care is required.
19111 rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19112 partRel = try_relation_open(partrelid, AccessExclusiveLock);
19114 /* If the relations aren't there, something bad happened; bail out */
19115 if (rel == NULL)
19117 if (partRel != NULL) /* shouldn't happen */
19118 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19119 partrelname);
19120 ereport(ERROR,
19121 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19122 errmsg("partitioned table \"%s\" was removed concurrently",
19123 parentrelname)));
19125 if (partRel == NULL)
19126 ereport(ERROR,
19127 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19128 errmsg("partition \"%s\" was removed concurrently", partrelname)));
19130 tab->rel = rel;
19133 /* Do the final part of detaching */
19134 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19136 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19138 /* keep our lock until commit */
19139 table_close(partRel, NoLock);
19141 return address;
19145 * Second part of ALTER TABLE .. DETACH.
19147 * This is separate so that it can be run independently when the second
19148 * transaction of the concurrent algorithm fails (crash or abort).
19150 static void
19151 DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent,
19152 Oid defaultPartOid)
19154 Relation classRel;
19155 List *fks;
19156 ListCell *cell;
19157 List *indexes;
19158 Datum new_val[Natts_pg_class];
19159 bool new_null[Natts_pg_class],
19160 new_repl[Natts_pg_class];
19161 HeapTuple tuple,
19162 newtuple;
19163 Relation trigrel = NULL;
19165 if (concurrent)
19168 * We can remove the pg_inherits row now. (In the non-concurrent case,
19169 * this was already done).
19171 RemoveInheritance(partRel, rel, true);
19174 /* Drop any triggers that were cloned on creation/attach. */
19175 DropClonedTriggersFromPartition(RelationGetRelid(partRel));
19178 * Detach any foreign keys that are inherited. This includes creating
19179 * additional action triggers.
19181 fks = copyObject(RelationGetFKeyList(partRel));
19182 if (fks != NIL)
19183 trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19184 foreach(cell, fks)
19186 ForeignKeyCacheInfo *fk = lfirst(cell);
19187 HeapTuple contup;
19188 Form_pg_constraint conform;
19189 Constraint *fkconstraint;
19190 Oid insertTriggerOid,
19191 updateTriggerOid;
19193 contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19194 if (!HeapTupleIsValid(contup))
19195 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19196 conform = (Form_pg_constraint) GETSTRUCT(contup);
19198 /* consider only the inherited foreign keys */
19199 if (conform->contype != CONSTRAINT_FOREIGN ||
19200 !OidIsValid(conform->conparentid))
19202 ReleaseSysCache(contup);
19203 continue;
19206 /* unset conparentid and adjust conislocal, coninhcount, etc. */
19207 ConstraintSetParentConstraint(fk->conoid, InvalidOid, InvalidOid);
19210 * Also, look up the partition's "check" triggers corresponding to the
19211 * constraint being detached and detach them from the parent triggers.
19213 GetForeignKeyCheckTriggers(trigrel,
19214 fk->conoid, fk->confrelid, fk->conrelid,
19215 &insertTriggerOid, &updateTriggerOid);
19216 Assert(OidIsValid(insertTriggerOid));
19217 TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19218 RelationGetRelid(partRel));
19219 Assert(OidIsValid(updateTriggerOid));
19220 TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19221 RelationGetRelid(partRel));
19224 * Make the action triggers on the referenced relation. When this was
19225 * a partition the action triggers pointed to the parent rel (they
19226 * still do), but now we need separate ones of our own.
19228 fkconstraint = makeNode(Constraint);
19229 fkconstraint->contype = CONSTRAINT_FOREIGN;
19230 fkconstraint->conname = pstrdup(NameStr(conform->conname));
19231 fkconstraint->deferrable = conform->condeferrable;
19232 fkconstraint->initdeferred = conform->condeferred;
19233 fkconstraint->location = -1;
19234 fkconstraint->pktable = NULL;
19235 fkconstraint->fk_attrs = NIL;
19236 fkconstraint->pk_attrs = NIL;
19237 fkconstraint->fk_matchtype = conform->confmatchtype;
19238 fkconstraint->fk_upd_action = conform->confupdtype;
19239 fkconstraint->fk_del_action = conform->confdeltype;
19240 fkconstraint->fk_del_set_cols = NIL;
19241 fkconstraint->old_conpfeqop = NIL;
19242 fkconstraint->old_pktable_oid = InvalidOid;
19243 fkconstraint->skip_validation = false;
19244 fkconstraint->initially_valid = true;
19246 createForeignKeyActionTriggers(partRel, conform->confrelid,
19247 fkconstraint, fk->conoid,
19248 conform->conindid,
19249 InvalidOid, InvalidOid,
19250 NULL, NULL);
19252 ReleaseSysCache(contup);
19254 list_free_deep(fks);
19255 if (trigrel)
19256 table_close(trigrel, RowExclusiveLock);
19259 * Any sub-constraints that are in the referenced-side of a larger
19260 * constraint have to be removed. This partition is no longer part of the
19261 * key space of the constraint.
19263 foreach(cell, GetParentedForeignKeyRefs(partRel))
19265 Oid constrOid = lfirst_oid(cell);
19266 ObjectAddress constraint;
19268 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19269 deleteDependencyRecordsForClass(ConstraintRelationId,
19270 constrOid,
19271 ConstraintRelationId,
19272 DEPENDENCY_INTERNAL);
19273 CommandCounterIncrement();
19275 ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
19276 performDeletion(&constraint, DROP_RESTRICT, 0);
19279 /* Now we can detach indexes */
19280 indexes = RelationGetIndexList(partRel);
19281 foreach(cell, indexes)
19283 Oid idxid = lfirst_oid(cell);
19284 Oid parentidx;
19285 Relation idx;
19286 Oid constrOid;
19287 Oid parentConstrOid;
19289 if (!has_superclass(idxid))
19290 continue;
19292 parentidx = get_partition_parent(idxid, false);
19293 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
19295 idx = index_open(idxid, AccessExclusiveLock);
19296 IndexSetParentIndex(idx, InvalidOid);
19299 * If there's a constraint associated with the index, detach it too.
19300 * Careful: it is possible for a constraint index in a partition to be
19301 * the child of a non-constraint index, so verify whether the parent
19302 * index does actually have a constraint.
19304 constrOid = get_relation_idx_constraint_oid(RelationGetRelid(partRel),
19305 idxid);
19306 parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
19307 parentidx);
19308 if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
19309 ConstraintSetParentConstraint(constrOid, InvalidOid, InvalidOid);
19311 index_close(idx, NoLock);
19314 /* Update pg_class tuple */
19315 classRel = table_open(RelationRelationId, RowExclusiveLock);
19316 tuple = SearchSysCacheCopy1(RELOID,
19317 ObjectIdGetDatum(RelationGetRelid(partRel)));
19318 if (!HeapTupleIsValid(tuple))
19319 elog(ERROR, "cache lookup failed for relation %u",
19320 RelationGetRelid(partRel));
19321 Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
19323 /* Clear relpartbound and reset relispartition */
19324 memset(new_val, 0, sizeof(new_val));
19325 memset(new_null, false, sizeof(new_null));
19326 memset(new_repl, false, sizeof(new_repl));
19327 new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
19328 new_null[Anum_pg_class_relpartbound - 1] = true;
19329 new_repl[Anum_pg_class_relpartbound - 1] = true;
19330 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
19331 new_val, new_null, new_repl);
19333 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
19334 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
19335 heap_freetuple(newtuple);
19336 table_close(classRel, RowExclusiveLock);
19339 * Drop identity property from all identity columns of partition.
19341 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
19343 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
19345 if (!attr->attisdropped && attr->attidentity)
19346 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
19347 AccessExclusiveLock, true, true);
19350 if (OidIsValid(defaultPartOid))
19353 * If the relation being detached is the default partition itself,
19354 * remove it from the parent's pg_partitioned_table entry.
19356 * If not, we must invalidate default partition's relcache entry, as
19357 * in StorePartitionBound: its partition constraint depends on every
19358 * other partition's partition constraint.
19360 if (RelationGetRelid(partRel) == defaultPartOid)
19361 update_default_partition_oid(RelationGetRelid(rel), InvalidOid);
19362 else
19363 CacheInvalidateRelcacheByRelid(defaultPartOid);
19367 * Invalidate the parent's relcache so that the partition is no longer
19368 * included in its partition descriptor.
19370 CacheInvalidateRelcache(rel);
19373 * If the partition we just detached is partitioned itself, invalidate
19374 * relcache for all descendent partitions too to ensure that their
19375 * rd_partcheck expression trees are rebuilt; must lock partitions before
19376 * doing so, using the same lockmode as what partRel has been locked with
19377 * by the caller.
19379 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19381 List *children;
19383 children = find_all_inheritors(RelationGetRelid(partRel),
19384 AccessExclusiveLock, NULL);
19385 foreach(cell, children)
19387 CacheInvalidateRelcacheByRelid(lfirst_oid(cell));
19393 * ALTER TABLE ... DETACH PARTITION ... FINALIZE
19395 * To use when a DETACH PARTITION command previously did not run to
19396 * completion; this completes the detaching process.
19398 static ObjectAddress
19399 ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
19401 Relation partRel;
19402 ObjectAddress address;
19403 Snapshot snap = GetActiveSnapshot();
19405 partRel = table_openrv(name, AccessExclusiveLock);
19408 * Wait until existing snapshots are gone. This is important if the
19409 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19410 * user could immediately run DETACH FINALIZE without actually waiting for
19411 * existing transactions. We must not complete the detach action until
19412 * all such queries are complete (otherwise we would present them with an
19413 * inconsistent view of catalogs).
19415 WaitForOlderSnapshots(snap->xmin, false);
19417 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
19419 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19421 table_close(partRel, NoLock);
19423 return address;
19427 * DetachAddConstraintIfNeeded
19428 * Subroutine for ATExecDetachPartition. Create a constraint that
19429 * takes the place of the partition constraint, but avoid creating
19430 * a dupe if an constraint already exists which implies the needed
19431 * constraint.
19433 static void
19434 DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
19436 List *constraintExpr;
19438 constraintExpr = RelationGetPartitionQual(partRel);
19439 constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
19442 * Avoid adding a new constraint if the needed constraint is implied by an
19443 * existing constraint
19445 if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
19447 AlteredTableInfo *tab;
19448 Constraint *n;
19450 tab = ATGetQueueEntry(wqueue, partRel);
19452 /* Add constraint on partition, equivalent to the partition constraint */
19453 n = makeNode(Constraint);
19454 n->contype = CONSTR_CHECK;
19455 n->conname = NULL;
19456 n->location = -1;
19457 n->is_no_inherit = false;
19458 n->raw_expr = NULL;
19459 n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
19460 n->initially_valid = true;
19461 n->skip_validation = true;
19462 /* It's a re-add, since it nominally already exists */
19463 ATAddCheckConstraint(wqueue, tab, partRel, n,
19464 true, false, true, ShareUpdateExclusiveLock);
19469 * DropClonedTriggersFromPartition
19470 * subroutine for ATExecDetachPartition to remove any triggers that were
19471 * cloned to the partition when it was created-as-partition or attached.
19472 * This undoes what CloneRowTriggersToPartition did.
19474 static void
19475 DropClonedTriggersFromPartition(Oid partitionId)
19477 ScanKeyData skey;
19478 SysScanDesc scan;
19479 HeapTuple trigtup;
19480 Relation tgrel;
19481 ObjectAddresses *objects;
19483 objects = new_object_addresses();
19486 * Scan pg_trigger to search for all triggers on this rel.
19488 ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19489 F_OIDEQ, ObjectIdGetDatum(partitionId));
19490 tgrel = table_open(TriggerRelationId, RowExclusiveLock);
19491 scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
19492 true, NULL, 1, &skey);
19493 while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
19495 Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
19496 ObjectAddress trig;
19498 /* Ignore triggers that weren't cloned */
19499 if (!OidIsValid(pg_trigger->tgparentid))
19500 continue;
19503 * Ignore internal triggers that are implementation objects of foreign
19504 * keys, because these will be detached when the foreign keys
19505 * themselves are.
19507 if (OidIsValid(pg_trigger->tgconstrrelid))
19508 continue;
19511 * This is ugly, but necessary: remove the dependency markings on the
19512 * trigger so that it can be removed.
19514 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19515 TriggerRelationId,
19516 DEPENDENCY_PARTITION_PRI);
19517 deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19518 RelationRelationId,
19519 DEPENDENCY_PARTITION_SEC);
19521 /* remember this trigger to remove it below */
19522 ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
19523 add_exact_object_address(&trig, objects);
19526 /* make the dependency removal visible to the deletion below */
19527 CommandCounterIncrement();
19528 performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
19530 /* done */
19531 free_object_addresses(objects);
19532 systable_endscan(scan);
19533 table_close(tgrel, RowExclusiveLock);
19537 * Before acquiring lock on an index, acquire the same lock on the owning
19538 * table.
19540 struct AttachIndexCallbackState
19542 Oid partitionOid;
19543 Oid parentTblOid;
19544 bool lockedParentTbl;
19547 static void
19548 RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid,
19549 void *arg)
19551 struct AttachIndexCallbackState *state;
19552 Form_pg_class classform;
19553 HeapTuple tuple;
19555 state = (struct AttachIndexCallbackState *) arg;
19557 if (!state->lockedParentTbl)
19559 LockRelationOid(state->parentTblOid, AccessShareLock);
19560 state->lockedParentTbl = true;
19564 * If we previously locked some other heap, and the name we're looking up
19565 * no longer refers to an index on that relation, release the now-useless
19566 * lock. XXX maybe we should do *after* we verify whether the index does
19567 * not actually belong to the same relation ...
19569 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
19571 UnlockRelationOid(state->partitionOid, AccessShareLock);
19572 state->partitionOid = InvalidOid;
19575 /* Didn't find a relation, so no need for locking or permission checks. */
19576 if (!OidIsValid(relOid))
19577 return;
19579 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
19580 if (!HeapTupleIsValid(tuple))
19581 return; /* concurrently dropped, so nothing to do */
19582 classform = (Form_pg_class) GETSTRUCT(tuple);
19583 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
19584 classform->relkind != RELKIND_INDEX)
19585 ereport(ERROR,
19586 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19587 errmsg("\"%s\" is not an index", rv->relname)));
19588 ReleaseSysCache(tuple);
19591 * Since we need only examine the heap's tupledesc, an access share lock
19592 * on it (preventing any DDL) is sufficient.
19594 state->partitionOid = IndexGetRelation(relOid, false);
19595 LockRelationOid(state->partitionOid, AccessShareLock);
19599 * ALTER INDEX i1 ATTACH PARTITION i2
19601 static ObjectAddress
19602 ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
19604 Relation partIdx;
19605 Relation partTbl;
19606 Relation parentTbl;
19607 ObjectAddress address;
19608 Oid partIdxId;
19609 Oid currParent;
19610 struct AttachIndexCallbackState state;
19613 * We need to obtain lock on the index 'name' to modify it, but we also
19614 * need to read its owning table's tuple descriptor -- so we need to lock
19615 * both. To avoid deadlocks, obtain lock on the table before doing so on
19616 * the index. Furthermore, we need to examine the parent table of the
19617 * partition, so lock that one too.
19619 state.partitionOid = InvalidOid;
19620 state.parentTblOid = parentIdx->rd_index->indrelid;
19621 state.lockedParentTbl = false;
19622 partIdxId =
19623 RangeVarGetRelidExtended(name, AccessExclusiveLock, 0,
19624 RangeVarCallbackForAttachIndex,
19625 (void *) &state);
19626 /* Not there? */
19627 if (!OidIsValid(partIdxId))
19628 ereport(ERROR,
19629 (errcode(ERRCODE_UNDEFINED_OBJECT),
19630 errmsg("index \"%s\" does not exist", name->relname)));
19632 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19633 partIdx = relation_open(partIdxId, AccessExclusiveLock);
19635 /* we already hold locks on both tables, so this is safe: */
19636 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
19637 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19639 ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19641 /* Silently do nothing if already in the right state */
19642 currParent = partIdx->rd_rel->relispartition ?
19643 get_partition_parent(partIdxId, false) : InvalidOid;
19644 if (currParent != RelationGetRelid(parentIdx))
19646 IndexInfo *childInfo;
19647 IndexInfo *parentInfo;
19648 AttrMap *attmap;
19649 bool found;
19650 int i;
19651 PartitionDesc partDesc;
19652 Oid constraintOid,
19653 cldConstrId = InvalidOid;
19656 * If this partition already has an index attached, refuse the
19657 * operation.
19659 refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19661 if (OidIsValid(currParent))
19662 ereport(ERROR,
19663 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19664 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19665 RelationGetRelationName(partIdx),
19666 RelationGetRelationName(parentIdx)),
19667 errdetail("Index \"%s\" is already attached to another index.",
19668 RelationGetRelationName(partIdx))));
19670 /* Make sure it indexes a partition of the other index's table */
19671 partDesc = RelationGetPartitionDesc(parentTbl, true);
19672 found = false;
19673 for (i = 0; i < partDesc->nparts; i++)
19675 if (partDesc->oids[i] == state.partitionOid)
19677 found = true;
19678 break;
19681 if (!found)
19682 ereport(ERROR,
19683 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19684 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19685 RelationGetRelationName(partIdx),
19686 RelationGetRelationName(parentIdx)),
19687 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19688 RelationGetRelationName(partIdx),
19689 RelationGetRelationName(parentTbl))));
19691 /* Ensure the indexes are compatible */
19692 childInfo = BuildIndexInfo(partIdx);
19693 parentInfo = BuildIndexInfo(parentIdx);
19694 attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19695 RelationGetDescr(parentTbl),
19696 false);
19697 if (!CompareIndexInfo(childInfo, parentInfo,
19698 partIdx->rd_indcollation,
19699 parentIdx->rd_indcollation,
19700 partIdx->rd_opfamily,
19701 parentIdx->rd_opfamily,
19702 attmap))
19703 ereport(ERROR,
19704 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19705 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19706 RelationGetRelationName(partIdx),
19707 RelationGetRelationName(parentIdx)),
19708 errdetail("The index definitions do not match.")));
19711 * If there is a constraint in the parent, make sure there is one in
19712 * the child too.
19714 constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19715 RelationGetRelid(parentIdx));
19717 if (OidIsValid(constraintOid))
19719 cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19720 partIdxId);
19721 if (!OidIsValid(cldConstrId))
19722 ereport(ERROR,
19723 (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19724 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19725 RelationGetRelationName(partIdx),
19726 RelationGetRelationName(parentIdx)),
19727 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19728 RelationGetRelationName(parentIdx),
19729 RelationGetRelationName(parentTbl),
19730 RelationGetRelationName(partIdx))));
19733 /* All good -- do it */
19734 IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
19735 if (OidIsValid(constraintOid))
19736 ConstraintSetParentConstraint(cldConstrId, constraintOid,
19737 RelationGetRelid(partTbl));
19739 free_attrmap(attmap);
19741 validatePartitionedIndex(parentIdx, parentTbl);
19744 relation_close(parentTbl, AccessShareLock);
19745 /* keep these locks till commit */
19746 relation_close(partTbl, NoLock);
19747 relation_close(partIdx, NoLock);
19749 return address;
19753 * Verify whether the given partition already contains an index attached
19754 * to the given partitioned index. If so, raise an error.
19756 static void
19757 refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
19759 Oid existingIdx;
19761 existingIdx = index_get_partition(partitionTbl,
19762 RelationGetRelid(parentIdx));
19763 if (OidIsValid(existingIdx))
19764 ereport(ERROR,
19765 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19766 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19767 RelationGetRelationName(partIdx),
19768 RelationGetRelationName(parentIdx)),
19769 errdetail("Another index is already attached for partition \"%s\".",
19770 RelationGetRelationName(partitionTbl))));
19774 * Verify whether the set of attached partition indexes to a parent index on
19775 * a partitioned table is complete. If it is, mark the parent index valid.
19777 * This should be called each time a partition index is attached.
19779 static void
19780 validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
19782 Relation inheritsRel;
19783 SysScanDesc scan;
19784 ScanKeyData key;
19785 int tuples = 0;
19786 HeapTuple inhTup;
19787 bool updated = false;
19789 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19792 * Scan pg_inherits for this parent index. Count each valid index we find
19793 * (verifying the pg_index entry for each), and if we reach the total
19794 * amount we expect, we can mark this parent index as valid.
19796 inheritsRel = table_open(InheritsRelationId, AccessShareLock);
19797 ScanKeyInit(&key, Anum_pg_inherits_inhparent,
19798 BTEqualStrategyNumber, F_OIDEQ,
19799 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19800 scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19801 NULL, 1, &key);
19802 while ((inhTup = systable_getnext(scan)) != NULL)
19804 Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19805 HeapTuple indTup;
19806 Form_pg_index indexForm;
19808 indTup = SearchSysCache1(INDEXRELID,
19809 ObjectIdGetDatum(inhForm->inhrelid));
19810 if (!HeapTupleIsValid(indTup))
19811 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
19812 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19813 if (indexForm->indisvalid)
19814 tuples += 1;
19815 ReleaseSysCache(indTup);
19818 /* Done with pg_inherits */
19819 systable_endscan(scan);
19820 table_close(inheritsRel, AccessShareLock);
19823 * If we found as many inherited indexes as the partitioned table has
19824 * partitions, we're good; update pg_index to set indisvalid.
19826 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19828 Relation idxRel;
19829 HeapTuple indTup;
19830 Form_pg_index indexForm;
19832 idxRel = table_open(IndexRelationId, RowExclusiveLock);
19833 indTup = SearchSysCacheCopy1(INDEXRELID,
19834 ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19835 if (!HeapTupleIsValid(indTup))
19836 elog(ERROR, "cache lookup failed for index %u",
19837 RelationGetRelid(partedIdx));
19838 indexForm = (Form_pg_index) GETSTRUCT(indTup);
19840 indexForm->indisvalid = true;
19841 updated = true;
19843 CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
19845 table_close(idxRel, RowExclusiveLock);
19846 heap_freetuple(indTup);
19850 * If this index is in turn a partition of a larger index, validating it
19851 * might cause the parent to become valid also. Try that.
19853 if (updated && partedIdx->rd_rel->relispartition)
19855 Oid parentIdxId,
19856 parentTblId;
19857 Relation parentIdx,
19858 parentTbl;
19860 /* make sure we see the validation we just did */
19861 CommandCounterIncrement();
19863 parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
19864 parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
19865 parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
19866 parentTbl = relation_open(parentTblId, AccessExclusiveLock);
19867 Assert(!parentIdx->rd_index->indisvalid);
19869 validatePartitionedIndex(parentIdx, parentTbl);
19871 relation_close(parentIdx, AccessExclusiveLock);
19872 relation_close(parentTbl, AccessExclusiveLock);
19877 * Return an OID list of constraints that reference the given relation
19878 * that are marked as having a parent constraints.
19880 static List *
19881 GetParentedForeignKeyRefs(Relation partition)
19883 Relation pg_constraint;
19884 HeapTuple tuple;
19885 SysScanDesc scan;
19886 ScanKeyData key[2];
19887 List *constraints = NIL;
19890 * If no indexes, or no columns are referenceable by FKs, we can avoid the
19891 * scan.
19893 if (RelationGetIndexList(partition) == NIL ||
19894 bms_is_empty(RelationGetIndexAttrBitmap(partition,
19895 INDEX_ATTR_BITMAP_KEY)))
19896 return NIL;
19898 /* Search for constraints referencing this table */
19899 pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19900 ScanKeyInit(&key[0],
19901 Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
19902 F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
19903 ScanKeyInit(&key[1],
19904 Anum_pg_constraint_contype, BTEqualStrategyNumber,
19905 F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
19907 /* XXX This is a seqscan, as we don't have a usable index */
19908 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
19909 while ((tuple = systable_getnext(scan)) != NULL)
19911 Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19914 * We only need to process constraints that are part of larger ones.
19916 if (!OidIsValid(constrForm->conparentid))
19917 continue;
19919 constraints = lappend_oid(constraints, constrForm->oid);
19922 systable_endscan(scan);
19923 table_close(pg_constraint, AccessShareLock);
19925 return constraints;
19929 * During DETACH PARTITION, verify that any foreign keys pointing to the
19930 * partitioned table would not become invalid. An error is raised if any
19931 * referenced values exist.
19933 static void
19934 ATDetachCheckNoForeignKeyRefs(Relation partition)
19936 List *constraints;
19937 ListCell *cell;
19939 constraints = GetParentedForeignKeyRefs(partition);
19941 foreach(cell, constraints)
19943 Oid constrOid = lfirst_oid(cell);
19944 HeapTuple tuple;
19945 Form_pg_constraint constrForm;
19946 Relation rel;
19947 Trigger trig = {0};
19949 tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
19950 if (!HeapTupleIsValid(tuple))
19951 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
19952 constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19954 Assert(OidIsValid(constrForm->conparentid));
19955 Assert(constrForm->confrelid == RelationGetRelid(partition));
19957 /* prevent data changes into the referencing table until commit */
19958 rel = table_open(constrForm->conrelid, ShareLock);
19960 trig.tgoid = InvalidOid;
19961 trig.tgname = NameStr(constrForm->conname);
19962 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
19963 trig.tgisinternal = true;
19964 trig.tgconstrrelid = RelationGetRelid(partition);
19965 trig.tgconstrindid = constrForm->conindid;
19966 trig.tgconstraint = constrForm->oid;
19967 trig.tgdeferrable = false;
19968 trig.tginitdeferred = false;
19969 /* we needn't fill in remaining fields */
19971 RI_PartitionRemove_Check(&trig, rel, partition);
19973 ReleaseSysCache(tuple);
19975 table_close(rel, NoLock);
19980 * resolve column compression specification to compression method.
19982 static char
19983 GetAttributeCompression(Oid atttypid, const char *compression)
19985 char cmethod;
19987 if (compression == NULL || strcmp(compression, "default") == 0)
19988 return InvalidCompressionMethod;
19991 * To specify a nondefault method, the column data type must be toastable.
19992 * Note this says nothing about whether the column's attstorage setting
19993 * permits compression; we intentionally allow attstorage and
19994 * attcompression to be independent. But with a non-toastable type,
19995 * attstorage could not be set to a value that would permit compression.
19997 * We don't actually need to enforce this, since nothing bad would happen
19998 * if attcompression were non-default; it would never be consulted. But
19999 * it seems more user-friendly to complain about a certainly-useless
20000 * attempt to set the property.
20002 if (!TypeIsToastable(atttypid))
20003 ereport(ERROR,
20004 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20005 errmsg("column data type %s does not support compression",
20006 format_type_be(atttypid))));
20008 cmethod = CompressionNameToMethod(compression);
20009 if (!CompressionMethodIsValid(cmethod))
20010 ereport(ERROR,
20011 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20012 errmsg("invalid compression method \"%s\"", compression)));
20014 return cmethod;
20018 * resolve column storage specification
20020 static char
20021 GetAttributeStorage(Oid atttypid, const char *storagemode)
20023 char cstorage = 0;
20025 if (pg_strcasecmp(storagemode, "plain") == 0)
20026 cstorage = TYPSTORAGE_PLAIN;
20027 else if (pg_strcasecmp(storagemode, "external") == 0)
20028 cstorage = TYPSTORAGE_EXTERNAL;
20029 else if (pg_strcasecmp(storagemode, "extended") == 0)
20030 cstorage = TYPSTORAGE_EXTENDED;
20031 else if (pg_strcasecmp(storagemode, "main") == 0)
20032 cstorage = TYPSTORAGE_MAIN;
20033 else if (pg_strcasecmp(storagemode, "default") == 0)
20034 cstorage = get_typstorage(atttypid);
20035 else
20036 ereport(ERROR,
20037 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20038 errmsg("invalid storage type \"%s\"",
20039 storagemode)));
20042 * safety check: do not allow toasted storage modes unless column datatype
20043 * is TOAST-aware.
20045 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20046 ereport(ERROR,
20047 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20048 errmsg("column data type %s can only have storage PLAIN",
20049 format_type_be(atttypid))));
20051 return cstorage;
20055 * Struct with context of new partition for inserting rows from split partition
20057 typedef struct SplitPartitionContext
20059 ExprState *partqualstate; /* expression for checking slot for partition
20060 * (NULL for DEFAULT partition) */
20061 BulkInsertState bistate; /* state of bulk inserts for partition */
20062 TupleTableSlot *dstslot; /* slot for inserting row into partition */
20063 Relation partRel; /* relation for partition */
20064 } SplitPartitionContext;
20068 * createSplitPartitionContext: create context for partition and fill it
20070 static SplitPartitionContext *
20071 createSplitPartitionContext(Relation partRel)
20073 SplitPartitionContext *pc;
20075 pc = (SplitPartitionContext *) palloc0(sizeof(SplitPartitionContext));
20076 pc->partRel = partRel;
20079 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20080 * don't bother using it.
20082 pc->bistate = GetBulkInsertState();
20084 /* Create tuple slot for new partition. */
20085 pc->dstslot = MakeSingleTupleTableSlot(RelationGetDescr(pc->partRel),
20086 table_slot_callbacks(pc->partRel));
20087 ExecStoreAllNullTuple(pc->dstslot);
20089 return pc;
20093 * deleteSplitPartitionContext: delete context for partition
20095 static void
20096 deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
20098 ExecDropSingleTupleTableSlot(pc->dstslot);
20099 FreeBulkInsertState(pc->bistate);
20101 table_finish_bulk_insert(pc->partRel, ti_options);
20103 pfree(pc);
20107 * moveSplitTableRows: scan split partition (splitRel) of partitioned table
20108 * (rel) and move rows into new partitions.
20110 * New partitions description:
20111 * partlist: list of pointers to SinglePartitionSpec structures.
20112 * newPartRels: list of Relations.
20113 * defaultPartOid: oid of DEFAULT partition, for table rel.
20115 static void
20116 moveSplitTableRows(Relation rel, Relation splitRel, List *partlist, List *newPartRels, Oid defaultPartOid)
20118 /* The FSM is empty, so don't bother using it. */
20119 int ti_options = TABLE_INSERT_SKIP_FSM;
20120 CommandId mycid;
20121 EState *estate;
20122 ListCell *listptr,
20123 *listptr2;
20124 TupleTableSlot *srcslot;
20125 ExprContext *econtext;
20126 TableScanDesc scan;
20127 Snapshot snapshot;
20128 MemoryContext oldCxt;
20129 List *partContexts = NIL;
20130 TupleConversionMap *tuple_map;
20131 SplitPartitionContext *defaultPartCtx = NULL,
20132 *pc;
20133 bool isOldDefaultPart = false;
20135 mycid = GetCurrentCommandId(true);
20137 estate = CreateExecutorState();
20139 forboth(listptr, partlist, listptr2, newPartRels)
20141 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20143 pc = createSplitPartitionContext((Relation) lfirst(listptr2));
20145 if (sps->bound->is_default)
20147 /* We should not create constraint for detached DEFAULT partition. */
20148 defaultPartCtx = pc;
20150 else
20152 List *partConstraint;
20154 /* Build expression execution states for partition check quals. */
20155 partConstraint = get_qual_from_partbound(rel, sps->bound);
20156 partConstraint =
20157 (List *) eval_const_expressions(NULL,
20158 (Node *) partConstraint);
20159 /* Make boolean expression for ExecCheck(). */
20160 partConstraint = list_make1(make_ands_explicit(partConstraint));
20163 * Map the vars in the constraint expression from rel's attnos to
20164 * splitRel's.
20166 partConstraint = map_partition_varattnos(partConstraint,
20167 1, splitRel, rel);
20169 pc->partqualstate =
20170 ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
20171 Assert(pc->partqualstate != NULL);
20174 /* Store partition context into list. */
20175 partContexts = lappend(partContexts, pc);
20179 * Create partition context for DEFAULT partition. We can insert values
20180 * into this partition in case spaces with values between new partitions.
20182 if (!defaultPartCtx && OidIsValid(defaultPartOid))
20184 /* Indicate that we allocate context for old DEFAULT partition */
20185 isOldDefaultPart = true;
20186 defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
20189 econtext = GetPerTupleExprContext(estate);
20191 /* Create necessary tuple slot. */
20192 srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
20193 table_slot_callbacks(splitRel));
20196 * Map computing for moving attributes of split partition to new partition
20197 * (for first new partition, but other new partitions can use the same
20198 * map).
20200 pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
20201 tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
20202 RelationGetDescr(pc->partRel));
20204 /* Scan through the rows. */
20205 snapshot = RegisterSnapshot(GetLatestSnapshot());
20206 scan = table_beginscan(splitRel, snapshot, 0, NULL);
20209 * Switch to per-tuple memory context and reset it for each tuple
20210 * produced, so we don't leak memory.
20212 oldCxt = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate));
20214 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20216 bool found = false;
20217 TupleTableSlot *insertslot;
20219 /* Extract data from old tuple. */
20220 slot_getallattrs(srcslot);
20222 econtext->ecxt_scantuple = srcslot;
20224 /* Search partition for current slot srcslot. */
20225 foreach(listptr, partContexts)
20227 pc = (SplitPartitionContext *) lfirst(listptr);
20229 if (pc->partqualstate /* skip DEFAULT partition */ &&
20230 ExecCheck(pc->partqualstate, econtext))
20232 found = true;
20233 break;
20235 ResetExprContext(econtext);
20237 if (!found)
20239 /* Use DEFAULT partition if it exists. */
20240 if (defaultPartCtx)
20241 pc = defaultPartCtx;
20242 else
20243 ereport(ERROR,
20244 (errcode(ERRCODE_CHECK_VIOLATION),
20245 errmsg("can not find partition for split partition row"),
20246 errtable(splitRel)));
20249 if (tuple_map)
20251 /* Need to use map to copy attributes. */
20252 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
20254 else
20256 /* Copy attributes directly. */
20257 insertslot = pc->dstslot;
20259 ExecClearTuple(insertslot);
20261 memcpy(insertslot->tts_values, srcslot->tts_values,
20262 sizeof(Datum) * srcslot->tts_nvalid);
20263 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20264 sizeof(bool) * srcslot->tts_nvalid);
20266 ExecStoreVirtualTuple(insertslot);
20269 /* Write the tuple out to the new relation. */
20270 table_tuple_insert(pc->partRel, insertslot, mycid,
20271 ti_options, pc->bistate);
20273 ResetExprContext(econtext);
20275 CHECK_FOR_INTERRUPTS();
20278 MemoryContextSwitchTo(oldCxt);
20280 table_endscan(scan);
20281 UnregisterSnapshot(snapshot);
20283 if (tuple_map)
20284 free_conversion_map(tuple_map);
20286 ExecDropSingleTupleTableSlot(srcslot);
20288 FreeExecutorState(estate);
20290 foreach(listptr, partContexts)
20291 deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
20293 /* Need to close table and free buffers for DEFAULT partition. */
20294 if (isOldDefaultPart)
20296 Relation defaultPartRel = defaultPartCtx->partRel;
20298 deleteSplitPartitionContext(defaultPartCtx, ti_options);
20299 /* Keep the lock until commit. */
20300 table_close(defaultPartRel, NoLock);
20305 * createPartitionTable: create table for a new partition with given name
20306 * (newPartName) like table (modelRel)
20308 * Emulates command: CREATE [TEMP] TABLE <newPartName> (LIKE <modelRel's name>
20309 * INCLUDING ALL EXCLUDING INDEXES EXCLUDING IDENTITY EXCLUDING STATISTICS)
20311 * Also, this function sets the new partition access method same as parent
20312 * table access methods (similarly to CREATE TABLE ... PARTITION OF). It
20313 * checks that parent and child tables have compatible persistence.
20315 * Function returns the created relation (locked in AccessExclusiveLock mode).
20317 static Relation
20318 createPartitionTable(RangeVar *newPartName, Relation modelRel,
20319 AlterTableUtilityContext *context)
20321 CreateStmt *createStmt;
20322 TableLikeClause *tlc;
20323 PlannedStmt *wrapper;
20324 Relation newRel;
20326 /* If existing rel is temp, it must belong to this session */
20327 if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20328 !modelRel->rd_islocaltemp)
20329 ereport(ERROR,
20330 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20331 errmsg("cannot create as partition of temporary relation of another session")));
20333 /* New partition should have the same persistence as modelRel */
20334 newPartName->relpersistence = modelRel->rd_rel->relpersistence;
20336 createStmt = makeNode(CreateStmt);
20337 createStmt->relation = newPartName;
20338 createStmt->tableElts = NIL;
20339 createStmt->inhRelations = NIL;
20340 createStmt->constraints = NIL;
20341 createStmt->options = NIL;
20342 createStmt->oncommit = ONCOMMIT_NOOP;
20343 createStmt->tablespacename = get_tablespace_name(modelRel->rd_rel->reltablespace);
20344 createStmt->if_not_exists = false;
20345 createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
20347 tlc = makeNode(TableLikeClause);
20348 tlc->relation = makeRangeVar(get_namespace_name(RelationGetNamespace(modelRel)),
20349 RelationGetRelationName(modelRel), -1);
20352 * Indexes will be inherited on "attach new partitions" stage, after data
20353 * moving. We also don't copy the extended statistics for consistency
20354 * with CREATE TABLE PARTITION OF.
20356 tlc->options = CREATE_TABLE_LIKE_ALL &
20357 ~(CREATE_TABLE_LIKE_INDEXES | CREATE_TABLE_LIKE_IDENTITY | CREATE_TABLE_LIKE_STATISTICS);
20358 tlc->relationOid = InvalidOid;
20359 createStmt->tableElts = lappend(createStmt->tableElts, tlc);
20361 /* Need to make a wrapper PlannedStmt. */
20362 wrapper = makeNode(PlannedStmt);
20363 wrapper->commandType = CMD_UTILITY;
20364 wrapper->canSetTag = false;
20365 wrapper->utilityStmt = (Node *) createStmt;
20366 wrapper->stmt_location = context->pstmt->stmt_location;
20367 wrapper->stmt_len = context->pstmt->stmt_len;
20369 ProcessUtility(wrapper,
20370 context->queryString,
20371 false,
20372 PROCESS_UTILITY_SUBCOMMAND,
20373 NULL,
20374 NULL,
20375 None_Receiver,
20376 NULL);
20379 * Open the new partition with no lock, because we already have
20380 * AccessExclusiveLock placed there after creation.
20382 newRel = table_openrv(newPartName, NoLock);
20385 * We intended to create the partition with the same persistence as the
20386 * parent table, but we still need to recheck because that might be
20387 * affected by the search_path. If the parent is permanent, so must be
20388 * all of its partitions.
20390 if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20391 newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20392 ereport(ERROR,
20393 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20394 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
20395 RelationGetRelationName(modelRel))));
20397 /* Permanent rels cannot be partitions belonging to temporary parent */
20398 if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20399 modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20400 ereport(ERROR,
20401 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20402 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
20403 RelationGetRelationName(modelRel))));
20405 return newRel;
20409 * ALTER TABLE <name> SPLIT PARTITION <partition-name> INTO <partition-list>
20411 static void
20412 ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel,
20413 PartitionCmd *cmd, AlterTableUtilityContext *context)
20415 Relation splitRel;
20416 Oid splitRelOid;
20417 char relname[NAMEDATALEN];
20418 Oid namespaceId;
20419 ListCell *listptr,
20420 *listptr2;
20421 bool isSameName = false;
20422 char tmpRelName[NAMEDATALEN];
20423 List *newPartRels = NIL;
20424 ObjectAddress object;
20425 Oid defaultPartOid;
20427 defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20430 * We are going to detach and remove this partition: need to use exclusive
20431 * lock for preventing DML-queries to the partition.
20433 splitRel = table_openrv(cmd->name, AccessExclusiveLock);
20435 splitRelOid = RelationGetRelid(splitRel);
20437 /* Check descriptions of new partitions. */
20438 foreach(listptr, cmd->partlist)
20440 Oid existing_relid;
20441 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20443 strlcpy(relname, sps->name->relname, NAMEDATALEN);
20446 * Look up the namespace in which we are supposed to create the
20447 * partition, check we have permission to create there, lock it
20448 * against concurrent drop, and mark stmt->relation as
20449 * RELPERSISTENCE_TEMP if a temporary namespace is selected.
20451 sps->name->relpersistence = rel->rd_rel->relpersistence;
20452 namespaceId =
20453 RangeVarGetAndCheckCreationNamespace(sps->name, NoLock, NULL);
20456 * This would fail later on anyway if the relation already exists. But
20457 * by catching it here we can emit a nicer error message.
20459 existing_relid = get_relname_relid(relname, namespaceId);
20460 if (existing_relid == splitRelOid && !isSameName)
20461 /* One new partition can have the same name as split partition. */
20462 isSameName = true;
20463 else if (existing_relid != InvalidOid)
20464 ereport(ERROR,
20465 (errcode(ERRCODE_DUPLICATE_TABLE),
20466 errmsg("relation \"%s\" already exists", relname)));
20469 /* Detach split partition. */
20470 RemoveInheritance(splitRel, rel, false);
20471 /* Do the final part of detaching. */
20472 DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
20475 * If new partition has the same name as split partition then we should
20476 * rename split partition for reusing name.
20478 if (isSameName)
20481 * We must bump the command counter to make the split partition tuple
20482 * visible for renaming.
20484 CommandCounterIncrement();
20485 /* Rename partition. */
20486 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20487 RenameRelationInternal(splitRelOid, tmpRelName, false, false);
20490 * We must bump the command counter to make the split partition tuple
20491 * visible after renaming.
20493 CommandCounterIncrement();
20496 /* Create new partitions (like split partition), without indexes. */
20497 foreach(listptr, cmd->partlist)
20499 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20500 Relation newPartRel;
20502 newPartRel = createPartitionTable(sps->name, rel, context);
20503 newPartRels = lappend(newPartRels, newPartRel);
20506 /* Copy data from split partition to new partitions. */
20507 moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
20508 /* Keep the lock until commit. */
20509 table_close(splitRel, NoLock);
20511 /* Attach new partitions to partitioned table. */
20512 forboth(listptr, cmd->partlist, listptr2, newPartRels)
20514 SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20515 Relation newPartRel = (Relation) lfirst(listptr2);
20518 * wqueue = NULL: verification for each cloned constraint is not
20519 * needed.
20521 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
20522 /* Keep the lock until commit. */
20523 table_close(newPartRel, NoLock);
20526 /* Drop split partition. */
20527 object.classId = RelationRelationId;
20528 object.objectId = splitRelOid;
20529 object.objectSubId = 0;
20530 /* Probably DROP_CASCADE is not needed. */
20531 performDeletion(&object, DROP_RESTRICT, 0);
20535 * moveMergedTablesRows: scan partitions to be merged (mergingPartitionsList)
20536 * of the partitioned table (rel) and move rows into the new partition
20537 * (newPartRel).
20539 static void
20540 moveMergedTablesRows(Relation rel, List *mergingPartitionsList,
20541 Relation newPartRel)
20543 CommandId mycid;
20545 /* The FSM is empty, so don't bother using it. */
20546 int ti_options = TABLE_INSERT_SKIP_FSM;
20547 ListCell *listptr;
20548 BulkInsertState bistate; /* state of bulk inserts for partition */
20549 TupleTableSlot *dstslot;
20551 mycid = GetCurrentCommandId(true);
20553 /* Prepare a BulkInsertState for table_tuple_insert. */
20554 bistate = GetBulkInsertState();
20556 /* Create necessary tuple slot. */
20557 dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
20558 table_slot_callbacks(newPartRel));
20559 ExecStoreAllNullTuple(dstslot);
20561 foreach(listptr, mergingPartitionsList)
20563 Relation mergingPartition = (Relation) lfirst(listptr);
20564 TupleTableSlot *srcslot;
20565 TupleConversionMap *tuple_map;
20566 TableScanDesc scan;
20567 Snapshot snapshot;
20569 /* Create tuple slot for new partition. */
20570 srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
20571 table_slot_callbacks(mergingPartition));
20574 * Map computing for moving attributes of merged partition to new
20575 * partition.
20577 tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
20578 RelationGetDescr(newPartRel));
20580 /* Scan through the rows. */
20581 snapshot = RegisterSnapshot(GetLatestSnapshot());
20582 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
20584 while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20586 TupleTableSlot *insertslot;
20588 /* Extract data from old tuple. */
20589 slot_getallattrs(srcslot);
20591 if (tuple_map)
20593 /* Need to use map to copy attributes. */
20594 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
20596 else
20598 /* Copy attributes directly. */
20599 insertslot = dstslot;
20601 ExecClearTuple(insertslot);
20603 memcpy(insertslot->tts_values, srcslot->tts_values,
20604 sizeof(Datum) * srcslot->tts_nvalid);
20605 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20606 sizeof(bool) * srcslot->tts_nvalid);
20608 ExecStoreVirtualTuple(insertslot);
20611 /* Write the tuple out to the new relation. */
20612 table_tuple_insert(newPartRel, insertslot, mycid,
20613 ti_options, bistate);
20615 CHECK_FOR_INTERRUPTS();
20618 table_endscan(scan);
20619 UnregisterSnapshot(snapshot);
20621 if (tuple_map)
20622 free_conversion_map(tuple_map);
20624 ExecDropSingleTupleTableSlot(srcslot);
20627 ExecDropSingleTupleTableSlot(dstslot);
20628 FreeBulkInsertState(bistate);
20630 table_finish_bulk_insert(newPartRel, ti_options);
20634 * ALTER TABLE <name> MERGE PARTITIONS <partition-list> INTO <partition-name>
20636 static void
20637 ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel,
20638 PartitionCmd *cmd, AlterTableUtilityContext *context)
20640 Relation newPartRel;
20641 ListCell *listptr;
20642 List *mergingPartitionsList = NIL;
20643 Oid defaultPartOid;
20644 Oid namespaceId;
20645 Oid existingRelid;
20648 * Lock all merged partitions, check them and create list with partitions
20649 * contexts.
20651 foreach(listptr, cmd->partlist)
20653 RangeVar *name = (RangeVar *) lfirst(listptr);
20654 Relation mergingPartition;
20657 * We are going to detach and remove this partition: need to use
20658 * exclusive lock for preventing DML-queries to the partition.
20660 mergingPartition = table_openrv(name, AccessExclusiveLock);
20662 /* Store a next merging partition into the list. */
20663 mergingPartitionsList = lappend(mergingPartitionsList,
20664 mergingPartition);
20668 * Look up the namespace in which we are supposed to create the partition,
20669 * check we have permission to create there, lock it against concurrent
20670 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
20671 * namespace is selected.
20673 cmd->name->relpersistence = rel->rd_rel->relpersistence;
20674 namespaceId =
20675 RangeVarGetAndCheckCreationNamespace(cmd->name, NoLock, NULL);
20678 * Check if this name is already taken. This helps us to detect the
20679 * situation when one of the merging partitions has the same name as the
20680 * new partition. Otherwise, this would fail later on anyway but catching
20681 * this here allows us to emit a nicer error message.
20683 existingRelid = get_relname_relid(cmd->name->relname, namespaceId);
20685 if (OidIsValid(existingRelid))
20687 Relation sameNamePartition = NULL;
20689 foreach_ptr(RelationData, mergingPartition, mergingPartitionsList)
20691 if (RelationGetRelid(mergingPartition) == existingRelid)
20693 sameNamePartition = mergingPartition;
20694 break;
20698 if (sameNamePartition)
20701 * The new partition has the same name as one of merging
20702 * partitions.
20704 char tmpRelName[NAMEDATALEN];
20706 /* Generate temporary name. */
20707 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20710 * Rename the existing partition with a temporary name, leaving it
20711 * free for the new partition. We don't need to care about this
20712 * in the future because we're going to eventually drop the
20713 * existing partition anyway.
20715 RenameRelationInternal(RelationGetRelid(sameNamePartition),
20716 tmpRelName, false, false);
20719 * We must bump the command counter to make the new partition
20720 * tuple visible for rename.
20722 CommandCounterIncrement();
20724 else
20726 ereport(ERROR,
20727 (errcode(ERRCODE_DUPLICATE_TABLE),
20728 errmsg("relation \"%s\" already exists", cmd->name->relname)));
20732 /* Detach all merged partitions. */
20733 defaultPartOid =
20734 get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20735 foreach(listptr, mergingPartitionsList)
20737 Relation mergingPartition = (Relation) lfirst(listptr);
20739 /* Remove the pg_inherits row first. */
20740 RemoveInheritance(mergingPartition, rel, false);
20741 /* Do the final part of detaching. */
20742 DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
20745 /* Create table for new partition, use partitioned table as model. */
20746 newPartRel = createPartitionTable(cmd->name, rel, context);
20748 /* Copy data from merged partitions to new partition. */
20749 moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
20751 /* Drop the current partitions before attaching the new one. */
20752 foreach(listptr, mergingPartitionsList)
20754 ObjectAddress object;
20755 Relation mergingPartition = (Relation) lfirst(listptr);
20757 /* Get relation id before table_close() call. */
20758 object.objectId = RelationGetRelid(mergingPartition);
20759 object.classId = RelationRelationId;
20760 object.objectSubId = 0;
20762 /* Keep the lock until commit. */
20763 table_close(mergingPartition, NoLock);
20765 performDeletion(&object, DROP_RESTRICT, 0);
20767 list_free(mergingPartitionsList);
20770 * Attach a new partition to the partitioned table. wqueue = NULL:
20771 * verification for each cloned constraint is not needed.
20773 attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
20775 /* Keep the lock until commit. */
20776 table_close(newPartRel, NoLock);